Сергей Попов

Администратор
30.12.2015
6 223
6 963
Специализация
  1. OSINT
  2. Веб-безопасность
Статус верификации
  1. ✓ Verified
Ноутбук на тёмном антистатическом коврике, экран светится терминалом Python с кодом requests.get и подсвеченным янтарным цветом результатом IDOR. Клавиатуру освещает резкий боковой свет, вокруг — г...


На прошлом проекте по пентесту финтех-платформы мне достался REST API с 340 эндпоинтами. Первые два часа я тупо перебирал user_id в Burp Repeater - копировать запрос, подставить ID, отправить, посмотреть ответ, записать аномалию. К двухсотому запросу глаз замылился настолько, что я пропускал очевидные вещи. Переключился: 20 минут на скрипт с requests, 8 минут на прогон всех 340 эндпоинтов. Скрипт нашёл три IDOR, которые прошли мимо при ручном анализе. Не потому что Python умнее - цикл for просто не устаёт на двухсотой итерации. Дальше - о том, как строить такие скрипты правильно: от HTTP-сессии с нужными заголовками до автоматического обнаружения аномалий в ответах API.

Когда скрипт на Python быстрее Burp Suite - и когда нет​

Прежде чем открывать IDE, разберёмся с границей: автоматизация пентеста на Python не заменяет Burp Suite. Водораздел проходит по одному критерию - формализуемость проверки. Если задачу можно описать одним предложением («проверь все ID от 1 до 500, покажи те, где пришёл 200 с телом больше 100 байт»), скрипт на requests справится быстрее. Если для описания нужен абзац с ветвлениями - Burp Repeater и Intruder в руках пентестера эффективнее.

КритерийPython (requests + BeautifulSoup)Burp Suite Pro
ПреимуществаПолная кастомизация логики. Интеграция в CI/CD. Масштабирование на сотни эндпоинтов. Бесплатный стекВизуальный интерцепт запросов. Встроенный сканер. Экосистема экстендеров (Autorize, ActiveScan++)
ОграниченияНет визуального интерцепта. Ручная обработка TLS-пиннинга. Логику детектирования пишешь с нуляЛицензия ~449 USD/год. Тяжело масштабировать. Сложно встраивать в пайплайны
Когда использоватьМассовая проверка BOLA/IDOR по диапазону ID. Регрессионные проверки после фиксов. Кастомная бизнес-логика. Фаззинг нестандартных форматов (protobuf, msgpack)Исследование нового API - маппинг эндпоинтов, понимание авторизации. Multi-step workflow. WebSocket
Когда НЕ использоватьИнтерактивное исследование незнакомого API. OAuth-потоки с redirect-цепочками. Тестирование SPAАвтоматизация 200+ эндпоинтов с одинаковой логикой. Ночной CI/CD-прогон

На практике подход комбинированный: разведка начинается в Burp - интерцепт трафика, маппинг эндпоинтов, понимание авторизационной модели. Как только появляется паттерн для массовой проверки - пишется скрипт. Burp - для разведки, Python - для масштабируемой проверки.

Отдельно про попытки построить полноценный аналог BurpSuite на Python: это тупик. Burp - GUI-приложение с прокси-сервером, визуальным анализом трафика и зоопарком плагинов, который развивался годами. Воспроизводить всё это - проект на год. Цель другая: взять одну конкретную проверку из Burp (Intruder-прогон, Repeater-тест) и реализовать её скриптом, который запускается одной командой и пишет результат в stdout. Это Python скрипт для тестирования безопасности API, а не «свой Burp».

Место автоматизации в цепочке пентеста API​

Каждый скрипт для тестирования API на уязвимости закрывает задачу конкретного этапа. Привязка к MITRE ATT&CK помогает понять, где именно в цепочке находится каждая проверка и что должно произойти до и после.

Reconnaissance - Active Scanning (T1595): скрипты перечисления эндпоинтов. Отправка requests.get() по списку URL из Swagger-спецификации или по словарю путей - Wordlist Scanning (T1595.003). Парсинг HTML через BeautifulSoup на этом этапе помогает вытянуть ссылки и формы со страниц, расширяя поверхность атаки. Vulnerability Scanning (T1595.002) - автоматизированная проверка известных паттернов уязвимостей по найденным эндпоинтам.

