$npx -y skills add Prohao42/aimy-skill --skill http-parameter-pollution--- name: http-parameter-pollution description: >- HTTP Parameter Pollution (HPP): duplicate query/body keys parsed differently by servers, proxies, WAFs, and app frameworks. Use when filters and application layers disagree on which value wins, enabling bypass, SSRF second U
| 1 | # SKILL: HTTP Parameter Pollution (HPP) |
| 2 | |
| 3 | > **AI LOAD INSTRUCTION**: Model the **full request path**: browser → CDN/WAF → reverse proxy → app framework → business code. Duplicate keys (`a=1&a=2`) are not an error at HTTP level; each hop may pick first, last, join, or array-ify. Test HPP when WAF and app disagree, or when internal HTTP clients rebuild query strings. Routing note: when the same parameter appears multiple times, or WAF/backend stacks differ, use the Section 1 matrix to test first/last/merge assumptions, then design Section 3 scenario chains. |
| 4 | |
| 5 | ## 0. QUICK START |
| 6 | |
| 7 | **Hypothesis**: the **security check** reads one occurrence of a parameter while the **action** reads another. |
| 8 | |
| 9 | ### First-pass payloads |
| 10 | |
| 11 | ```text |
| 12 | id=1&id=2 |
| 13 | id=1&id=1%20OR%201=1 |
| 14 | url=https://legit.example&id=https://evil.example |
| 15 | amount=1&amount=9999 |
| 16 | csrf=TOKEN_A&csrf=TOKEN_B |
| 17 | user=alice&user=admin |
| 18 | ``` |
| 19 | |
| 20 | ### Body variants (repeat for POST) |
| 21 | |
| 22 | ```text |
| 23 | application/x-www-form-urlencoded |
| 24 | id=1&id=2 |
| 25 | |
| 26 | multipart/form-data |
| 27 | ------boundary |
| 28 | Content-Disposition: form-data; name="id" |
| 29 | 1 |
| 30 | ------boundary |
| 31 | Content-Disposition: form-data; name="id" |
| 32 | 2 |
| 33 | ``` |
| 34 | |
| 35 | ### Quick methodology |
| 36 | |
| 37 | 1. Fingerprint **front** stack (CDN/WAF) vs **origin** (language/framework) using baseline `a=1&a=2`. |
| 38 | 2. Send **both** orders: `a=1&a=2` and `a=2&a=1` (some parsers are order-sensitive). |
| 39 | 3. If JSON: test **duplicate keys** and Content-Type confusion (see Section 2). |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## 1. SERVER BEHAVIOR MATRIX |
| 44 | |
| 45 | Typical defaults — **always confirm**; middleware and custom parsers override these. |
| 46 | |
| 47 | | Technology | Behavior | Example: `a=1&a=2` | |
| 48 | |---|---|---| |
| 49 | | PHP / Apache (`$_GET`) | Last occurrence | `a=2` | |
| 50 | | ASP.NET / IIS | Often comma-joined (all) | `a=1,2` | |
| 51 | | JSP / Tomcat (servlet param) | First occurrence | `a=1` | |
| 52 | | Python / Django (`QueryDict`) | Last occurrence | `a=2` | |
| 53 | | Python / Flask (`request.args`) | First occurrence | `a=1` | |
| 54 | | Node.js / Express (`req.query`) | Array of values | `a=['1','2']` (shape may vary by parser version) | |
| 55 | | Perl / CGI | First occurrence | `a=1` | |
| 56 | | Ruby / Rack (Rack::Utils) | Last occurrence | `a=2` | |
| 57 | | Go `net/http` (`ParseQuery`) | First occurrence | `a=1` | |
| 58 | |
| 59 | **Why it matters**: a WAF on **IIS** might see `1,2` while PHP backend receives `2` only — or the reverse if a proxy normalizes. |
| 60 | |
| 61 | --- |
| 62 | |
| 63 | ## 2. PAYLOAD PATTERNS |
| 64 | |
| 65 | ### 2.1 Basic duplicate key |
| 66 | |
| 67 | ```http |
| 68 | GET /api?q=safe&q=evil HTTP/1.1 |
| 69 | ``` |
| 70 | |
| 71 | ### 2.2 Array-style (PHP / some frameworks) |
| 72 | |
| 73 | ```http |
| 74 | GET /api?id[]=1&id[]=2 HTTP/1.1 |
| 75 | ``` |
| 76 | |
| 77 | ### 2.3 Mixed array + scalar |
| 78 | |
| 79 | ```http |
| 80 | GET /api?item[]=a&item=b HTTP/1.1 |
| 81 | ``` |
| 82 | |
| 83 | ### 2.4 Encoded ampersand (parser differential) |
| 84 | |
| 85 | ```text |
| 86 | # Literal & inside a value vs new pair — depends on decoder |
| 87 | param=value1%26other=value2 |
| 88 | param=value1&other=value2 |
| 89 | ``` |
| 90 | |
| 91 | ### 2.5 Nested / bracket keys |
| 92 | |
| 93 | ```http |
| 94 | GET /api?user[name]=a&user[role]=user&user[role]=admin HTTP/1.1 |
| 95 | ``` |
| 96 | |
| 97 | ### 2.6 JSON duplicate keys |
| 98 | |
| 99 | ```json |
| 100 | {"test":"user","test":"admin"} |
| 101 | ``` |
| 102 | |
| 103 | Many parsers keep **last** key; some keep **first**. JavaScript `JSON.parse` keeps the last duplicate key. |
| 104 | |
| 105 | --- |
| 106 | |
| 107 | ## 3. ATTACK SCENARIOS |
| 108 | |
| 109 | ### 3.1 HPP + WAF bypass |
| 110 | |
| 111 | **Pattern**: WAF inspects **first** value; application uses **last**. |
| 112 | |
| 113 | ```text |
| 114 | id=1&id=1%20UNION%20SELECT%20... |
| 115 | ``` |
| 116 | |
| 117 | Also try: benign value in JSON field duplicated in query string, if gateway merges sources differently. |
| 118 | |
| 119 | ### 3.2 HPP + SSRF |
| 120 | |
| 121 | **Pattern**: validator reads **safe** URL; fetcher reads **internal/evil** URL. |
| 122 | |
| 123 | ```text |
| 124 | url=https://allowed.cdn.example/&url=http://169.254.169.254/ |
| 125 | ``` |
| 126 | |
| 127 | Confirm which component (library vs app) consumes which occurrence. |
| 128 | |
| 129 | ### 3.3 HPP + CSRF |
| 130 | |
| 131 | **Pattern**: duplicate anti-CSRF token so one copy satisfies parser A and another satisfies parser B. |
| 132 | |
| 133 | ```text |
| 134 | csrf=LEGIT&csrf=IGNORED_OR_ALT |
| 135 | ``` |
| 136 | |
| 137 | Use only in **authorized** CSRF assessments with a clear state-changing target. |
| 138 | |
| 139 | ### 3.4 HPP + business logic (e.g. payment) |
| 140 | |
| 141 | ```text |
| 142 | amount=1&amount=5000 |
| 143 | quantity=1&quantity=-1 |
| 144 | price=9.99&price=0.01 |
| 145 | ``` |
| 146 | |
| 147 | Pair with **race conditions** or **server-side rounding** for higher impact; HPP alone often needs a split interpretation across layers. |
| 148 | |
| 149 | --- |
| 150 | |
| 151 | ## 4. TOOLS |
| 152 | |
| 153 | | Tool | How to use | |
| 154 | |---|---| |
| 155 | | **Burp Suite** | Repeater: duplicate keys in raw query/body; Param Miner / extensions for hidden params; compare responses for `first` vs `last` interpretation | |
| 156 | | **OWASP ZAP** | Manual Request Editor; Automated Scan may not deeply fuzz HPP — prefer manual variants | |
| 157 | | **Custom scripts** | Build exact raw HTTP (preserve ordering) — some clients normalize duplicates | |
| 158 | |
| 159 | **Tip**: log **raw** query strings at the app if y |