Published: 28 April 2023 at 12:00 UTC
Updated: 28 April 2023 at 12:31 UTC
Any individual website component can undermine the security of the entire site, and analytics platforms are no exception. With this in mind, we decided to do a quick audit of Piwik PRO to make sure it was safe to deploy on portswigger.net.
I decided to look for client-side issues like DOM XSS - I focussed on this because we were introducing new script resources and therefore the most likely vector would be a DOM XSS vulnerability. The first thing I did was browse the site with DOM Invader enabled and try injecting canaries - this yielded no results, which was good news. Next, I changed the DOM Invader canary to a blank value which enabled me to see all the sinks being used regardless of whether the canary was present or not. This is super useful for spotting stuff like document.write() and sure enough, there was a document.write call and various innerHTML assignments. I got a stack trace and inspected the document.write() call and noticed there was a debug flag… That led me to my next question - what does this do?
I added the flag to the URL and low and behold, an analytics debugger appeared. I tested that the document.write call wasn't vulnerable to XSS and then I pondered my next question: how was this debugger constructed? I started inspecting the debugger using devtools and immediately noticed an "ng-app" event. Jackpot, this is my old friend AngularJS.
Once you have identified that you have a AngularJS gadget there are two possible outcomes. You can either perform client-side template injection (CSTI), or you have a CSP bypass. CSTI wasn't possible because it requires a HTML injection vulnerability in order to inject the script resources. This left a CSP bypass, which is important to fix because if your site has a HTML injection vulnerability then you can use the CSP bypass to escalate to XSS. I've done this in the past to find XSS in PayPal.
On further inspection, the debugger seemed to use an iframe and loaded various script resources that were allowed by our CSP. I consulted our XSS cheat sheet to see the various CSP bypasses for AngularJS. I picked the first one and entered the following into the console:
document.body.innerHTML=`<iframe srcdoc="<div lang=en ng-app=application ng-csp class=ng-scope>
<input autofocus ng-focus=$event.composedPath()|orderBy:'.constructor.from(,alert)'>
2nd Mar 2023, 10:51 - Reported CSP bypass to Piwik
2nd Mar 2023, 11:20 - Acknowledged by Piwik
3rd Mar 2023, 13:09 - Vulnerability confirmed
7th Mar 2023, 12:24 - CSP deployment instructions updated to fix vulnerability
28th April 2023, 13:00 - Blog post released