Yesterday, the attack worked only if a victim clicked a malicious link. Today? The payload lives inside the application and attacks everyone automatically.

Welcome to Stored XSS.

🧠 What Is Stored XSS?

Stored (Persistent) XSS happens when:

  1. User input is submitted
  2. The application stores it in the database
  3. The stored input is rendered later
  4. The browser executes it

Unlike reflected XSS, this one doesn't need a crafted link every time.

The attack persists. The application does not sanitize or encode user input before storing it.

That's the root cause.

πŸ§ͺ Lab Overview

Vulnerability: Stored Cross-Site Scripting Severity: High (CVSS 8.6)

The vulnerable area:

  • Stored messages: chat section/ Comment section etc..

User messages were saved directly and rendered without encoding.

πŸ’£ Proof of Concept

🎯 Payload Used (From Report)

<script>alert('stored')</script>

πŸ” Steps To Reproduce

  1. Navigate to the Stored Message section.
  2. Enter the payload inside the message field:
  3. <script>alert('stored')</script>
  4. Submit the message.
  5. Refresh or revisit the page.

πŸ’₯ Result

The JavaScript executes automatically.

No clicking required.

Anyone who loads that page becomes a victim.

πŸ“Έ Screenshot evidence appears on Page 5 of the report

PW VAPT REPORT ISSAN PDF

.

⚠️ Why This Is More Dangerous Than Reflected XSS

Let's compare:

Reflected XSS

Stored XSS

Requires crafted link

Executes automatically

Affects one victim at a time

Affects all users

Temporary

Persistent

Medium–High impact

Very High impact

Stored XSS spreads like infection.

If an admin views the page? Game over.

🧨 Real-World Impact

An attacker can:

  • πŸͺ Steal session cookies
  • πŸ”“ Hijack accounts
  • 🎭 Inject fake login forms (phishing)
  • 🧾 Modify the DOM
  • πŸ‘‘ Take over admin accounts
  • 🦠 Spread malicious payloads to every visitor

This is why it scored:

CVSS: 8.6 (High)

πŸ”¬ Why It Works (Technical Breakdown)

Imagine backend logic like:

$message = $_POST['message'];

INSERT INTO chat (msg) VALUES ('$message');

Then later:

echo $row['msg'];

No encoding. No filtering. No escaping.

So the database stores raw JavaScript.

And the browser executes it.

πŸ›‘οΈ How To Fix It

βœ… 1. Output Encoding (Mandatory)

echo htmlspecialchars($message, ENT_QUOTES, 'UTF-8');

βœ… 2. Sanitize Input

Use:

  • DOMPurify
  • HTMLPurifier

βœ… 3. Implement CSP

Content-Security-Policy: script-src 'self'

βœ… 4. Disable Inline JS

Block:

  • <script>
  • onerror
  • onclick
  • onmouseover

🧩 Beginner Explanation

Reflected XSS = You send attack β†’ server reflects it β†’ victim clicks β†’ it runs.

Stored XSS = You inject attack β†’ server saves it β†’ every visitor runs it automatically.

One is a spear.

The other is a landmine.

🎯 Day 2 Key Takeaway

If user input is stored and later rendered:

It must be encoded on output.

Always.

Never trust:

  • Forms
  • Comments
  • Chat messages
  • Profile fields
  • Headers
  • URLs

Everything is attacker-controlled.

None
Tomorrow we go deeper!