На пентесте финтех-приложения в AWS PDF-генератор принял
http://169.254.169.254/latest/meta-data/iam/security-credentials/ как URL документа. Три GET-запроса - и в Burp отобразился JSON с AccessKeyId, SecretAccessKey и SessionToken IAM-роли, у которой были права на S3 и DynamoDB. Одиннадцать минут от обнаружения SSRF до рабочего aws s3 ls. Это не экзотика: по данным F5 Labs, в марте 2025 года зафиксирована целенаправленная кампания по массовой эксплуатации EC2 Instance Metadata через SSRF - атакующие перебирали шесть вариантов параметров (dest, file, redirect, target, uri, url) в сочетании с четырьмя путями к metadata endpoint. SSRF сидит на позиции A10 в OWASP Top 10 2021, но в облаке последствия на порядки серьёзнее, чем чтение /etc/passwd: это прямой path к credential theft и lateral movement по всему аккаунту.Место в kill chain: почему SSRF в облаке - не про локальные файлы
На bare-metal сервере SSRF чаще всего заканчивается сканированием внутренних портов или чтением конфигов. В облаке та же уязвимость даёт credentials, которые открывают доступ ко всей подписке. Разница - как между кражей ключа от подсобки и кражей мастер-карты от всего здания. Полная цепочка по MITRE ATT&CK:- Exploit Public-Facing Application (T1190, Initial Access) - SSRF в веб-приложении: PDF-генератор, webhook validator, image proxy, import-from-URL, open redirect с серверным fetch
- Cloud Instance Metadata API (T1552.005, Credential Access) - через SSRF запрашиваем
http://169.254.169.254и получаем временные credentials IAM-роли, Service Account или Managed Identity - Cloud Accounts (T1078.004, Privilege Escalation / Persistence) - аутентифицируемся в AWS/GCP/Azure API с украденными credentials
- Cloud Service Discovery (T1526) и Cloud Infrastructure Discovery (T1580) - перечисляем ресурсы: S3-бакеты, Lambda-функции, EC2-инстансы, VPC-конфигурации
- Data from Cloud Storage (T1530, Collection) - выгружаем данные или используем credentials для дальнейшего lateral movement
- Exfiltration Over C2 Channel (T1041, Exfiltration) - передача собранных данных на инфраструктуру атакующего
aws s3 ls.Контекст применимости: цепочка работает для любого EC2/GCE/Azure VM, где приложению с SSRF назначена IAM-роль (AWS), Service Account (GCP) или Managed Identity (Azure). Для контейнерных сред (ECS, EKS, GKE) endpoints другие - подробности ниже.
IMDSv1: три запроса от SSRF до рабочих credentials
Требования к окружению
- ОС: любая с Burp Suite или curl
- Инструменты: Burp Suite Community/Pro, curl 7.x+, AWS CLI v2 (для постэксплуатации), опционально
enumerate-iam(Python 3.8+) - Сетевые условия: доступ к целевому веб-приложению; internet для работы с AWS API после получения credentials
- Права: легитимный скоуп (bug bounty программа, договор на пентест)
AWS - endpoint без аутентификации
Instance Metadata Service v1 не требует ни токенов, ни заголовков. Любой HTTP GET с инстанса кhttp://169.254.169.254 возвращает данные. Этот link-local адрес недоступен из интернета, но SSRF делает запрос от имени сервера - то есть изнутри. По сути, приложение само ходит за своими же credentials и отдаёт их нам.Эксплуатация укладывается в три шага:
Шаг 1. Подставляем в уязвимый параметр URL
http://169.254.169.254/latest/meta-data/iam/security-credentials/. Если SSRF reflected - видим имя IAM-роли, привязанной к инстансу (например, webapp-prod-role).Шаг 2. Запрашиваем credentials конкретной роли:
http://169.254.169.254/latest/meta-data/iam/security-credentials/webapp-prod-role. Ответ - JSON с AccessKeyId, SecretAccessKey, Token и Expiration.Шаг 3. Проверяем credentials:
Bash:
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=wJalr...
export AWS_SESSION_TOKEN=IQoJb3...
aws sts get-caller-identity
arn:aws:sts::123456789012:assumed-role/webapp-prod-role/i-0abc... - credentials рабочие, можно переходить к постэксплуатации.User-data как бонус. Endpoint
http://169.254.169.254/latest/user-data часто содержит скрипты инициализации инстанса. По данным appsecure.security, разработчики встраивают пароли баз данных, API-ключи и конфигурационные секреты прямо в user-data «для удобства при запуске». Удобно, ничего не скажешь - и нам тоже удобно. Этот канал утечки существует параллельно с IAM credentials.ECS-контейнеры - другой endpoint. В ECS credentials доступны не через
169.254.169.254, а через http://169.254.170.2 по пути из переменной окружения AWS_CONTAINER_CREDENTIALS_RELATIVE_URI. Если SSRF позволяет читать /proc/self/environ (через file:// или path traversal), полный URL собирается из переменной.GCP и Azure - заголовки как единственный барьер
GCP и Azure добавили обязательные заголовки к metadata-запросам. Это усложняет эксплуатацию через стандартный SSRF, но не закрывает её полностью.Google Cloud Platform требует
Metadata-Flavor: Google в каждом запросе к http://metadata.google.internal/computeMetadata/v1/. Без заголовка - 403. Для получения OAuth-токена Service Account целевой URL: http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token. Эксплуатация возможна, если SSRF позволяет контролировать заголовки (HTTP-клиент на стороне сервера это умеет) или через CRLF-injection в URL-параметре для инъекции Metadata-Flavor: Google в запрос.Azure использует тот же
169.254.169.254, но требует заголовок Metadata: true и параметр api-version. Managed Identity token запрашивается через http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/. По данным appsecure.security, webhook-обработчики в Azure-приложениях часто поддерживают POST с кастомными заголовками - что делает эксплуатацию вполне реалистичной.| Облако | Endpoint | Обязательный заголовок | Эксплуатация через базовый SSRF |
|---|---|---|---|
| AWS IMDSv1 | 169.254.169.254 | Нет | Да, один GET |
| AWS IMDSv2 | 169.254.169.254 | X-aws-ec2-metadata-token | Нет без контроля метода и заголовков |
| GCP | metadata.google.internal | Metadata-Flavor: Google | Нет без контроля заголовков |
| Azure | 169.254.169.254 | Metadata: true | Нет без контроля заголовков |
Обход IMDSv2: когда PUT-запрос не помогает
AWS ввёл IMDSv2 как прямой ответ на массовую эксплуатацию metadata через SSRF. Механизм сессионный: сначала PUT-запрос наhttp://169.254.169.254/latest/api/token с заголовком X-aws-ec2-metadata-token-ttl-seconds: 21600 возвращает токен, затем каждый GET к metadata должен содержать этот токен в X-aws-ec2-metadata-token.F5 Labs пишут, что IMDSv2 «fully mitigates exposure of EC2 Metadata via SSRF as SSRF vulnerabilities do not generally expose the ability to specify headers». Для типичного SSRF - да. Но «типичный» и «все» - разные вещи.
Когда IMDSv2 не блокирует атаку
IMDSv1 fallback включён - самый частый случай на практике. IMDSv2 обратно совместим. Если параметрHttpTokens выставлен в optional (а не required), обе версии работают одновременно. Атакующий делает обычный GET без токена - и получает ответ по IMDSv1. На cloud-пентестах я регулярно вижу HttpTokens: optional на production-инстансах, потому что legacy-скрипты, обращающиеся к metadata без токена, ломаются после enforce. AWS с 2024 года применяет IMDSv2 required по умолчанию для новых AMI quick-start и новых launch templates через console, но custom AMI и существующие конфигурации остаются с HttpTokens: optional до явного изменения. Jira-тикет создан полгода назад, а воз и ныне там.SSRF с контролем HTTP-метода и заголовков. Если приложение использует HTTP-клиент с поддержкой произвольных методов (PUT) и кастомных заголовков - IMDSv2 обходится в два запроса вместо одного. Также gopher-протокол позволяет сформировать произвольный TCP-пакет с PUT-запросом и заголовком
X-aws-ec2-metadata-token-ttl-seconds - при условии, что SSRF-уязвимость поддерживает схему gopher://.Redirect chain при включённом fallback. IMDSv2 по умолчанию ставит hop limit = 1 для IP TTL ответов от metadata service (блокирует доступ из контейнеров за дополнительным network namespace), поэтому получить токен через redirect нельзя. Но если IMDSv1 не отключён - redirect на
http://169.254.169.254/latest/meta-data/... выполнит обычный GET, который IMDSv1 обработает без вопросов. Классический сценарий: фильтр SSRF проверяет только исходный URL (разрешённый внешний домен), а не конечный пункт назначения.Выбор вектора: decision tree
| Условие | Вектор | Сложность |
|---|---|---|
| IMDSv1 включён, reflected SSRF | Прямой GET к 169.254.169.254 | Низкая |
| IMDSv1 включён, blind SSRF | OOB через Burp Collaborator + DNS/HTTP exfiltration | Средняя |
| Только IMDSv2, базовый SSRF без контроля заголовков | Не эксплуатируется (если IMDSv1 полностью отключён) | - |
| Только IMDSv2, SSRF с контролем заголовков | PUT за токеном, затем GET с токеном | Средняя |
| Только IMDSv2, поддержка gopher:// в SSRF | Gopher payload с PUT-запросом | Высокая |
| GCP/Azure, SSRF без контроля заголовков | CRLF injection или не эксплуатируется | Высокая |
| GCP/Azure, SSRF с контролем заголовков | GET с нужным заголовком | Средняя |
Ограничения: gopher-payload для IMDSv2 bypass требует, чтобы HTTP-клиент приложения поддерживал схему
gopher://. Современные библиотеки (requests в Python, axios в Node.js) gopher не поддерживают - это про legacy-стеки с curl-подобными клиентами или PHP с allow_url_fopen. Если видите modern-стек - gopher можно не пробовать.Постэксплуатация: от украденного токена до exfiltration
Получить credentials - половина дела. Дальше нужно оценить привилегии и действовать быстро: временные токены AWS живут от 1 до 12 часов. Часы тикают.
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Что ловит облачный мониторинг и где промахивается
Кампания марта 2025: данные F5 Labs
Масштаб проблемы хорошо виден на кампании, зафиксированной F5 Labs в марте 2025 года. Активность началась 13 марта с разведки одним IP (193.41.206.72). Основная фаза - с 15 по 25 марта, несколько IP из ASN 34534 (FBW NETWORKS SAS, Франция/Румыния). Хосты имели идентичную конфигурацию: OpenSSH_9.2p1 Debian, порт 10250 (Kubernetes), TLS-сертификаты с Subject Alternative NamesDNS:cold01 и DNS:cold07 - общая инфраструктура одного актора.Запросы шли в формате
/?parameter=http://169.254.169.254/latest/subpath с шестью вариантами parameter (dest, file, redirect, target, uri, url) и четырьмя subpath. F5 Labs подтвердил, что вариации выбирались с uniform randomness - кампания полностью автоматизирована. Не таргетированная атака: сканировали всё, что hosted на EC2, надеясь наткнуться на SSRF-уязвимый endpoint. Ковровая бомбардировка.Подход Wiz: аномалии IMDS-доступа
Исследователи Wiz описали метод обнаружения IMDS-злоупотреблений через анализ аномалий на уровне процессов. Логика простая: если процесс, который в 98% сред не обращается к metadata endpoint, вдруг запрашивает/latest/meta-data/iam/security-credentials/ - это индикатор компрометации. Этот подход привёл к обнаружению CVE-2025-51591 - SSRF в Pandoc (CVSS 3.7, LOW по NVD, CWE-918). Сам CVSS низкий, потому что оценивает уязвимость в изоляции. Но в облаке даже Low-severity SSRF через <iframe> в HTML-документе достаточен для кражи IAM credentials, если конвертация запускается на EC2 с привязанной ролью. CVSS смотрит на уязвимость в вакууме - а мы смотрим на цепочку.Слепые зоны WAF и мониторинга
WAF-правила на строку169.254.169.254 ловят прямые запросы, но пропускают альтернативные представления: decimal http://2852039166/, hex IP http://0xA9FEA9FE/, octal http://0251.0376.0251.0376/, IPv6-mapped http://[::ffff:a9fe:a9fe]/. DNS rebinding обходит WAF целиком: первый DNS-ответ возвращает легитимный IP (проверка проходит), второй - 169.254.169.254 (запрос уходит на metadata). Красивый фантик - а внутри тот же 169.254.169.254.IMDSv2 enforcement - технически самая эффективная мера. Но на каждом втором cloud-пентесте картина одна: команда знает про IMDSv2, документация изучена, тикет в Jira создан полгода назад, а
HttpTokens до сих пор optional. Причина всегда организационная, не техническая: кто-то боится, что старый скрипт сломается. И пока этот скрипт не проверен и не переписан - инстанс остаётся с IMDSv1.GuardDuty детектит credential exfiltration по source IP, но если атакующий использует credentials изнутри AWS (через свой инстанс в том же регионе), алерт
InstanceCredentialExfiltration.OutsideAWS не сработает. AWS добавил InstanceCredentialExfiltration.InsideAWS - но с ограниченными условиями срабатывания.Перейти с IMDSv1 на IMDSv2 - одна команда:
aws ec2 modify-instance-metadata-options --instance-id i-xxx --http-tokens required. Одна. По данным SonicWall (2025 Cyber Threat Report), рост числа SSRF-атак между 2023 и 2024 годами составил 452% - и этот рост коррелирует с доступностью инструментов автоматизации, которые сканируют endpoint'ы массово, как показала кампания из ASN 34534. SSRF в облаке перестанет быть настолько разрушительным не когда WAF научится ловить каждый encoding-трюк, а когда enforce IMDSv2 станет таким же дефолтом, как TLS на 443. AWS движется в эту сторону, но legacy будет жить ещё годы. И пока оно живёт - три GET-запроса к 169.254.169.254 остаются одним из самых коротких путей от web-бага до полного компромата облачного аккаунта.Проверьте свои инстансы:
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens]' --output table. Если в колонке HttpTokens хоть одно optional - у вас та же проблема, что и у того финтех-приложения. Только пока без PDF-генератора в цепочке.