Главная / Блог / SSRF в CTF: эксплуатация уязвимости до флага

14 мин.00

SSRF в CTF: эксплуатация уязвимости до флага

SSRF в CTF: эксплуатация уязвимости до флага

На последнем CTF web-таск уровня hard занял у команды четыре часа. Из них три — не на поиск SSRF. Параметр url в эндпоинте /api/preview нашёлся за пять минут разведки через Burp. Всё остальное время ушло на обход blacklist-фильтра и превращение слепой SSRF в чтение флага из Redis на localhost:6379 через gopher://. Цепочка: обнаружение → подтверждение через OOB → bypass blacklist → gopher payload → флаг. Ниже — разбор каждого шага с конкретными payload'ами, которые работают на тасках от easy до hard. Без воды, с объяснением «почему это сработало» и альтернативными векторами, когда основной путь заблокирован.

Бизнес-логика SSRF: зачем серверу ходить по чужим URL

Суть Server-Side Request Forgery — заставить сервер отправить HTTP-запрос по адресу, который выбрал атакующий. Сервер сидит внутри периметра: ему доступны loopback-адреса (127.0.0.1), приватные IP-диапазоны соседних сервисов (10.x.x.x, 172.16.x.x, 192.168.x.x) и облачные metadata-эндпоинты (169.254.169.254). Файрволы и сегментация не спасают от запросов изнутри — поэтому SSRF уязвимость превращает внешнюю атаку во внутреннюю разведку. Подробнее — в нашем руководстве по создание ctf заданий.

По классификации OWASP это A10:2021 в OWASP Top 10 для веб-приложений и API7:2023 в OWASP API Security Top 10 (Server-Side Request Forgery — API принимает URL от пользователя и выполняет запрос без валидации). В терминах MITRE ATT&CK эксплуатация SSRF раскладывается в цепочку:

  • Exploit Public-Facing Application (T1190, Initial Access) — веб-приложение как точка входа за периметр
  • Network Service Discovery (T1046, Discovery) — сканирование внутренних портов через серверный запрос
  • Cloud Instance Metadata API (T1552.005, Credential Access) — чтение облачных метаданных с IAM-кредами
  • Exploitation of Remote Services (T1210, Lateral Movement) — перемещение вглубь инфраструктуры с полученными учётными данными

В CTF это работает так: таск даёт веб-приложение с функцией, принимающей URL. За приложением — внутренний сервис с флагом. Задача — добраться до него, используя сервер как прокси. Мотивация атакующего в реальном мире — доступ к cloud credentials, секретам, внутренним API и базам данных. В CTF — к flag.txt, но механика та же.

Где искать точки входа для эксплуатации SSRF в CTF-тасках

Прежде чем стрелять payload'ами — маппинг поверхности атаки. На easy-тасках SSRF уязвимость прячется в очевидных местах. На hard — придётся копать.

Очевидные entry points. Параметры с именами url, callback, redirect, file_url, webhook, image_url, src, dest, path, target, reference, feed в POST/GET-запросах. Функции загрузки файла по ссылке, генерации превью, проверки доступности URL, отправки тестового вебхука — классика easy-таска. Видите кнопку «Проверить URL» или «Загрузить по ссылке»? Перед вами SSRF entry point.

Скрытые entry points (medium/hard). HTTP-заголовки X-Forwarded-Host, Referer, X-Original-URL — сервер иногда использует их для формирования исходящих запросов. По данным YesWeHack, server-side аналитика, которая ходит по URL из заголовка Referer для анализа входящего трафика — один из наименее очевидных SSRF-векторов. Достаточно подставить Referer: http://your-collaborator/ в любой запрос к приложению и проверить callback.

URL внутри файлов. XML с XXE-payload'ом, SVG с тегом <image href="...">, Markdown с внешними изображениями, HTML-шаблоны для PDF-генераторов, DOCX/XLSX с внешними ссылками — всё это триггерит серверный fetch при парсинге. В CTF такие таски объединяют две уязвимости (XXE + SSRF или file upload + SSRF) и оцениваются выше.

