Авторские курсы Михаила Тарасова

WAF-байпас через JSON-перезапаковку: когда парсер бэкенда — твой лучший друг

WAF-байпас через JSON-перезапаковку: когда парсер бэкенда — твой лучший друг

Точка входа:
• Что вижу: Эндпоинт /api/graphql жрёт application/json, но WAF на входе — настоящий цербер, блочит любую кавычку или sleep(). Однако, если подсунуть ему кривой JSON, который он не понимает, а бэкенд — схавает, защита превращается в тыкву.
• Как поймал: Burp Suite Repeater + фаззинг руками. Стандартный {"id":"1'"} — бан. А вот {"id": "1", "id": "1' or 1=1--"} внезапно вернул не 403 Forbidden от WAF, а 200 OK с измененным контентом страницы. Классика рассинхронизации.
• Чем пахнет: SQL-инъекция, спрятанная за JSON Parameter Pollution (JPP). Вероятность — 10 из 10. Если WAF смотрит только на первый параметр, а бэкенд на последний — это не баг, это фича.
Че почем:
• Эксплойт: WAF видит {"id": "1", ...} и думает, что всё чисто, потому что его правила настроены на проверку первого вхождения ключа id. Он просто не дочитывает до второго. А вот бэкенд (привет, кривые PHP/Node.js парсеры) берет последнее значение ключа и подставляет в запрос.

• Обход защиты: Загрязнение параметров JSON (JPP). Мы даём WAF’у валидный и безобидный параметр, который он проверяет и пропускает. Сразу за ним пихаем второй параметр с тем же именем, но уже с нашим SQLi-payload’ом. WAF до него не добирается, а бэкенд — наоборот, использует именно его.

• Доказательство: В ответе вместо имени юзера прилетает что-то в духе "name": "admin@$2y$10$...". Видишь хэш пароля? Это не глюк. Это WAF, который думал, что он шериф, а оказался просто зрителем.

Советы:
• План атаки:
1. Найди все эндпоинты, принимающие JSON.
2. Проверь их на JPP. Отправь {"param":"legit", "param":"test"} и посмотри по ответу, какое значение было обработано — первое или последнее. Если последнее — поздравляю, у тебя есть вектор.
3. Вооружись sqlmap и файлом запроса из Burp. Укажи ему, что точка инъекции — второй параметр: sqlmap -r request.txt --level=5 --risk=3 --tamper=json_parameter_pollution. Если такого тампера нет, напиши свой на пять строк.
4. Дампи базу данных: sqlmap -r request.txt --current-db.
5. Если повезет, пробуй --os-shell и закрепляйся.

3 вектора для добивания:
• Пробуй мутировать сам JSON. Вместо {"id": "1'..."} кидай {"id": "1", "comment":"'...", "id": "..."}. Иногда парсеры склеивают значения или неправильно определяют границы.
• Поиграйся с пробелами и спецсимволами перед ключом: {"\u0020id": "legit", "id": "payload"}. Некоторые WAF’ы споткнутся на юникодном пробеле и не увидят первый ключ, а второй пропустят как дубликат.
• Если JPP не работает, попробуй JSON-массивы. {"id": "1' OR 1=1--", "legit"}. Некоторые фреймворки (типа Ruby on Rails) могут взять первый элемент массива и подставить в запрос.

Мои курсы