Initial Access - Exploit Public-Facing Application (T1190): эксплуатация найденных дыр. Автоматизированная проверка BOLA, инъекции через фаззинг параметров, обход авторизации - работа на получение доступа к данным или функциям, которые не должны быть доступны текущему пользователю.

Credential Access - Brute Force (T1110): автоматизация подбора учётных данных через login-эндпоинты. Скрипт с requests.Session() поддерживает cookies между попытками и корректно обрабатывает anti-brute-force механизмы. Password Spraying (T1110.003) - когда один пароль проверяется для множества аккаунтов: тут нужна отдельная логика с паузами между аккаунтами и ротацией паролей.

Execution - Python (T1059.006): сам факт использования Python для выполнения атакующих действий.

Контекст применения: описанная автоматизация проверок веб-приложений работает и на внешнем пентесте (публичный API, bug bounty), и на внутреннем (корпоративные API за VPN). Различие - в модели угроз. Внешний пентест: основная головная боль - rate limiting и WAF. Cloudflare может отрезать фаззинг-трафик после нескольких десятков запросов. Внутренний пентест: корпоративная аутентификация (NTLM, Kerberos), для которой нужны дополнительные библиотеки вроде requests_ntlm или requests_kerberos, и нередко - мониторинг со стороны SOC, когда массовые запросы генерируют алерты в SIEM.

Настройка рабочего окружения и HTTP-сессии​

Требования к окружению​

  • ОС: Linux (Kali 2024.x / Ubuntu 22.04+) или macOS. Windows через WSL2 работает без ограничений
  • Python: 3.10+ (match/case и typing improvements упрощают обработку ответов)
  • RAM: 4 ГБ минимум для скриптов; 8 ГБ если параллельно запущен Burp Suite Pro
  • Зависимости: pip install requests beautifulsoup4 urllib3
  • Сеть: доступ к целевому API; для внутренних тестов - активное VPN-подключение
  • Опционально: локальный прокси (Burp или mitmproxy) на 127.0.0.1:8080 для отладки запросов
Фундамент любого пентест-скрипта - правильно настроенная HTTP-сессия. Частая ошибка: вызывать requests.get() напрямую для каждого запроса. Это создаёт новое TCP-соединение каждый раз, теряет cookies и не позволяет переиспользовать авторизационные заголовки. Правильный путь - requests.Session(). Этот объект сохраняет cookies между запросами, переиспользует TCP через keep-alive и позволяет задать заголовки один раз.

