Перейти к содержимому

ffuf: фаззинг виртуальных хостов и скрытых параметров — от wordlist до обхода rate-limit

ffuf: фаззинг виртуальных хостов и скрытых параметров — от wordlist до обхода rate-limit
Время чтения: 11 мин.

На пентесте интернет-магазина основной домен показал стандартную витрину — ни одной зацепки. Один запрос с подменённым заголовком Host раскрыл staging-окружение с включённым debug-режимом и полным доступом к панели администратора. На том же IP-адресе, за тем же nginx. От запуска ffuf до находки — четыре минуты. По данным Verizon DBIR 2025, 26% подтверждённых инцидентов связаны с веб-атаками, и часть из них начинается именно с таких скрытых точек входа, которые разработчики считали невидимыми.

Место ffuf в цепочке разведки

ffuf (Fuzz Faster U Fool) — инструмент веб-фаззинга на Go с открытым исходным кодом. Фаззинг — это автоматизированная подстановка значений из словаря в HTTP-запросы, чтобы обнаружить скрытый контент: директории, файлы, виртуальные хосты, параметры.

Если говорить в терминах MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1595 — её идентификаторы), ffuf работает на этапе разведки — Wordlist Scanning (T1595.003, Reconnaissance). Результат фаззинга расширяет поверхность атаки и готовит почву для следующих шагов: от обнаружения Domain Properties (T1590.001) до эксплуатации Exploit Public-Facing Application (T1190, Initial Access).

Цепочка выглядит так: пассивная разведка (OSINT, DNS-записи) → активный фаззинг с ffuf → анализ найденных эндпоинтов → эксплуатация. Без качественного фаззинга половина поверхности атаки — скрытые панели, тестовые API, забытые виртуальные хосты — остаётся невидимой.

Статус инструмента: ffuf v2.1.0, репозиторий github.com/ffuf/ffuf, активно поддерживается, основной мейнтейнер — joohoi. Используется как стандарт наряду с gobuster и wfuzz.

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

Прежде чем запускать команды — проверьте готовность:

  • ОС: Kali Linux 2023+, любой Linux-дистрибутив, macOS, Windows через WSL
  • RAM: от 2 ГБ свободных (4 ГБ, если параллельно крутится Docker-стенд)
  • Go: версия 1.21+ для ffuf v2.1.0 (нужна только при сборке из исходников; загляните в go.mod репозитория)
  • Установка ffuf: в Kali — sudo apt install ffuf; на любой ОС с Go — go install github.com/ffuf/ffuf/v2@latest (эта же команда обновит до последней версии)
  • Словари: коллекция SecListsgit clone https://github.com/danielmiessler/SecLists.git
  • Права: root для ffuf не нужен, хватит сетевого доступа к цели
  • Для минилаба: Docker и Docker Compose, интернет для скачивания образа

ffuf vs альтернативы: когда что использовать

Критерий ffuf gobuster wfuzz Burp Intruder
Скорость Высокая (Go) Высокая (Go) Средняя (Python) Средняя-низкая
Где ставить FUZZ Где угодно: URL, заголовки, тело, cookie Фиксированные режимы (dir/vhost/dns) Где угодно Где угодно
Несколько словарей Да (clusterbomb, pitchfork) Нет Да Да
Автокалибровка фильтров Да (-ac) Нет Нет Нет нативно
Replay через Burp Да (-replay-proxy) Нет Нет Нативно
Когда использовать Универсальный фаззер: vhost, params, dirs Простой dir/vhost перебор Сложные цепочки с зависимостями GUI-анализ, ручная работа
Когда НЕ использовать Многошаговая аутентификация с CSRF Нужна подстановка в произвольное место Критична скорость на больших словарях Community edition (throttled)

Ключевое отличие: ffuf — не аналог dirb для перебора директорий. Слово FUZZ ставится в любое место HTTP-запроса, что по гибкости приближает его к Burp Intruder. Но ffuf бесплатен и живёт в терминале — идеален для автоматизации и встраивания в pipeline разведки.

Фаззинг виртуальных хостов: от теории к практике

Чем vhost fuzzing отличается от DNS-перебора

