Thank you for visiting. This blog post talks about CSP nonce bypasses. It starts with some context, continues with how to bypass CSP nonces in several situations and concludes with some commentary. As always, this blog post is my personal opinion on the subject, and I would love to hear yours.
Fast-forward to 2015, when Mario Heiderich made a cool XSS challenge called "Sh*t, it's CSP!", where the challenge was to escape an apparently safe CSP with the shortest payload possible. Unsurprisingly, JSONP made an appearance (but also Angular and Flash). Talk about beating a dead horse.
But this Christmas, as-if it was a piece of coal from Santa, Sebastian Lekies pointed out what in my opinion, seems to be a significant blow to CSP nonces, almost completely making CSP ineffective against many of the XSS vulnerabilities of 2016.
A CSS+CSP+DOM XSS three-wayWhile CSP nonces indeed seem resilient against 15-years-old XSS vulnerabilities, they don't seem to be so effective against DOM XSS. To explain why, I need to show you how web applications are written now a days, and how that differs from 2002.
Let me give you three examples (non-exhaustive list, of course) of DOM XSS bugs that are common and CSP nonces alone can't defend against:
- Persistent DOM XSS when the attacker can force navigation to the vulnerable page, and the payload is not included in the cached response (so need to be fetched).
- DOM XSS bugs where pages include third-party HTML code (eg, fetch(location.pathName).then(r=>r.text()).then(t=>body.innerHTML=t);)
- DOM XSS bugs where the XSS payload is in the location.hash (eg, https://victim/xss#!foo?payload=
The summary of this attack is that it's possible to create a CSS program that exfiltrates the values of HTML attributes character-by-character, simply by generating HTTP requests every time a CSS selector matches, and repeating consecutively. If you haven't seen this working, take a look here. The way it works is very simple, it just creates a CSS attribute selector of the form:
And then, once we get a match, repeat with:
Until it exfiltrates the complete attribute.
The attack for script tags is very straightforward. We need to do the exact same attack, with the only caveat of making sure the script tag is set to display: block;.
So, we now can extract a CSP nonce using CSS and the only thing we need to do so is to be able to inject multiple times in the same document. The three examples of DOM XSS I gave you above permit exactly just that. A way to inject an XSS payload multiple times in the same document. The perfect three-way.
Proof of ConceptAlright! Let's do this =)
Now, of course, the question is: How do you force the document to load twice!? With HTTP cache, of course! That's exactly what Sebastian showed us this Christmas.
|A happy @slekies wishing you happy CSP holidays! Ho! ho! ho! ho!|
Let me show you with an example, let's take the default Guestbook example from the AppEngine getting started guide with a few modifications that add AJAX support, and CSP nonces. The application is simple enough and is vulnerable to an obvious XSS but it is mitigated by CSP nonces, or is it?
Now, let's do the attack, to recap, we will:
- with CSS attribute reader.
- with the CSP nonce.
Stealing the CSP nonce will actually require some server-side code to keep track of the bruteforcing. You can find the code here, and you can run it by clicking the buttons above.
If all worked well, after clicking "Inject the XSS payload", you should have received an alert. Isn't that nice? =). In this case, the cache we are using is the BFCache since it's the most reliable, but you could use traditional HTTP caching as Sebastian did in his PoC.
Other DOM XSS
- We could try to lock CSP at runtime, as Devdatta proposed.
- We could disallow CSS3 attribute selectors to read nonce attributes.
- We could just give up with CSP. 💩
Anyway, happy holidays, everyone! and thank you for reading. If you have any feedback, or comments please comment below or on Twitter!