Настройка сводится к трём шагам. Первый - задать User-Agent через session.headers.update(). Дефолтный python-requests/2.x блокируется WAF первым делом; замена на строку реального браузера снижает процент ложных блокировок. Второй - добавить Authorization header с Bearer-токеном, API-ключом или Basic-auth строкой в зависимости от схемы аутентификации API. Третий - опционально прописать session.proxies для пропускания трафика через Burp ({"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}), чтобы видеть все запросы скрипта в Burp HTTP History и перепроверять аномалии руками.

Для API с cookie-based авторизацией workflow другой: сначала session.post() на /login с логином-паролем, затем сессия автоматически подхватывает Set-Cookie из ответа. Все последующие запросы через ту же сессию пойдут с нужными cookies без явной передачи. Это критически важно для requests библиотеки при пентесте - без сессии каждый запрос будет «неавторизованным».

Про TLS-верификацию: session.verify = False - стандартный приём на пентесте с самоподписанными сертификатами. Для подавления предупреждений urllib3 в начало скрипта добавляется urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning). В продакшн-коде такое недопустимо, но при ручном тестировании - норма.

Автоматизация проверок BOLA и IDOR через requests​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Критична здесь не отправка запроса, а логика анализа ответа. Проверка "order_total" in r.text - маркер того, что API вернул реальные данные, а не пустую заглушку. Без неё скрипт нагенерит ложных срабатываний: некоторые API возвращают 200 OK с пустым JSON {} для несуществующих объектов. Для повышения точности добавляется сравнение по размеру ответа - сначала запрос к «своему» объекту формирует baseline, затем каждый ответ сравнивается: если len(r.content) совпадает с baseline с точностью до 50 байт и содержит данные - вероятно, это BOLA.

Расширение подхода для проверки Broken Function Level Authorization (API5:2023): тот же принцип, но вместо перебора ID - перебор эндпоинтов с повышенными привилегиями. Авторизуемся как обычный пользователь и пробуем дёрнуть DELETE /api/users/42, PUT /api/admin/settings, POST /api/reports/generate. Если API возвращает 200 вместо 403 - найдена уязвимость авторизации на уровне функций. Тут Python-скрипт даже эффективнее Burp: список административных эндпоинтов готовится из Swagger-документации и прогоняется одной командой.

BeautifulSoup для парсинга форм и извлечения CSRF-токенов​

BeautifulSoup в контексте веб-пентеста решает конкретную задачу: вытянуть из HTML-страницы данные, необходимые для формирования следующего HTTP-запроса. Главный сценарий - парсинг HTML для анализа безопасности: CSRF-токены, скрытые поля форм, __VIEWSTATE в ASP.NET-приложениях.

Работает если: приложение рендерит HTML на сервере (Django, Rails, Laravel, ASP.NET MVC). CSRF-токен передаётся как скрытое поле <input type="hidden">. Токен привязан к сессии, а не к fingerprint браузера.

Не работает если: CSRF-токен генерируется JavaScript после загрузки DOM (SPA на React, Vue, Angular). Приложение использует double-submit cookie pattern без скрытого поля. Страница за CAPTCHA или JS-challenge (Cloudflare).

[Применимо: внешний пентест, серверно-рендеренные веб-приложения]

Типовая цепочка: GET-запрос на страницу с формой, парсинг HTML для извлечения токена, POST-запрос с токеном и полезной нагрузкой.
Python:
from bs4 import BeautifulSoup
import requests

s = requests.Session()
page = s.get("https://target.com/change-password")
soup = BeautifulSoup(page.text, "html.parser")
token = soup.find("input", {"name": "csrf_token"})["value"]

r = s.post("https://target.com/change-password", data={
    "csrf_token": token, "new_password": "Pentest123!"
})
print(f"Status: {r.status_code}, Length: {len(r.content)}")
Имя поля CSRF-токена варьируется между фреймворками: _token (Laravel), authenticity_token (Rails), __RequestVerificationToken (ASP.NET), csrfmiddlewaretoken (Django). Для универсальности собираются все скрытые поля: soup.find_all("input", {"type": "hidden"}) - и формируется словарь {field["name"]: field["value"]} для автоматической подстановки в запрос.

Зачем это нужно в цепочке пентеста: BeautifulSoup парсинг для веб-пентеста - связующее звено между обнаружением уязвимости и её автоматизированной эксплуатацией. Пример: обнаружена BOLA на эндпоинте смены email. Эндпоинт защищён CSRF-токеном, который обновляется при каждом запросе страницы. Без автоматического извлечения токена перед каждой итерацией автоматизация невозможна - придётся вручную копировать токен в Burp Repeater. А это уже не автоматизация, а мучение.

Ещё один сценарий - парсинг страниц ошибок. При фаззинге API сервер иногда возвращает HTML-страницу со stack trace вместо JSON. BeautifulSoup позволяет вытянуть текст ошибки: soup.find("pre") часто содержит трейсбек с названиями внутренних модулей, путями файлов на сервере и версиями библиотек - ценные данные для развития атаки. Это частный случай Security Misconfiguration (A05:2021 по OWASP Top 10) - verbose error messages, торчащие наружу.

Фаззинг API-эндпоинтов и детекция аномалий​

Фаззинг API на Python - отправка нестандартных значений в параметры и анализ отклонений от нормы. Это автоматизированное сканирование веб-уязвимостей в миниатюре: не тысячи проверок как у коммерческого сканера, а десятки целевых пейлоадов под конкретный параметр с осмысленным анализом ответов.

Работает если: API не защищён WAF с агрессивным rate limiting. Ответы содержат достаточно информации для сравнения (разные статус-коды, разный размер тела, разное время ответа). Параметры передаются в URL, query string или JSON body.

Не работает если: WAF (Cloudflare, AWS WAF, ModSecurity CRS) блокирует после 10-20 запросов с «подозрительными» символами. API возвращает одинаковый ответ при любом вводе (blind-сценарий). Параметры шифруются или подписываются на клиенте перед отправкой.

[Применимо: внешний пентест (с ограничениями по rate limiting), внутренний пентест, bug bounty]

Принцип: сначала «нормальный» запрос формирует baseline. Каждый фаззинг-запрос сравнивается с baseline по трём метрикам: статус-код, длина тела ответа, время ответа.
Python:
import requests, time

s = requests.Session()
s.headers.update({"Authorization": "Bearer <token>"})
payloads = ["' OR 1=1--", "<script>", "../../../etc/passwd", "${7*7}", "{{7*7}}"]
url = "https://api.target.com/v1/search"

baseline = s.get(url, params={"q": "normalvalue"})
for p in payloads:
    t0 = time.time()
    r = s.get(url, params={"q": p}, timeout=10)
    delta = time.time() - t0
    if r.status_code != baseline.status_code or abs(len(r.content) - len(baseline.content)) > 50:
        print(f"[ANOMALY] payload={p} status={r.status_code} len={len(r.content)} time={delta:.2f}s")
Список payloads здесь минимальный - для демонстрации. В реальном проекте словарь расширяется до сотен записей из SecLists (каталоги Fuzzing/ и Payloads/). SQLi-пейлоады проверяют Injection (A03:2021 по OWASP Top 10). SSTI-пейлоады ({{7[I]7}}, ${7[/I]7}) проверяют Server-Side Template Injection: если в ответе появляется 49 - шаблонизатор исполнил выражение, и это уже не «аномалия», а RCE на горизонте. Path traversal (../../../etc/passwd) проверяет чтение файлов за пределами webroot.

Порог abs(len(r.content) - len(baseline.content)) > 50 - отправная точка, которую нужно калибровать под конкретный API. Если ответы содержат динамические данные (timestamp, request ID, session token), длина будет плавать. Более устойчивый подход - искать конкретные маркеры в тексте: строки traceback, exception, syntax error, mysql, postgresql. Их наличие при фаззинг-пейлоаде - почти гарантированный индикатор уязвимости.

Временная аномалия - отдельная метрика, которую часто упускают. Если baseline приходит за 50 мс, а при пейлоаде ' OR SLEEP(5)-- задержка вырастает до 5 секунд - это индикатор time-based blind SQL injection. Проверка: if delta > baseline_time * 3 или жёсткий порог if delta > 3.0. Для REST API security testing этот подход критичен: «слепые» инъекции не проявляются в статус-коде или теле ответа, только во времени.

Ещё деталь: при фаззинге стоит логировать не только аномалии, но и все ответы в файл. Потом этот лог можно загрузить в Burp через paste from file и просмотреть в Repeater. Связка инструментов пентестера на Python с GUI-анализом: массовая отправка скриптом, точечная верификация в Burp.

Ограничения: когда Python-скрипты не работают​

Автоматизация на requests и BeautifulSoup покрывает кучу рутинных проверок, но не серебряная пуля. Вот конкретные границы.

WAF и rate limiting. Cloudflare Bot Management, AWS WAF с OWASP CRS, ModSecurity - детектируют массовые запросы с фаззинг-пейлоадами. Обходы (ротация User-Agent, time.sleep() между запросами, IP-ротация через SOCKS-прокси) усложняют скрипт настолько, что проще взять специализированный инструмент: ffuf для фаззинга с встроенным rate-limit, Nuclei для шаблонизированных проверок.

SPA и JavaScript-зависимая логика. Если API-запросы формируются JavaScript-кодом (React, Angular, Vue), BeautifulSoup бесполезен - он видит только исходный HTML до исполнения скриптов. Нужны headless-браузеры: Playwright (быстрее и стабильнее Selenium) или puppeteer через pyppeteer.

WebSocket и gRPC. Библиотека requests работает только с HTTP/HTTPS. Для WebSocket-эндпоинтов - websockets или socketio. Для gRPC - grpcio с protobuf-генерацией. Burp Suite Pro поддерживает WebSocket из коробки, что делает его предпочтительным для интерактивного анализа таких протоколов.

Сложные аутентификационные потоки. OAuth 2.0 с PKCE, SAML SSO, MFA - воспроизведение browser-flow в requests технически возможно, но трудоёмко. Client Credentials и Password Grant - без проблем. Authorization Code Flow с redirect-цепочкой - задача для Selenium или ручной работы. Broken Authentication (API2:2023) в OAuth-конфигурации чаще находится интерактивным анализом, а не автоматизированным перебором.

False positive management. Скрипт находит аномалии, но не понимает бизнес-контекст. API, который возвращает разный размер ответа для пользователей с разными подписками - не BOLA, а feature. API, отвечающий 500 на пустую строку - не injection, а баг валидации. Каждая находка требует ручной верификации. Без этого шага отчёт превращается в мусор.

Написание сканера уязвимостей на Python оправдано для задач с чёткой формализуемой логикой: массовая проверка BOLA, регрессия после фиксов, фаззинг параметров с измеримым baseline, автоматический сбор метаданных (версии серверов, открытые Swagger-эндпоинты, утечка отладочной информации через Security Misconfiguration, A05:2021). Для всего остального - Burp Suite в руках пентестера.

Минилаб для отработки​

Требования к окружению​

  • RAM: 4 ГБ минимум (2 ГБ на Docker-контейнер + 2 ГБ на хостовую систему с Python)
  • ОС: любая с Docker Engine - Linux, macOS, Windows (WSL2)
  • Зависимости: Docker, Python 3.10+, pip install requests beautifulsoup4
  • Сеть: только localhost, интернет не требуется после скачивания образа
Целевое приложение: OWASP Juice Shop - уязвимое веб-приложение с REST API и HTML-формами. Запуск одной командой: docker run -p 3000:3000 bkimminich/juice-shop. API доступен по http://localhost:3000/api/, документация по http://localhost:3000/api-docs/.

Сценарий 1 - BOLA: зарегистрировать двух пользователей через POST /api/Users/, авторизоваться от первого через POST /rest/user/login, получить Bearer-токен из ответа. Запустить скрипт перебора ID корзин через GET /api/BasketItems/{id} - Juice Shop намеренно не проверяет принадлежность объекта.

Сценарий 2 - Демонстрация ограничений BeautifulSoup: открыть главную GET /, попробовать парсить формы. Juice Shop - SPA на Angular, поэтому BeautifulSoup покажет только пустой каркас без динамического контента. Наглядно показывает границу инструмента: для SPA нужен Playwright.

Сценарий 3 - Фаззинг SQLi: отправить SQL-пейлоады в поиск через GET /rest/products/search?q=PAYLOAD. Juice Shop намеренно уязвим - при пейлоаде ' OR 1=1-- API вернёт все товары вместо пустого результата, а при '))-- покажет stack trace с деталями SQLite-запроса. Тот самый момент, когда в ответе вместо JSON прилетает трейсбек с путями файлов.

