SSRF Cloud Metadata: Доступ к метаданным облака
SSRF-уязвимость: неполная фильтрация IP позволяет обратиться к link-local адресу облачных метаданных (169.254.169.254).
mediumgolangPro
Задача
# SSRF: доступ к облачному metadata-сервису через неполную фильтрацию IP
## Сценарий
Вы аудируете внутренний сервис **SecurePreview**, развёрнутый в облаке (формат развёртывания эмулирует AWS EC2 / GCP / Azure). У сервиса есть эндпоинт «предпросмотра ссылки»: пользователь передаёт URL, сервер скачивает страницу и возвращает её содержимое. Команда добавила «защиту» от SSRF — блокировку обращений к loopback-адресам. По данным внутреннего аудита, в кластере выполняется агент облачной инфраструктуры, отдающий конфиденциальные настройки и токены IAM по специальному внутреннему адресу; в его ответе содержится конфиденциальная метка, которую требуется извлечь.
## Цель
1. Подтвердите, что прямое обращение к loopback-адресам через эндпоинт предпросмотра действительно блокируется.
2. Найдите класс «внутренних» адресов, который не покрывается существующей фильтрацией, но используется облачной инфраструктурой для предоставления метаданных инстанса.
3. Используя серверный HTTP-клиент как прокси, обратитесь к этому диапазону и извлеките флаг из ответа.
## Теория
В облачных средах (AWS, GCP, Azure, DigitalOcean) каждый запущенный инстанс имеет доступ к **Instance Metadata Service** (IMDS) по фиксированному link-local адресу. Сервис возвращает чувствительные данные: IAM-токены, ключи доступа, переменные окружения, сетевую конфигурацию, user-data (часто содержит скрипты с паролями). IMDS не требует аутентификации — единственная защита заключается в том, что обратиться к нему могут только процессы, выполняющиеся внутри самого инстанса. SSRF-уязвимость превращает прокси-эндпоинт приложения в proxy для metadata-сервиса, минуя эту защиту.
**Уязвимый паттерн:**
```go
// Защита покрывает только часть «внутренних» адресов
if ip.IsLoopback() {
http.Error(w, "Forbidden", 403); return
}
// 169.254.169.254 (link-local) не блокируется — IsLoopback() возвращает false
http.Get(targetURL)
```
Категории «внутренних» адресов, которые нужно блокировать в SSRF-фильтре: loopback (`127.0.0.0/8`, `::1`), приватные сети RFC1918 (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`), link-local unicast (`169.254.0.0/16`, `fe80::/10`), link-local multicast, unspecified (`0.0.0.0`, `::`), unique local IPv6 (`fc00::/7`). Многие реализации фильтров покрывают только loopback или только RFC1918 — link-local часто остаётся «забытым» классом, что и эксплуатирует metadata SSRF.
## Точка входа атаки
| Параметр | Значение |
|----------|----------|
| Учётные данные | `demo` / `demo` |
| Эндпоинт предпросмотра | `GET /url-preview?url=<URL>` |
| Цель | metadata-эндпоинт облачной инфраструктуры (или его эмуляция в этой лабе на собственном сервере приложения) |