
Individual website components can compromise the security of your entire site, and analytics platforms are no exception. With this in mind, we decided to do a quick audit of his Piwik PRO to make sure it is safe to deploy on portswigger.net.
I decided to look for client side issues like DOM XSS. The focus was on introducing a new scripting resource and the most likely vector was a DOM XSS vulnerability. The first thing I did was browse the site with DOM Invader enabled and try to inject a canary. I didn’t get any results, but this was good news. Then I changed the DOM Invader canary to a blank value. This allowed me to see all sinks being used regardless of whether a canary was present or not. This is very useful for finding things like document.write(). Indeed, there were document.write calls and various innerHTML assignments. I got the stack trace and inspected the document.write() call and noticed there was a debug flag… which led me to my next question – what does this do?
I added a flag to the URL and lo and behold the analytics debugger appeared. After testing that document.write calls are not vulnerable to XSS, I thought about the following questions: How was this debugger built? I started inspecting the debugger using devtools and immediately noticed the “ng-app” event. Jackpot, this is my old friend his AngularJS.
You may wonder why I hit the jackpot. This is because AngularJS has well-known script gadgets that can be used to bypass Content Security Policy (CSP). A script gadget is JavaScript code, usually from a library, that adds additional functionality to HTML or JavaScript. Since the gadget already has JavaScript running and is allowed by policy, you can use this gadget to bypass CSP. A good example of this is AngularJS’s ng-focus. This event allows you to fire the browser’s focus event, but ng-focus is non-standard, so it’s allowed by CSP and done by AngularJS itself.
Once you confirm that you have an AngularJS gadget, there are two possible outcomes. You can do client-side template injection (CSTI) or bypass CSP. CSTI could not run because it requires an HTML injection vulnerability to inject script resources. If your site has HTML injection vulnerabilities, it’s important to fix them as they can be escalated to XSS using CSP bypass. I’ve done this in the past to find XSS on PayPal.
Upon further inspection, the debugger appears to be using an iframe, loading various scripting resources allowed by CSP. I went through the XSS cheat sheet to see various CSP bypasses for AngularJS. I chose the first one and typed in the console:
document.body.innerHTML=`<iframe srcdoc="<div lang=en ng-app=application ng-csp class=ng-scope>
<script src=https://ps.containers.piwik.pro/container-debugger/vendor.js></script>
<script src=https://ps.containers.piwik.pro/container-debugger/scripts.js></script>
<script src=https://ps.containers.piwik.pro/container-debugger/templates.cache.js></script>
<input autofocus ng-focus=$event.composedPath()|orderBy:'[].constructor.from([1],alert)'>
</div>
">`
Sure enough, this bypassed CSP entirely. Because the script was allowlisted, the attacker was able to use composedPath() to inject her AngularJS directive and her ng-focus event in order to get the window objects in the array. rice field. The orderBy filter traversing the scope of that array and executing code ends up in a window object and indirectly calls the alert function using Array.from(). This bypasses CSP. He reported the issue to Piwik, who updated her CSP deployment procedures to address the vulnerability. He fixed the problem by hardening CSP to allowlist specific JavaScript files instead of entire domains. He also used his nonce in certain scripts to prevent attackers from injecting his resources into his own AngularJS scripts.
This is valid now – if you find something we missed, please report it to the PortSwigger and Piwik PRO bug bounty programs.
Timeline
Mar 02, 2023 10:51 – Report CSP bypass to Piwik
Mar 02, 2023 11:20 – Approved by Piwik
Mar 3, 2023 13:09 – Vulnerability confirmed
03/07/2023 12:24 – CSP deployment instructions updated to fix vulnerability
Apr 28, 2023 13:00 – Blog post published
back to all articles