Bypassing Content Security Policy (CSP) with Reflective XSS

Grishma Acharya
5 min readMay 10, 2024

--

The Basics: XSS and CSP

In web security, two terms that often come up are Cross-Site Scripting (XSS) and Content Security Policy (CSP). In this blog, we’ll dive into what XSS and CSP are, how they work, and how they relate to each other.

Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) is a type of security vulnerability commonly found in web applications. It occurs when an attacker injects malicious scripts into web pages viewed by other users. These scripts can be executed in the context of the victim’s browser, allowing the attacker to steal sensitive information, deface websites, or even take control of user sessions.

Types of XSS:

  1. Reflected XSS: In this type of XSS attack, the malicious script is reflected off the web server, typically in the form of a URL parameter or input field. When the victim visits a specially crafted URL or submits a form, the script is executed in their browser.
  2. Stored XSS: Also known as persistent XSS, this attack involves injecting malicious scripts into a web application’s database. When other users access the affected page, the script is served from the server, executing in their browsers. A popular example for it is the twitter deck: https://www.acunetix.com/blog/articles/tweetdeck-worm-worked/
  3. DOM-based XSS: This variant occurs when the client-side JavaScript code of a web page processes user input in an unsafe manner. The malicious payload is executed in the Document Object Model (DOM) of the page, without involving the server.

Content Security Policy (CSP)

Content Security Policy (CSP) is a security standard that helps mitigate XSS attacks by defining the trusted sources of content that a browser should execute or render. It works by allowing web developers to specify a set of directives in the HTTP headers or HTML meta tags, instructing the browser on what types of content are allowed to be loaded and executed. It can serve as a crucial layer of defense against XSS attacks, data breaches, clickjacking, and other common web application security risks.

Common CSP Directives:

A Content Security Policy (CSP) directive is a rule that specifies which sources are allowed for loading or executing certain types of content on a web page. These directives provide control over the behavior of web browsers regarding the loading and execution of resources, helping to mitigate security risks such as Cross-Site Scripting (XSS) attacks and data exfiltration.

  • default-src: Specifies the default policy for fetching content such as JavaScript, CSS, images, etc.
  • script-src: Controls the sources from which JavaScript can be executed.
  • style-src: Defines the valid sources for CSS stylesheets.
  • connect-src: Determines the allowed sources for network connections (e.g., AJAX requests).
  • font-src: Specifies the permitted sources for web fonts.
  • frame-src: Defines the sources that can be used in <frame>, <iframe>, <object>, <embed>, and <applet> elements.

The commonly used rules are as follows:

  1. * - Allows all URLs except data:, blob:, and filesystem:.
  2. 'none' - Doesn't allow anything.
  3. 'self' - Only allows same-origin resources.
  4. https: - Allows all HTTPS resources.
  5. example.com - Allows specific domains (both HTTP and HTTPS).
  6. https://example.com - Allows specific origins (HTTPS only).

Example:
Directive: script-src

Rule: script-src 'self' https://example.com;

Explanation: This rule tells the browser where it can load JavaScript code from. Specifically:

  • 'self': It's allowed to load JavaScript from the same place where the web page is hosted.
  • https://example.com: It's also allowed to load JavaScript from https://example.com.

Here:

  • JavaScript from the current website ('self') can run, which is essential for the website's functionality.
  • JavaScript from https://example.com is permitted to execute, allowing the web page to use scripts hosted on that domain.

Any JavaScript code from sources other than 'self' and https://example.com will be blocked by the browser, providing protection against XSS attacks that attempt to inject and execute malicious scripts from unauthorized origins.

In order to deepen our understanding, let’s explore the web challenge of BSidesSF 2024 CTF

Some hints were in the challenge website:

This CSP configuration allows:

  • Inline scripts ('unsafe-inline') and scripts from the same origin ('self') via default-src and script-src.
  • Connections to any origin (*) via connect-src.
  • Styles from the same origin and specific external sources ('self', fonts.googleapis.com, fonts.gstatic.com) via style-src-elem and font-src.

Now, in the scenario where the “/xss-one-flag” page is restricted to the admin, we need a script that attempts to exploit a Cross-Site Scripting (XSS) vulnerability to steal the admin’s session cookies. Once the admin’s session cookies are obtained, it should be sent to a designated endpoint controlled by me (i.e. https://eon5d8as8cblk75.m.pipedream.net), allowing to access the restricted “/xss-one-flag” page.

This script uses fetch() function to send a POST request without needing CORS preflight checks. It includes the user’s cookies from the current document as the request body, sending them to the specified endpoint.

How the script worked for the CSP configuration:

  1. ‘self’ Origin: The script itself is hosted on the same origin ('self') as specified in the CSP configuration. Therefore, it satisfies the script-src directive, which allows scripts from the same origin.
  2. ‘no-cors’ Mode: The mode: 'no-cors' option in the fetch request allows the script to make a cross-origin POST request without requiring CORS preflight requests. This bypasses the restrictions set by the CSP's connect-src directive, which allows connections to any origin (*).
  3. Sending Cookies: By including document.cookie as the body of the request, the script sends the user's cookies to the specified endpoint (https://eon5d8as8cblk75.m.pipedream.net). This action is not prevented by the CSP configuration, as it does not restrict the content of outgoing requests.

After injecting the XSS payload into an input field on the vulnerable web page, I monitored the requests on my pipedream.com account. I observed that the payload successfully executed, and I received the user’s cookies in the requestbin account.

We got the flag: CTF{try-50me-XS5}

References:

  1. https://book.hacktricks.xyz/pentesting-web/content-security-policy-csp-bypass
  2. https://aszx87410.github.io/beyond-xss/en/
  3. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources

Lets get connected:
https://www.linkedin.com/in/grishma-acharya-9a5279224/

--

--

Grishma Acharya
Grishma Acharya

Written by Grishma Acharya

CTF player | Cyber security enthusiast

No responses yet