Виртуальный хост (vhost) — механизм, при котором один IP-адрес обслуживает несколько сайтов. Какой сайт отдать, сервер решает по заголовку Host в HTTP-запросе или по расширению SNI (Server Name Indication — указание имени сервера при TLS-рукопожатии) для HTTPS.

Два способа обнаружить скрытые хосты:

  1. DNS brute force — отправляет DNS-запросы к DNS-серверу, проверяя: резолвится ли staging.target.com? Находит только поддомены, зарегистрированные в публичном DNS.
  2. Vhost fuzzing — отправляет HTTP-запросы напрямую к серверу, подменяя заголовок Host. Находит виртуальные хосты, которых нет в DNS — внутренние панели, staging, dev-окружения.

Второй способ раскрывает самое интересное: разработчики создают виртуальный хост для staging без DNS-записи и считают его невидимым. Поэтому vhost fuzzing — одна из самых недооценённых техник разведки в bug bounty, о чём справедливо пишут в thehacker.recipes.

Практика: находим скрытый виртуальный хост

Базовая команда для ffuf vhost fuzzing из официального репозитория:

ffuf -w SecLists/Discovery/DNS/subdomains-top1million-5000.txt \
     -u http://target.com \
     -H "Host: FUZZ.target.com" \
     -fs 4242

Что делает каждый флаг:

  • -w — путь к словарю. Файл subdomains-top1million-5000.txt содержит 5000 популярных поддоменов: staging, dev, admin, api и подобные. Для более глубокого перебора берите subdomains-top1million-20000.txt
  • -u — URL цели (основной домен или IP-адрес)
  • -H "Host: FUZZ.target.com" — тут вся магия: ffuf подставляет каждое слово из словаря вместо FUZZ в заголовок Host
  • -fs 4242 — фильтрует ответы размером 4242 байт. Это размер «дефолтного» ответа, который сервер отдаёт, когда виртуальный хост не найден

Как узнать размер дефолтного ответа? Перед запуском ffuf отправьте запрос с заведомо несуществующим поддоменом: curl -s -H "Host: thisDoesNotExist123.target.com" http://target.com | wc -c. Полученное число подставьте в -fs. Или — и это удобнее — используйте автокалибровку: замените -fs 4242 на -ac, и ffuf определит дефолтный ответ сам.

Вывод при обнаружении виртуального хоста выглядит так:

staging    [Status: 200, Size: 15234, Words: 345, Lines: 122, Duration: 84ms]
dev        [Status: 403, Size: 287, Words: 12, Lines: 8, Duration: 91ms]

Status 200 — хост доступен, копаем дальше. Status 403 — хост существует, но закрыт. Это тоже ценная находка: 403 означает, что доступ ограничен (по IP или через авторизацию), но сам хост реальный. Дальше можно пробовать обходить ограничения.

Если приложение не позволяет фаззить через заголовок Host — например, за обратным прокси с жёсткой валидацией — используйте альтернативный синтаксис: ffuf -c -w wordlist.txt -u "http://FUZZ.target.com/". Здесь FUZZ в URL заставляет HTTP-клиент ffuf автоматически выставить корректные SNI и заголовок Host для каждого кандидата. Флаг -r (follow redirects) добавляйте отдельно, если сервер отвечает редиректами.

Контекст применения виртуальных хостов в пентесте:

  • Внешний пентест: ищете staging/dev-окружения за тем же IP, что и основной сайт
  • Внутренний пентест: за одним IP корпоративного сервера часто живут десятки приложений (Jira, GitLab, Jenkins), невидимых через публичный DNS. Тут vhost fuzzing особенно результативен
  • Bug bounty: при скоупе *.target.com — первый шаг после пассивной DNS-разведки

Фаззинг скрытых параметров ffuf

Веб-приложение может обрабатывать параметры, которые нигде не видны в интерфейсе: debug-флаги, скрытые поля форм, внутренние API-ключи. Фаззинг параметров помогает их обнаружить, и ffuf справляется с этим не хуже, чем с vhost-перебором.

GET-параметры

Для перебора имён GET-параметров FUZZ ставится на место имени: ffuf -w SecLists/Discovery/Web-Content/burp-parameter-names.txt -u "https://target.com/page.php?FUZZ=test_value" -fs 4242. Значение test_value произвольное — задача найти имя параметра, которое меняет поведение приложения (размер ответа, HTTP-код, количество слов).