JavaScript-бандлы. На hard-тасках стоит искать скрытые эндпоинты в JS-файлах: роуты /api/fetch, /api/proxy, /api/import, /api/preview, /api/unfurl могут не отображаться в UI, но принимать запросы напрямую. LinkFinder или расширение JSpector для Burp помогают автоматизировать поиск. Переменные с именами url, target, dest, src, href, load, request в JS-коде — прямые указатели на fetch-функции.

Подтверждение SSRF уязвимости: out-of-band каналы и классический ответ

Подозрительный параметр найден. Подставляем внешний URL и смотрим — приходит ли запрос от сервера. В CTF два сценария:

Classic SSRF — сервер возвращает содержимое fetched-ресурса в HTTP-ответе. Подставляем http://127.0.0.1/ — видим HTML локального веб-сервера прямо в body ответа. Самый простой вариант: easy-таски с параметром url=https://example.com и ответом с контентом запрошенной страницы.

Blind SSRF — сервер выполняет запрос, но не показывает ответ. Тут нужен out-of-band (OOB) канал: контролируемый сервер, фиксирующий входящее соединение. Blind SSRF — норма на medium и hard.

Делай раз: готовим OOB-listener. Варианты: Burp Collaborator (Burp Suite Pro), interactsh-client (open-source альтернатива от ProjectDiscovery), webhook.site или простой listener на VPS через nc -lvnp 8080. Совет: дефолтный домен Collaborator (oastify.com) иногда блокируется на CTF-платформах — self-hosted interactsh-server надёжнее.

Делай два: отправляем запрос с OOB-payload'ом — подставляем url=http://YOUR-COLLABORATOR-ID.oastify.com в уязвимый параметр через curl или Burp Repeater.

Делай три: проверяем listener. DNS-запрос от IP сервера = SSRF подтверждена. Тонкость: даже если HTTP-трафик наружу заблокирован, DNS-резолв почти всегда разрешён. Payload http://uniqueid.attacker-dns.com может не дойти как HTTP, но DNS-обращение зафиксируется — этого хватит для подтверждения уязвимости и дальнейшей работы.

На medium-тасках обычно нужна blind SSRF с дальнейшей экстракцией данных: через тайминги (открытый порт отвечает быстрее закрытого) или через OOB-канал (DNS exfiltration — данные передаются как поддомены: data.attacker.com).

Эксплуатация SSRF: доступ к внутренним сервисам через подделку запросов

Сканирование портов через SSRF

После подтверждения — разведка внутренней сети. Базовый подход: подставляем http://127.0.0.1:PORT/ в уязвимый параметр и перебираем порты. По разнице в ответах (размер body, время ответа, статус-код) определяем открытые сервисы. Для автоматизации — ffuf:

ffuf -w /usr/share/seclists/Fuzzing/4-digits-0000-9999.txt:PORT \
  -u http://target.ctf/api/preview -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "url=http://127.0.0.1:PORT/" -fs 0

Предусловие: система с установленными ffuf и SecLists (Kali Linux из коробки). Флаг -fs 0 отсекает ответы нулевого размера — закрытый порт обычно возвращает пустой body. Ответы с ненулевым size — открытые сервисы. Типичные находки в CTF: Redis (6379), Elasticsearch (9200), MySQL (3306), внутренние HTTP API (8080, 5000, 3000), PHP-FPM/FastCGI (9000).

В Kubernetes-тасках стоит проверить http://kubernetes.default.svc — если отвечает, появляется вектор для разведки кластера. Пробуйте DNS-имена вместо IP: http://redis:6379/, http://db:3306/ — в Docker Compose и Kubernetes сервисы доступны по имени.

Это прямая реализация техники Network Service Discovery (T1046) — сервер выступает прокси для маппинга сети, к которой у вас нет прямого доступа.

SSRF и cloud metadata — главный приз в облачных тасках

Облачные провайдеры предоставляют metadata-сервис на предсказуемых адресах. Через эксплуатацию SSRF уязвимости можно добраться до временных IAM-кредов — это Cloud Instance Metadata API (T1552.005, Credential Access) в чистом виде.

AWS IMDS v1 — самый опасный сценарий:

curl -X POST http://target.ctf/api/preview \
  -d "url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"

Если SSRF работает — в ответе будет имя IAM-роли. Следующий шаг: запрос к http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name> возвращает AccessKeyId, SecretAccessKey и SessionToken без аутентификации. Просто GET — и вот ключи от облака.

По данным Unit 42 (Palo Alto Networks), исследование CVE-2019-8451 (SSRF в Jira, CVSS 6.5 MEDIUM, CWE-918 — Server-Side Request Forgery) показало масштаб проблемы: из более чем 7000 Jira-инстансов в публичных облаках 45% были уязвимы и не пропатчены, а 56% из уязвимых раскрывали облачные метаданные. Утечки включали сетевую конфигурацию, исходный код и учётные данные. EPSS для CVE-2019-8451 — 0.9445 (top 1%), экстремально высокая вероятность эксплуатации. Уязвимость работала без аутентификации: символ @ в параметре URL обходил валидацию JiraWhitelist, перенаправляя запрос на произвольный хост.

IMDSv2 требует предварительного PUT-запроса для получения session token, что серьёзно усложняет эксплуатацию через типовые SSRF-векторы, ограниченные GET-запросами. Но в CTF-тасках обычно моделируется IMDSv1 — задачник проверяет понимание вектора, а не знание конкретной версии API.

GCP требует заголовок Metadata-Flavor: Google, Azure — Metadata: true. Не каждый серверный HTTP-клиент позволяет установить произвольный заголовок через пользовательский URL — поэтому GCP и Azure менее уязвимы к простой SSRF. В CTF-тасках с GCP/Azure обычно дают дополнительный контроль над заголовками или используют HTTP-клиент без ограничений.

Полная цепочка эксплуатации в cloud-таске: SSRF → чтение /latest/meta-data/ → получение имени IAM-роли → запрос кредов → использование через aws s3 ls или aws s3 cp s3://bucket/flag.txt - для чтения бакета с флагом.

Обход фильтров SSRF: blacklist bypass техники для CTF

На medium/hard появляется фильтрация. Типичный подход — denylist: regex блокирует обращения к 127.0.0.1, 10.x.x.x, 192.168.x.x, 172.16-31.x.x и 169.254.169.254. Подход обречён — IP-адрес записывается десятками способов, и каждый нужно проверять отдельно.

Альтернативные представления IP-адресов для SSRF blacklist bypass

Один и тот же 127.0.0.1 можно записать так, что regex его не узнает:

Формат Запись для 127.0.0.1 Запись для 169.254.169.254
Decimal 2130706433 2852039166
Hex 0x7f000001 0xa9fea9fe
Octal 0177.0.0.1 0251.0376.0251.0376
IPv6 mapped [::ffff:127.0.0.1] [::ffff:169.254.169.254]
IPv6 loopback [::1] —
Short form http://0/ —
URL-encoded 127%2e0%2e0%2e1 169%2e254%2e169%2e254

По данным Elastic Security, их detection rule для SSRF к cloud metadata проверяет все эти варианты одновременно: decimal, hex, octal, IPv6-mapped, URL-encoded формы и даже AWS-specific IPv6 endpoint fd00:ec2::254. Если CTF-таск не проверяет все формы — одна из них пройдёт. На практике начинайте с decimal и hex — они чаще всего пропускаются blacklist'ами.

