Key takeaways
Why reflected XSS is mostly dead
Modern browsers, WAFs, and framework-level output encoding have made classic reflected XSS rare in well-maintained applications. The XSS that pays in 2026 requires understanding JavaScript execution contexts, DOM sinks, and the specific sanitization logic of the target.
That shift means the best XSS hunters are JavaScript readers, not payload spammers.
DOM-based XSS: finding dangerous sinks
DOM XSS occurs when JavaScript reads attacker-controlled data from a source (location.hash, location.search, document.referrer, postMessage) and writes it to a dangerous sink (innerHTML, eval, document.write, location.href, setTimeout with string argument).
The fastest way to find DOM XSS is to search the JavaScript for dangerous sinks and trace the data flow back to a controllable source.
Common DOM XSS sink patterns to search for
// Dangerous sinks
document.getElementById('x').innerHTML = location.hash.slice(1);
eval(location.search.split('code=')[1]);
document.write(decodeURIComponent(location.hash));
window.location = userInput; // open redirect -> XSS
setTimeout(userInput, 1000); // string evalPrototype pollution to XSS
Prototype pollution occurs when an attacker can set properties on Object.prototype via a merge, clone, or path-assignment function. If the application later reads from Object.prototype in a dangerous context, this can lead to XSS.
The chain is: find a prototype pollution gadget (usually in a deep merge function), find a downstream sink that reads from Object.prototype, and craft a payload that connects them.
Prototype pollution XSS chain
// Pollution via URL: ?__proto__[innerHTML]=<img src=x onerror=alert(1)>
// If the app does: element.innerHTML = options.innerHTML || ''
// And options inherits from Object.prototype
// Then the polluted value reaches the sink
// Test with:
Object.prototype.innerHTML = '<img src=x onerror=alert(1)>';
// Then trigger the code path that reads innerHTML from an options objectCSP bypass techniques
Content Security Policy is the main defense against XSS. Bypasses exist when the policy allows unsafe-inline, unsafe-eval, or overly broad source directives. The most reliable bypasses in 2026 are JSONP endpoints on whitelisted domains, Angular template injection on sites that allow angular.js CDN, and base-uri injection.
If the CSP allows a CDN domain that hosts JSONP endpoints, you can load a script from that domain with a callback parameter that executes your payload.
- JSONP bypass: find a JSONP endpoint on a whitelisted domain.
- Angular template injection: if angular.js is allowed, use {{constructor.constructor('alert(1)')()}}.
- base-uri injection: if base-uri is not set, inject a base tag to redirect relative script loads.
- Trusted Types bypass: find a policy that allows dangerous patterns.
Writing a bounty-grade XSS report
A good XSS report includes: the exact URL and parameter, the payload used, a screenshot or video of the alert, the impact (session theft, credential capture, account takeover), and a remediation recommendation.
The difference between a $200 and a $2000 XSS report is usually the impact narrative. Show what an attacker can actually do with the XSS, not just that an alert box appeared.
FAQ
Is XSS still worth reporting in 2026?
Yes, especially DOM-based XSS, stored XSS, and XSS that bypasses CSP. Reflected XSS without impact is often rated low, but any XSS that can steal session tokens, capture credentials, or perform actions on behalf of the victim is still rated high or critical.