В результатах ищите ответы, которые отличаются от дефолтного. Если параметр debug вернул ответ на 500 байт больше — он включает отладочную информацию. Параметр admin с кодом 403 — скрытая проверка привилегий. И то, и другое — точки для дальнейшей эксплуатации.

POST-параметры и Content-Type фильтрация ffuf

Для POST-запросов добавляются флаги -X POST и -d для тела запроса: ffuf -w paramnames.txt -X POST -d "FUZZ=test" -H "Content-Type: application/x-www-form-urlencoded" -u https://target.com/api/action -fc 401. Флаг -fc 401 отфильтрует ответы с кодом 401 (неавторизован): если эндпоинт возвращает 401 на любой невалидный параметр, нас интересуют все остальные коды.

Для JSON API меняйте Content-Type и формат данных: ffuf -w paramnames.txt -X POST -d '{"FUZZ":"test"}' -H "Content-Type: application/json" -u https://target.com/api/v2/action -mc 200. Флаг -mc 200 покажет только ответы с кодом 200 — успешно обработанные запросы.

Фильтрация ответов и автокалибровка

При фаззинге сервер возвращает тысячи ответов — большинство одинаковые. Без фильтрации работать невозможно. У ffuf два механизма.

Ручные фильтры (флаги -f*): -fc 404,403 исключает ответы с указанными кодами; -fs 1234 — ответы размером 1234 байт; -fw 42 — с 42 словами; -fl 10 — с 10 строками. Фильтры убирают шум.

Матчеры (флаги -m*) работают наоборот, показывают только совпадения: -mc 200,301 отображает ответы с кодами 200 и 301; -mr "root:x" — ответы, содержащие строку root:x. Последний вариант полезен при тестировании LFI (Local File Inclusion — уязвимость, позволяющая читать файлы на сервере через манипуляцию путём к файлу).

Автокалибровка (-ac) — одна из сильных сторон ffuf, которую русскоязычные гайды обычно игнорируют. Вместо ручного определения дефолтного размера через curl, ffuf сам отправляет несколько случайных запросов, определяет типичный ответ и фильтрует его: ffuf -w wordlist.txt -u http://target.com/FUZZ -ac. Для нестандартных случаев есть -acc (custom calibration string) и -acs (custom strategies), но в подавляющем большинстве сценариев хватает -ac.

Обход rate-limit при фаззинге ffuf

На реальных целях — программы bug bounty, продакшн-серверы — агрессивный фаззинг быстро приведёт к бану по IP. По умолчанию ffuf работает на 40 потоках, и это серьёзная нагрузка. Три механизма управления скоростью решают проблему.

ffuf -w wordlist.txt \
     -u http://target.com \
     -H "Host: FUZZ.target.com" \
     -ac \
     -t 5 \
     -p "0.5-1.5" \
     -rate 10

Что делает каждый флаг:

  • -t 5 — снижает параллелизм до 5 потоков вместо 40 по умолчанию
  • -p "0.5-1.5" — случайная пауза от 0.5 до 1.5 секунд между запросами. Случайность тут принципиальна: фиксированный интервал проще детектировать на стороне WAF (Web Application Firewall — межсетевой экран для веб-приложений)
  • -rate 10 — жёсткий лимит: максимум 10 запросов в секунду. -rate ограничивает RPS через token bucket независимо от числа потоков; при совместном использовании с -t фактическая скорость определяется обоими параметрами

Для совсем жёстких ограничений: -maxtime 300 ограничивает общее время сканирования (в секундах). При рекурсии добавьте -maxtime-job 60 — каждая поддиректория будет обрабатываться максимум минуту, после чего ffuf перейдёт к следующей.

Replay через Burp Suite: добавьте -replay-proxy http://127.0.0.1:8080, и ffuf будет дублировать все найденные матчи через Burp. Основной трафик идёт напрямую (быстро), а каждая находка дополнительно появляется в Burp History для детального разбора. Так вы не гоните весь поток через прокси и не теряете в скорости.

Чего делать не стоит: подмена X-Forwarded-For для обхода rate-limit встречается в старых гайдах, но на практике серьёзные WAF (Cloudflare, AWS WAF) не доверяют этому заголовку от клиента. На уровне базового nginx/Apache — может сработать, если rate-limit настроен по $http_x_forwarded_for, но это скорее ошибка конфигурации цели, а не надёжная техника. WAF с ML-детектом (Cloudflare Bot Management) распознаёт фаззинг по поведенческим паттернам независимо от заголовков.

Ограничения и когда ffuf не подходит

  • Словарь = потолок. ffuf перебирает то, что вы ему дали. Если staging-v2-internal отсутствует в словаре — вы его не найдёте. Решение: генерируйте кастомные словари через CeWL (извлечение слов с сайта цели), добавляйте паттерны из собранных данных разведки
  • Сложная аутентификация. Если цель требует CSRF-токен на каждый запрос или многошаговый OAuth-флоу, ffuf не справится. Тут нужен Burp Suite Turbo Intruder с Python-скриптами или кастомный фаззер
  • Риск DoS. На 40 потоках по умолчанию ffuf создаёт нагрузку, сопоставимую с лёгкой DoS-атакой для слабого сервера. На продакшне без согласования с владельцем — прямой путь к инциденту. Начинайте с -t 5 и увеличивайте постепенно
  • HTTPS и SNI. При vhost fuzzing через HTTPS сервер использует SNI для определения хоста на этапе TLS-рукопожатия. Если SNI не совпадает с заголовком Host — получите ошибку TLS вместо HTTP-ответа. Решение: фаззьте через HTTP (порт 80) или используйте синтаксис с FUZZ в URL
  • Поведенческий WAF. Современные WAF детектируют фаззинг по паттернам поведения, а не только по RPS. Обход таких систем — отдельная тема, выходящая за рамки базового ffuf

Минилаб: стенд за 10 минут

Требования: Docker, Docker Compose, 2 ГБ свободного RAM, интернет для скачивания образа.

Шаг 1. Клонируйте стенд ffuf.me — среду, созданную специально для отработки навыков фаззинга с ffuf: git clone https://github.com/adamtlangley/ffufme.git && cd ffufme. Стенд упоминается на странице официального репозитория ffuf как рекомендуемая площадка для практики.

Шаг 2. Запустите контейнер: docker-compose up -d. Проверка: curl -s http://localhost | head -3 — должна вернуться HTML-страница с инструкциями. Видите HTML — стенд работает.

Шаг 3. Попробуйте ffuf vhost fuzzing на стенде: ffuf -w SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u http://localhost -H "Host: FUZZ.ffuf.me" -ac -t 10. Автокалибровка отсечёт шум, а реальные виртуальные хосты появятся в результатах. Пустой вывод — все ответы одинаковые, скрытых хостов нет (на этом стенде основной акцент на директории и параметры).

Шаг 4. Переключитесь на фаззинг параметров: ffuf -w SecLists/Discovery/Web-Content/burp-parameter-names.txt -u "http://localhost/cd/param/data?FUZZ=1" -ac. Найденные параметры покажут, как приложение реагирует на скрытые входные данные.

Онлайн-версия стенда доступна по адресу http://ffuf.me — подходит для тренировки без Docker, но скорость ограничена сетевым соединением.

Большинство русскоязычных гайдов по ffuf сводятся к перечислению флагов и паре команд для перебора директорий — по сути, пересказ ffuf -h. Реальная ценность ffuf не в скорости перебора /admin и /backup — gobuster делает то же самое. Ценность — в гибкости: FUZZ в заголовке Host превращает перебиратель директорий в инструмент, который находит то, чего нет ни в одном DNS-резолвере. Но есть проблема, о которой редко говорят: качество результата на 90% определяется словарём, а не инструментом. На одном из engagement я видел, как коллега два часа фаззил цель с дефолтным common.txt на 4700 записей — ноль результатов. Потом — 15 минут с кастомным словарём из 200 слов, собранных из JavaScript-файлов через linkfinder и cewl, дали три скрытых эндпоинта. Один из них привёл к полноценному RCE. Инструмент — это рука. Голова — это понимание, что именно вы ищете, и словарь, собранный под конкретную цель. Кто начинает путь в пентест и хочет выстроить эту логику системно — базовый трек IB Basics в Codeby Academy для тех, кто не хочет в одиночку ковыряться в Kali.

Эту тему и смежные навыки разбирают на практике в курсе «Тестирование веб-приложений на проникновение (WAPT)» Codeby Academy.