Отдельный трюк — домены, резолвящиеся в localhost. Сервис spoofed.burpcollaborator.net или собственный домен с A-записью 127.0.0.1 обходит фильтры, которые проверяют IP, но не делают DNS-резолв. В CTF это работает, когда фильтр проверяет строковое представление URL, а не результат резолва.

DNS rebinding и open redirect как обход WAF SSRF

Когда blacklist проверяет IP после DNS-резолва — помогает DNS rebinding. Механизм: атакующий контролирует DNS-запись, которая при первом запросе (момент проверки фильтром) резолвится в разрешённый IP, а при повторном запросе (момент реального fetch) — в 127.0.0.1. TTL записи выставляется в 0, чтобы DNS-кэш не мешал переключению.

Для CTF есть готовые сервисы: 7f000001.c0a80001.rbndr.us — ребайндит между 127.0.0.1 и 192.168.0.1. Подставляете такой домен в уязвимый параметр. При удачном timing'е фильтр увидит один IP, а реальный запрос уйдёт на другой. Техника не гарантирует срабатывание с первой попытки — иногда нужно отправить 5-10 запросов подряд. Не сработало с первого раза — не бросайте, просто повторите.

Open redirect — ещё один рабочий bypass. Если в приложении есть уязвимость открытого перенаправления (даже на совершенно другом эндпоинте), её можно использовать для обхода SSRF-фильтра:

  1. Фильтр проверяет домен в URL — видит разрешённый target.ctf
  2. Сервер делает запрос к http://target.ctf/redirect?to=http://127.0.0.1:6379/
  3. Сервер получает 302 и переходит по redirect на 127.0.0.1 — уже без повторной проверки

Многие HTTP-клиенты по умолчанию следуют за redirect'ами (follows redirect). В CTF open redirect + SSRF — частая комбинация двухступенчатого таска: сначала найти redirect, потом использовать его для bypass.

Ещё один приём — URL с базовой аутентификацией: http://allowed-domain@127.0.0.1/. Небезопасный парсер проверяет startsWith('allowed-domain'), но реальный запрос уходит на 127.0.0.1. Именно этот трюк использовался в CVE-2019-8451: символ @ в URL обходил JiraWhitelist, перенаправляя запрос на произвольный хост. Blacklist-подход к фильтрации SSRF payload'ов — гонка вооружений, которую защитник проигрывает.

SSRF payload через gopher:// — эскалация до RCE

Gopher — протокол, позволяющий отправить произвольные TCP-данные на указанный хост и порт. Формат: gopher://HOST:PORT/_<url-encoded-data> (символ _ после слэша — обязательный первый байт, который gopher-клиент отбрасывает). Если серверный HTTP-клиент поддерживает gopher:// — это прямой путь к RCE через внутренние сервисы.

Redis RCE. Redis по умолчанию слушает на 127.0.0.1:6379 без пароля. Через gopher отправляются Redis-команды напрямую: SET для записи PHP-шелла в переменную, CONFIG SET dir /var/www/html для выбора директории записи, CONFIG SET dbfilename shell.php для имени файла, SAVE для сброса базы на диск. Каждая команда URL-кодируется с %0d%0a (CRLF — разделитель Redis-протокола). Результат: веб-шелл на диске, доступный через HTTP. Для генерации payload'ов используют Gopherus (архивный инструмент, последние коммиты — 2020 год, но работает) или формируют вручную по RESP-протоколу Redis. Я предпочитаю руками — Gopherus иногда генерит payload с лишними переносами строк, и потом сидишь отлаживаешь чужой скрипт вместо своего payload'а.

FastCGI RCE. Аналогичный подход: gopher-payload к 127.0.0.1:9000 (PHP-FPM) формирует FastCGI-запрос, задающий PHP_VALUE с директивой auto_prepend_file или allow_url_include=1. Если PHP-FPM слушает на TCP без ограничения по IP — это RCE. В CTF порт 9000 на localhost — практически гарантия того, что ожидается gopher-payload к FastCGI.