Альтернативный стенд: crAPI (Completely Ridiculous API) от OWASP - специально для поиска уязвимостей веб-приложений скриптом. Покрывает BOLA, Mass Assignment, Unrestricted Resource Consumption (API4:2023) и другие позиции из OWASP API Security Top 10. Запуск через docker-compose (yaml валяется на GitHub OWASP/crAPI).

Большинство материалов про пентест API на Python начинаются с «установите requests» и заканчиваются отправкой GET-запроса. Это как учить хирургию на примере бинта. Реальная сложность не в HTTP-вызовах, а в логике принятия решений: как отличить BOLA от нормального поведения API, как не нагенерить сотню false positive, как учитывать rate limiting и при этом не положить прод заказчика. Написать requests.get() может студент второго курса. Написать скрипт с корректным анализом ответов - задача другого уровня.

За последние пару лет наблюдаю повторяющийся сценарий: команды начинают с «автоматизируем всё на Python» и через квартал откатываются к Burp Repeater. Проблема не в автоматизации, а в попытке применить её ко всему подряд. Скрипты идеальны для проверок с чёткой формализуемой логикой - одно условие, один цикл, один результат. Исследование нового API, где бизнес-логика ещё не понятна - работа для человека, не для цикла.

Правило, подтверждённое десятками проектов: если задачу описываешь одним предложением - пиши скрипт. Если нужен абзац - работай руками в Burp. И не пытайся строить «универсальный сканер»: каждый хороший пентест-скрипт одноразовый, написан под конкретный API и конкретную уязвимость. Попытка сделать «всё в одном» заканчивается тем, что инструмент не делает хорошо ничего. Если хочешь потренировать эту цепочку на живом стенде - на HackerLab лежат лабы по web-уязвимостям, где BOLA и injection ломаются без риска задеть чужой прод.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab