CSP Bypass ”script-src” – Detailed Writeup

This write-up is all about how I bypassed the CSP and achieved Persistent XSS. I was working on a private program and it was protected by the CSP. Thus my goal was to dig deeper and deeper. The first thing I did was look for any sensitive vulnerability and I came across a feature that uploads documents to the server. Upon my testing, I uploaded the .html via unrestricted file upload bypassing the .pdf extension and replacing it with the .html. The HTML file looked like this:

<h1>Testing XSS</h1>
<img src=x onerror=alert(document.domain)>

Now, the problem arose when I clicked on the uploaded file. It opened in a blob format which basically is and could be a self XSS but there was a feature that allowed the file to be shared with other users. So, I decided to escalate it further. When I clicked on the uploaded XSS file, the XSS didn’t execute but the HTML code did execute. Now when I opened the console I saw CSP is not allowing any JS events or any other JS code due to the strict script-src rule.

When I saw the CSP error I got a bit disappointed but I decided to look further. I visited the CSP Evaluater a tool that lookup for CSP issues. The lookup results surprised me when I saw the results: Host whitelists can frequently be bypassed

CSP Evaluator results

There were bunch of allowed/whitelisted URLs. Thus I scrolled down and started looking for something that might help me bypass the CSP. When I scrolled to the end I got to know that the script-src allowed the YOUTUBE.

CSP Evaluator results 2

After a short research, I got to know that YouTube has this cool thing called oEmbed, which lets you easily embed videos on other websites. What’s interesting is that it has a feature called “Callback,” where you can put something, and it shows up on the page. Plus, it recognizes it as “text/javascript,

Youtube Oembed
Content type

Now if change the callback parameter to alert(origin);, It will reflect on the oembed response.

Adding JS payload

As it can be seen in the screenshot the payload is visible in the response. So I created a very simple payload:

<script src="https://www.youtube.com/oembed?callback=alert(origin);"></script>

And added it to the HTML file. Now I just uploaded the new one .HTML file and opened the uploaded file. The XSS was executed. The CSP Bypass worked.

XSS executed

Because the XSS attack is carried out through the blob URL, one might wonder about its impact on other users. The explanation lies in the document feature, which enables the sharing of files with other users. When these users open the file, it’s also opened as a blob. However, the issue is that the DOM can be accessed, meaning any invited user has the potential to be affected by the XSS vulnerability.

CSP Bypass Fix:

If your Content Security Policy (CSP) is being bypassed through the YouTube oEmbed feature, and you want to address the issue without completely removing YouTube, you can consider the following steps:

  1. Refine CSP Policies:
    • Review and refine your CSP policies to be more specific. Instead of a broad script-src allowing all sources, try to limit it to only necessary and trusted sources. This may involve specifying the exact domains from which scripts are allowed.
  2. Use Subresource Integrity (SRI):
    • Implement Subresource Integrity (SRI) for scripts loaded from trusted sources. SRI allows browsers to verify that the scripts being loaded have not been tampered with.
  3. Frame Ancestors:
    • Utilize the frame-ancestors directive to specify which domains are allowed to embed your content in iframes. This can help prevent the loading of your content on malicious websites.
  4. Nonce-Based Approach:
    • If possible, implement a nonce-based approach for script elements. This involves generating a unique nonce (number used once) for each page load and including it in the CSP header. This way, only scripts with the matching nonce will be executed.
  5. Content Security Policy Report-Only Mode:
    • Implement the CSP in “report-only” mode initially (Content-Security-Policy-Report-Only header) and analyze the reports. This allows you to gather information on policy violations without blocking content. Adjust your policy based on the received reports.
  6. Strict-Dynamic:
    • If you need to allow dynamic script generation, consider using the 'strict-dynamic' keyword in the script-src directive. This allows scripts added via trusted script sources to execute.