SMTP abuse. Gopher-payload к 127.0.0.1:25 позволяет отправить email через внутренний SMTP-сервер. В CTF используется для отправки письма с данными на контролируемый адрес или для триггера серверного действия (password reset с контролируемым email).

Если фильтр блокирует gopher://, проверяйте альтернативные URI-схемы: dict:// (dictionary service — можно отправить одну строку на произвольный порт), file:/// (чтение локальных файлов, если поддерживается HTTP-клиентом), tftp://, ldap://. Серверный HTTP-клиент может поддерживать несколько схем — зависит от языка и библиотеки. Python urllib поддерживает file:// и ftp://, curl поддерживает практически всё включая gopher:// и dict://, Java HttpURLConnection — только http:// и https://. Знание стека — половина успеха: увидели в исходниках requests.get(user_url) (Python) — gopher:// не пройдёт, а file:// может.

SSRF в PDF-генераторах и headless-браузерах

Отдельная категория CTF-тасков — SSRF через PDF-генераторы и headless-браузеры (wkhtmltopdf, Puppeteer, WeasyPrint, Chrome headless). По данным YesWeHack, headless-браузеры принципиально отличаются от обычной SSRF: они исполняют JavaScript, поддерживают file:// протокол и имеют полный HTTP-стек с автоматическим следованием redirect'ов.

Если таск предлагает «сгенерировать PDF из HTML» или «сделать скриншот URL» — перед вами SSRF через headless-браузер. Базовый payload: тег <iframe src="file:///etc/passwd"> или <img src="http://127.0.0.1:8080/admin"> в HTML-шаблоне. Содержимое внутреннего ресурса отрендерится прямо в PDF. На одном CTF я потратил час, пытаясь прочитать /etc/passwd через обычную SSRF, а нужно было просто засунуть iframe в поле «описание профиля», которое рендерилось в PDF-отчёт.

Если JavaScript включён (Puppeteer, Chrome headless), возможности расширяются: через fetch() можно отправить POST-запрос с произвольными заголовками, а через XMLHttpRequest — прочитать ответ и вставить его в DOM для отображения в PDF. Это позволяет обходить ограничения, которые блокируют простые GET-запросы, и добавлять заголовки вроде Metadata-Flavor: Google для доступа к GCP metadata.

Fingerprinting стека: в HTTP-заголовках ответа ищите X-Powered-By, Server, в исходном коде — зависимости (wkhtmltopdf, puppeteer, playwright). Разные генераторы поддерживают разные URI-схемы: wkhtmltopdf обычно поддерживает file://, WeasyPrint — нет. Знание конкретного стека определяет набор рабочих payload'ов.

Цепочка SSRF атак: от подделки запроса до компрометации

SSRF — не финальная цель, а трамплин. В CTF-тасках уровня hard и в реальных пентестах цепочка выглядит так:

SSRF → Shellshock → RCE. Через SSRF отправляется запрос к внутреннему CGI-сервису, а в заголовке User-Agent передаётся Shellshock-payload: () { :; }; /bin/command. CVE-2014-6271 (CVSS 9.8 CRITICAL, CWE-78 — OS Command Injection) позволяет выполнить произвольный код через переменные окружения в GNU Bash до версии 4.3. EPSS — 1.0000, максимум шкалы. CVE в каталоге CISA KEV (Known Exploited Vulnerabilities) с 2022 года, подтверждённо эксплуатируется in the wild. Звучит как история из 2014-го, но на внутренних сервисах без обновлений — встречается до сих пор. В CTF Shellshock + blind SSRF — классическая комбинация: результат выполнения команды уходит через DNS-exfiltration на контролируемый домен.

SSRF → Cloud Credentials → S3 → Flag. Цепочка для cloud-тасков: через SSRF читаем metadata endpoint, получаем IAM-креды (T1552.005), используем их для доступа к S3/DynamoDB/Lambda (Credentials In Files, T1552.001), находим флаг в бакете или базе. По данным CrowdStrike, рост cloud intrusion случаев составил +26% год к году, и SSRF — один из основных initial access векторов.

