October 18, 2026
$9,800 for Sending the Same Parameter Twice: An HTTP Parameter Pollution Story That Bypassed aβ¦
I want to start with something that surprised me when I got back into bug hunting seriously this year: the 2026 bounty landscape has gottenβ¦

By T4nv1
6 min read
I want to start with something that surprised me when I got back into bug hunting seriously this year: the 2026 bounty landscape has gotten noticeably more selective. A few major programs have spent the past several months trimming public payout tiers and pushing their best rewards into invite-only layers, explicitly telling researchers that volume doesn't pay β quality does. That shift changed how I approach targets. I stopped chasing the first thing that looked interesting and started asking a slower, more annoying question before writing anything up: if two different parts of this stack read the same request, do they actually agree on what it says?
That question is the entire reason this bug exists, and it's worth walking through in detail because almost nobody tests for it deliberately anymore.
A Platform Built From Layers That Don't Talk to Each Other
The target, which I'll call Corvid Systems here since their disclosure policy asks researchers not to name them, is a B2B subscription platform selling seat-based licenses to mid-size companies. Like most production systems at this scale, requests to Corvid didn't hit one single piece of code β they passed through a web application firewall first, then a reverse proxy, then finally the application server itself. Three separate pieces of software, three separate implementations, all technically parsing the same HTTP request.
That architecture is completely ordinary. What made it interesting is a detail almost nobody thinks about once they've confirmed a request "works": what happens when the same parameter name appears more than once in the same request.
The Thing Nobody Agrees On
HTTP doesn't actually define a single correct behavior for duplicate parameters. If a request contains userId=104&userId=778, there's no universal standard saying which value should win. Some frameworks take the first occurrence. Some take the last. Some silently merge both into an array and only the developer's own code decides what happens next. This isn't a bug in any individual piece of software β it's an ambiguity the HTTP specification simply never resolved, and every piece of software in a request's path gets to make its own independent decision about it.
That ambiguity becomes dangerous the moment two different layers in the same pipeline make two different decisions, because an attacker can then show the security-enforcing layer one value while the actual business logic quietly acts on another.
I started testing this by sending a harmless duplicate parameter to a non-sensitive endpoint and watching how the response changed:
GET /api/account/profile?userId=104&userId=104GET /api/account/profile?userId=104&userId=104Identical values, no observable difference β expected, since there's nothing to disagree about when both copies match. The real test came next.
GET /api/account/profile?userId=104&userId=778GET /api/account/profile?userId=104&userId=778Account 104 belonged to me. Account 778 was a second test account I'd created specifically for this. The response came back containing account 778's profile data β meaning the application itself was reading the second occurrence of userId, discarding the first entirely.
That alone isn't yet a vulnerability. If nothing in front of the application was supposed to be checking authorization against that parameter, this would just be unusual behavior, not a security bypass. The actual bug depended on what Corvid's WAF rule was doing with that same request.
Finding the Disagreement
Corvid's WAF, based on response headers and a block page I triggered during earlier recon, was clearly inspecting requests for IDOR-style access attempts β sending a request for an account ID that didn't belong to my authenticated session, using only a single userId parameter, got flagged and blocked outright with a 403 before it ever reached the application.
GET /api/account/profile?userId=778
β 403 Forbidden (blocked by WAF rule)GET /api/account/profile?userId=778
β 403 Forbidden (blocked by WAF rule)So the control existed, and it worked, as long as there was exactly one value to inspect. The question that actually mattered was whether the WAF's parsing logic agreed with the application's parsing logic about which value counted when there were two.
GET /api/account/profile?userId=104&userId=778
β 200 OK, account 778's data returnedGET /api/account/profile?userId=104&userId=778
β 200 OK, account 778's data returnedIt went straight through. The WAF, evaluating the request, read the first occurrence of userId β my own account ID, 104, which matched my session and raised no red flags β and approved the request as legitimate. The application behind it, as I'd already confirmed, read the second occurrence and used that one to actually fetch data. Same request, two layers, two different readings, and the gap between those two readings was wide enough to walk an entire authorization control through.
Why This Matters More Than a Single Leaked Profile
A single profile endpoint leaking another user's name and email is a real finding, but it undersells what this bug actually represents. I spent time afterward checking which other endpoints shared the same userId-based pattern, because a parsing disagreement at the WAF layer isn't scoped to one specific route β it's a property of how the WAF parses requests generally, which meant it was worth checking broadly rather than assuming the one endpoint I'd found was the only one affected.
I found the same bypass worked against a billing-details endpoint, returning another account's invoice history and partial payment method information, and against a seat-management endpoint that listed every employee email address associated with another company's subscription. None of this required anything beyond the same duplicate-parameter trick, repeated against a different route each time.
Confirming Without Overreaching
I limited every test to two accounts I'd created and controlled myself, specifically so I could demonstrate cross-account access without ever touching a real customer's data. For the billing and seat-management findings, I confirmed the bypass worked by retrieving my own second test account's data through the duplicate-parameter technique rather than targeting any ID I hadn't already confirmed belonged to an account of mine, which meant I never needed to guess or enumerate real customer account IDs to make the case completely.
Writing the Report
Parameter pollution findings live or die on whether the report makes the two-layer disagreement unmistakably clear, because a reader who only checks the application's own validation logic β and never thinks to ask what the WAF in front of it actually inspected β will reasonably assume the control is doing its job.
Title: HTTP Parameter Pollution allows WAF-enforced IDOR protection bypass via duplicate userId parameter across multiple endpoints
Root cause: The WAF's request-inspection logic evaluates the first occurrence of a duplicated query parameter, while the application server's parameter-handling logic uses the last occurrence. An attacker can supply their own, authorized ID as the first value and a target ID as the second, passing WAF inspection while the application acts on the unauthorized value.
Reproduction: Baseline single-parameter block confirming the WAF rule functions correctly in the ordinary case; the duplicate-parameter bypass against the profile endpoint; and the same technique confirmed against billing and seat-management endpoints, with all cross-account testing limited to two self-controlled accounts throughout.
Impact: Any authenticated user can bypass IDOR protection across at least three endpoint categories by duplicating the relevant ID parameter, exposing other customers' profile, billing, and employee-roster data without needing to guess or brute-force IDs, since the technique works against any known or predictable target ID.
Fix recommendations:
- Configure the WAF to normalize duplicate parameters identically to the application layer β ideally by rejecting any request containing duplicate occurrences of a sensitive identifier parameter outright, rather than attempting to guess which one the application will honor
- Fix the disagreement at its source by auditing every layer in the request pipeline for its duplicate-parameter handling behavior and standardizing on one consistent rule platform-wide
- Treat parameter pollution testing as a standard part of any review involving a WAF or reverse proxy sitting in front of application logic, since this class of bug is invisible to testing that only exercises the application directly
What Happened After
Corvid's infrastructure team reproduced this within two days β a parsing disagreement between two pieces of infrastructure is usually straightforward to confirm once someone points at the specific request pattern. They patched the WAF configuration to reject duplicate occurrences of ID-bearing parameters within the week, and their resolution notes mentioned auditing several other identifier parameters across the platform for the same first-versus-last disagreement, finding two more affected routes in that pass. The report closed at $9,800, rated high severity, reflecting a complete bypass of an access-control layer across multiple distinct endpoints rather than a single isolated leak.
Why This Bug Class Still Matters in a More Selective Bounty Landscape
With payouts increasingly rewarding depth over volume this year, parameter pollution is worth knowing well specifically because it's cheap to test and consistently underexamined β most researchers check whether an endpoint's own logic is vulnerable and stop there, never circling back to ask whether a security layer sitting in front of that endpoint parses the same request the same way. A few habits worth carrying forward:
Always test duplicate parameters against any endpoint protected by a WAF or similar inspection layer, not just the application directly β the vulnerability only exists in the disagreement between two layers, so testing either one alone will never reveal it.
Once you find one disagreement, check whether it's systemic. A WAF's parsing behavior is a property of the WAF, not of any single endpoint, so a confirmed bypass on one route is a strong signal worth testing broadly across every other route sharing a similar parameter pattern.
Keep cross-account proof of concepts limited to accounts you control. Confirming impact on a second value you already know belongs to you demonstrates the bypass completely without ever needing to touch or guess a real customer's identifier.
Document the specific first-versus-last disagreement explicitly in your report. It's the entire mechanism, and a reader who misses that detail will likely underrate the finding as a simple IDOR rather than recognizing it as a systemic control bypass.
What stuck with me about this one is that nothing about it was clever in the way a sandbox escape or a signature forgery is clever. It was just a question nobody had gotten around to asking: do these two pieces of software, both reading the same request, actually agree on what it says? They didn't, and that one small disagreement was enough to walk straight past a control that, tested in isolation, worked exactly as intended.
I keep finding the most interesting bugs in the space between two systems that each think the other one is handling it β follow if you want the next breakdown when it's ready.