July 24, 2026
How I Found a Bug Worth $3,500 — In a Feature Nobody Was Watching.
A storage-exhaustion flaw. A stored XSS that waited for an admin. Both hiding in the same “boring” file upload form that hadn’t been…

By Vishw Bhatt
6 min read
A storage-exhaustion flaw. A stored XSS that waited for an admin. Both hiding in the same "boring" file upload form that hadn't been touched in years.
A file upload form. Zero server-side validation. A spoofed size field. A filename that executed code in an admin's browser three days after I uploaded it.
The Setup — Where Nobody Looks
This wasn't some flashy HackerOne program with a public leaderboard and a hall of fame. It's a private one 😉.
It was a B2B SaaS platform — the kind of internal business tool that handles users' images and map data out of it.
Just a login page, a dashboard, and a bunch of forms, well tested before, but no one thought what if it could be landing in the admin's pocket.
If you want to find bugs that matter, look where nobody else is looking. The flashy features get all the attention. The boring ones get all the vulnerabilities.
The program already had 1000+ reports and some of them disclosed around the image upload feature only.
That's usually where the good bugs live.
Target Recon — It's About the Questions You Ask
Nothing exotic here. Standard black-box approach. But the difference between finding nothing and finding two chained bugs comes down to what questions you ask:
→ Map every feature that touches user-supplied files
→ Note anything that accepts metadata alongside the file — filename, declared size, MIME type
→ Ask the question most testers skip: who eventually consumes this data, and in what context?
That last question is the one that actually matters.
Most testers stop at "does the upload work?"
The real question is: "Who could open this file later — and what do they trust about it?"
That single question turned a "meh" upload form into a two-bug chain worth writing about. Let me show you how.
🐛 Bug #1 — The Size Field That Lied
The upload form enforced a file size limit — on paper.
But here's the thing: the limit was checked against a client-supplied metadata field, not the actual bytes landing in storage.
Read that again.
So there were two requests in the image upload feature that were there:
- The Main File Upload:
- A simple POST request to /upload-user-profile-image with the usual file upload request.
- Every other file type was blocked.
- Only JPG and PNG
- Everything bigger than 15 MB gets blocked.
- Fully secure, and most of us were done here, including me, but then after some time, when I was testing BAC, I revisited that feature and found out about the second request.
- The Metadata Endpoint
- A metadata request going just after the upload request: POST /upload-user-profile-image-metadata
- Filename, FileType, Filesize, and some Creation and upload dates; that's it.
The server was checking the file size in the metadata of the main request and rejecting the file.
The Server was storing the file on a temporary server. Then it was being verified against the metadata sent through the second request and then being sent for the rest of the checks and then being deleted, as well as sending a response that the max limit was reached.
So I sent a file with a spoofed size value. The backend stored the real file — several gigabytes of it — while happily logging that it had accepted something tiny.
Classification
- CWE-434 (Unrestricted Upload) + CWE-770 (Uncontrolled Resource Consumption)
- Severity: ⚠️ Medium
- Estimated bounty (comparable program): 150–150–500
Not earth-shattering on its own. But it gets interesting when you combine it with what I found next.
💀 Bug #2 — The XSS That Waited for an Admin
Same upload flow. Different field. This time: the filename itself.
The filename was rendered unsanitized — but here's the twist that changes everything about this finding:
It didn't render in my view.
It rendered in an internal, admin-only screen used to review uploaded files.
I uploaded a file. Named it something… creative. Then I waited.
Three days later, an admin on the other side clicked into that file's details.
💥 Boom.
This is the detail that changes everything about severity. Let me break it down in plain English for everyone — techie or not:
Self-XSS 🤷 — XSS that only fires in your own browser. A shrug. Not a real vulnerability in most programs.
Admin Stored XSS 💀 — XSS that fires in a privileged admin session. Session hijacking. Forged actions. Potential full platform compromise.
Same technique. Same payload. But the victim is different — and that makes it a completely different conversation.
A regular user triggering XSS on themselves? Who cares.
An unprivileged uploader triggering code execution in an admin's browser? That's not just XSS. That's a privilege escalation through the browser. No phishing link. No social engineering. Just a filename.
Classification
- CWE-79 (Stored XSS) — Scope Change (unprivileged uploader → privileged admin context)
- Severity: 🔴 Critical
- Estimated bounty (comparable program): 1,000–1,000–3,000+
⛓️ The Chain — From "Boring Upload" to Admin Takeover
Each bug alone tells a story. Together, they tell a much scarier one.
Here's the full attack chain:
🔍 Step 1: Find a file upload form nobody has tested in years
📦 Step 2: Spoof the size field → quietly exhaust storage & degrade shared infrastructure
💉 Step 3: Inject a payload in the filename → it sits in the database and waits
💀 Step 4: Admin reviews the file → payload fires in their browser → session compromised.
Imagine someone slipping a note into a suggestion box, except the note explodes when the manager opens it — and also the suggestion box has no limit on how many notes you can stuff in there, so you can also fill the entire office with paper while you're at it.
That's basically it. That's the chain.
A low-effort path from "anonymous file upload" to "admin compromise." The kind of chain that gets a security report read carefully rather than being triaged in 30 seconds.
💵 Where the $3,500 Comes From
Let me be completely transparent here: this wasn't a public bounty payout. This was a private program.
Exhaustion DoS⚠️ High: $1000
Stored XSS → Admin Session🔴 Critical: $2500
📝 What I Learned — And What You Should Take Away
1. Metadata is attack surface.
Filenames, declared sizes, MIME types — anything the client sends alongside a file deserves the same scrutiny as the file content itself. If the server trusts any of it without verifying, you've got a bug. Most developers validate the file. Almost nobody validates the metadata about the file.
2. Severity is about the victim, not the vulnerability.
The exact same XSS payload is a non-issue against yourself and a Critical finding against an admin. Always trace who consumes the data downstream. That's where the real impact lives. The bug doesn't change — but the story around it changes completely.
3. "Boring" features age badly.
Upload forms get built once, pass QA, and are rarely revisited. That's exactly why they're worth a second — or third — look. The features everyone ignores are the ones that rot first. If a feature was last touched before someone left the company, it's practically begging to be tested.
4. Think in chains, not singles.
One medium bug and one critical bug are fine on their own. Together, they tell a story that gets a report read carefully instead of triaged in thirty seconds. Always look for how bugs connect. The difference between a 500-dollar finding and a 3,500-dollar finding is often just context.
🛡️ Responsible Disclosure
All findings referenced in this post have been formally reported to the affected organization, and remediation is in progress. No target details, exploit payloads, or identifying information are included.
Responsible disclosure isn't just a checkbox — it's how this industry stays functional. You find the bug, you report the bug, you wait for the fix. That's the deal.
About the Author
This is my first published write-up — but not my first finding.
I'm Vishw Bhatt, a security researcher focused on web application security, business logic flaws, and the bugs that live in features nobody thinks to test.
If you found this useful, or if you've got a story about a "boring" feature that bit back — let's connect.
🔗 LinkedIn: linkedin.com/in/vishw-bhatt
If you enjoyed this, give it a 👏 — it helps more people find it. And if you're a developer reading this: Go check your upload forms. Seriously.
Tags: #BugBounty #Cybersecurity #Infosec #WebSecurity #XSS #AppSec #Pentesting #Hacking #SecurityResearch #BugBountyTips