SSRF → Internal Admin Panel → Flag. Простейшая цепочка: через SSRF обращаемся к http://127.0.0.1:8080/admin, получаем доступ к панели управления без аутентификации (внутренний сервис доверяет localhost) и читаем флаг. По данным Unit 42, контейнеризированные приложения (Docker, Kubernetes, ECS, EKS) по умолчанию имеют доступ к metadata API хоста — SSRF из контейнера создаёт путь «побега» к метаданным хост-машины.

Детектирование SSRF со стороны blue team. Для полноты картины: Elastic Security выпустила detection rule «Web Server Cloud Metadata SSRF Request», которая ловит HTTP-запросы с URL или query string, содержащими адреса cloud metadata (все форматы: decimal, hex, octal, IPv6-mapped, URL-encoded). Правило привязано к тактикам Credential Access (T1552.005) и Initial Access (T1190). В SigmaHQ по тегу T1190 доступно 152 правила обнаружения, а по T1046 (Network Service Discovery) — 22 правила, включая детекцию port scan через firewall-логи. Понимание того, как SSRF детектируется, помогает в CTF-тасках, где задачник добавляет WAF или IDS, которые нужно обойти.

SSRF уязвимость — это точка входа. Она ценна не сама по себе, а как первый шаг цепочки. Обнаружили blind SSRF — ищите внутренние сервисы без аутентификации (Redis, Elasticsearch, admin-панели). Нашли classic SSRF — проверяйте cloud metadata. Упёрлись в blacklist — применяйте DNS rebinding, open redirect, альтернативные IP-представления. Добрались до внутреннего сервиса — эскалируйте через gopher или протоколо-специфичные payload'ы.

Разница между «нашёл SSRF» и «взял флаг» — в умении выстроить цепочку. Каждый шаг по отдельности несложен. Сложность — в комбинации: понять, какой bypass применить к конкретному фильтру, какой внутренний сервис атаковать и какой протокол использовать для эскалации. По опыту — основная ошибка не в незнании экзотических bypass-техник, а в неполном маппинге внутренней сети после первоначальной SSRF. Многие останавливаются, получив ответ от 127.0.0.1:80, и не проверяют другие порты, не сканируют приватные подсети, не пробуют gopher к Redis. В итоге — полчаса на подтверждение SSRF и три часа на «что делать дальше». Практика решает: чем больше тасков с разными комбинациями (SSRF + XXE, SSRF + open redirect, blind SSRF + gopher + Redis), тем быстрее выстраивается kill chain. Если хочешь не просто читать writeup'ы, а пройти всю атаку от recon до RCE самому — на WAPT эту цепочку проходят в течение двух модулей с лабами.

🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».

Поделиться

0 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...

Читайте также

Стеганография CTF: от файла до флага

13 мин.

8

Стеганография CTF: от файла до флага

Полный алгоритм решения стего-тасков: binwalk, steghide, zsteg, exiftool. Таблица совместимости форматов, decision tree и 8 ошибок, которые стоят флагов.

3 ОКТЯБРЬ, 2026

XSS CTF: разбор задания от инъекции до кражи cookie

12 мин.

16

XSS CTF: разбор задания от инъекции до кражи cookie

Пошаговый разбор XSS задания в CTF: поиск точки инъекции через DevTools, сборка cookie stealer payload через fetch, обход фильтров и session hijacking через Burp.

2 ОКТЯБРЬ, 2026

Linux для CTF: команды, права и поиск флагов

15 мин.

8

Linux для CTF: команды, права и поиск флагов

Пошаговый гайд по Linux для CTF: bash-команды, чтение прав chmod, поиск флагов через find и grep, первый privesc через sudo -l и SUID-бинарники. С чеклистом.

2 ОКТЯБРЬ, 2026