How I Found a Stored XSS That Could Have Taken Over Accounts (And Why It Was Marked Duplicate) + Video

Listen to this Post

Featured Image

Introduction:

In the high-stakes world of bug bounty hunting, discovering a Stored Cross-Site Scripting (XSS) vulnerability is often a guaranteed high-severity payout, especially when it leads to full Account Takeover (ATO). However, as one ethical hacker recently discovered, even the most critical flaws can be rendered unrewarding by a simple status: “Duplicate.” This article breaks down the methodology used to identify a Stored XSS chain leading to ATO, analyzes why it was still valid yet duplicated, and provides a technical deep dive into replicating and mitigating such attacks.

Learning Objectives:

  • Understand the step-by-step methodology for discovering Stored XSS vulnerabilities.
  • Learn how to chain a Stored XSS payload to achieve Account Takeover via session hijacking.
  • Identify common sanitization bypass techniques and how to test them.
  • Analyze the root cause of duplicate submissions in bug bounty programs.

You Should Know:

1. Phase 1: Reconnaissance and Input Mapping

The first step in finding XSS is to meticulously map the application’s attack surface. You are looking for every single point where the application accepts user data.
– Identify Entry Points: This includes comment sections, profile information fields (name, bio, website), support tickets, file upload metadata (exif data), and HTTP headers (like `User-Agent` or Referer) if stored.
– Tooling: Use Burp Suite’s Spider or the Integrations tab in your browser’s developer tools to catalog all endpoints.
– Command/Technique: While manually browsing, turn on Burp Intercept or use the Logger++ extension to record every request containing parameters (POST/GET). Look for parameters like message, comment, name, bio, settings

</code>.

<h2 style="color: yellow;">2. Phase 2: Detection and Reflection Analysis</h2>

Once inputs are identified, you must determine how the application handles them.
- The Test: Inject a unique, non-malicious string into every input field. For example: <code>"XSS-TEST-123</code>.
- Verification: Visit the page where that input is supposed to be displayed. View the page source (Ctrl+U or Cmd+U) and search for your string.
- Outcome: If your string appears in the HTML source without being encoded or filtered, you have a potential Reflected XSS. If it appears on a page viewed by other users (like a forum post or a profile page), you have a Stored XSS.

<h2 style="color: yellow;">3. Phase 3: Bypassing Sanitization with Payloads</h2>

Modern web applications rarely accept raw `<script>` tags. You must test the filter logic. The victim application likely had a WAF or input filter.
- Test Basic Execution: Start with the classic payload:
[bash]
<script>alert('XSS')</script>

Result: Likely blocked or encoded.

  • Test Context Breaking: If your input is placed inside an HTML tag attribute, you need to break out of it. For example, if the value is inside an `` tag:
    " onmouseover="alert('XSS')"
    
  • Test Obfuscation: If the filter blocks keywords like `script` or alert, try case variation or encoding.
    <ScRiPt>alert('XSS')</ScRiPt>
    

Or using Hex/Unicode encoding within events:

<img src=x onerror=\u0061lert('XSS')>

4. Phase 4: Achieving JavaScript Execution (The Bypass)

In the original post, the hacker mentioned bypassing sanitization. Let's assume the application stripped `