Перейти к содержимому
← Каталог golang SSRF

SSRF Redirect: Обход валидации через HTTP-редирект

SSRF-уязвимость: валидация URL обходится через HTTP-редирект на внутренний адрес.

mediumgolangPro
Задача
# SSRF: обход валидации IP через цепочку HTTP-редиректов ## Сценарий Вы аудируете внутренний сервис **SecurePreview**, в котором есть эндпоинт «предпросмотра ссылки»: пользователь передаёт URL, сервер скачивает страницу и возвращает её содержимое. Команда уверена, что эндпоинт безопасен — реализована проверка IP назначения по приватным диапазонам, перед запросом резолвится DNS и блокируются loopback/private адреса. По данным внутреннего аудита, в приложении сохранился административный API с конфиденциальными данными, доступный только при обращении с loopback-интерфейса; в его ответе раскрывается флаг. В фильтре также есть «удобство для разработки» — список доверенных внутренних hostname'ов, которые пропускаются без IP-проверки. Типичный enterprise-паттерн: внутренние сервисы (`prometheus.internal`, `auth.local`, `lab-redirector.lab-infra.svc.cluster.local`) в allowlist'е, чтобы не плодить DNS-исключений. Если такой trusted-сервис умеет редиректить на произвольный URL (хоть как partner-tracking endpoint, хоть как helper для тестов), вся защита от SSRF разваливается. ## Цель 1. Подтвердите, что прямое обращение к внутреннему API через `url-preview` корректно блокируется проверкой IP. 2. Найдите в инфраструктуре сервис, чей hostname добавлен в allowlist фильтра, и который умеет менять адрес назначения **после** первоначальной валидации. 3. Скомбинируйте обнаруженные эндпоинты так, чтобы серверный HTTP-клиент в итоге обратился к внутреннему API и вернул его ответ. Извлеките флаг. ## Теория Защита от SSRF, основанная на однократной валидации URL **до** запроса, имеет фундаментальный TOCTOU-разрыв (Time-Of-Check vs Time-Of-Use): между моментом проверки и фактическим завершением HTTP-обмена существуют события, способные сменить адрес назначения. Самая частая причина — поведение HTTP-клиента по умолчанию: при получении ответа `3xx` с заголовком `Location` клиент автоматически следует по новому адресу, не повторяя валидацию. **Уязвимый паттерн:** ```go // Проверка IP только для начального URL — клиент следует редиректам без переvalidation checkPrivateIP(initialURL) // ok, public IP client := &http.Client{} // дефолтные настройки следуют 3xx-редиректам resp, _ := client.Get(initialURL) ``` Атака усиливается, если у фильтра есть allowlist «доверенных» сервисов, чья IP-проверка вообще пропускается, и хотя бы один из них поддерживает open-redirect (`/r?target=`, `/go?url=`, `/redirect?to=`). Тогда атакующему достаточно: подать в защищённый эндпоинт URL такого trusted-сервиса с параметром редиректа на внутренний адрес → внутренний редирект перенаправит клиент на loopback → серверный HTTP-клиент молча перейдёт по редиректу и вернёт ответ внутреннего API, к которому валидация изначально не пускала. ## Точка входа атаки | Параметр | Значение | |----------|----------| | Учётные данные | `demo` / `demo` | | Защищённый эндпоинт предпросмотра | `GET /url-preview?url=<URL>` (валидирует IP до запроса; trusted hostnames пропускаются без проверки) | | Кандидат для редирект-цепочки | внутренний trusted-сервис, чей hostname в allowlist'е и который умеет отвечать 302 на параметр `target` | | Цель | внутренний административный API с конфиденциальной меткой |
🚧 Сайт в разработке. Полный функционал пока недоступен. Все вопросы — support@hackandfix.ru