Типовая облачная инфраструктура сегодня - это набор сервисов: API-gateway, backend-приложение, Redis или Memcached, очередь сообщений, контейнеры в Kubernetes, плюс облачная metadata-служба. Между ними - внутренние IP, trust boundaries условные, аутентификация не везде, потому что “это же внутри VPC”. И как только появляется SSRF, вся эта внутренняя сетка становится достижимой через HTTP-параметр.
SSRF в 2026 году - это не просто запрос к 127.0.0.1. Это:
- доступ к cloud metadata endpoint;
- возможность сканировать внутренние сервисы;
- взаимодействие с Redis или Elasticsearch без авторизации;
- обход firewall’ов через сам сервер приложения.
Целевая инфраструктура: как это обычно устроено
Представим типичную микросервисную среду в облаке. Frontend принимает пользовательские URL, backend умеет забирать внешние ресурсы для обработки. Это уже потенциальная точка SSRF. Backend работает внутри VPC, имеет доступ к Redis на 6379 порте, к metadata-сервису облака и к другим внутренним API.Redis поднят без аутентификации, потому что он только внутри сети. Metadata endpoint доступен с каждого инстанса. Контейнеры крутятся с ролью, у которой есть IAM-права на чтение из S3 или доступ к очереди.
И вот здесь цепочка складывается. SSRF → внутренняя сеть → Redis → запись полезной нагрузки → RCE внутри контейнера → использование облачных credentials → lateral movement.
Attack surface analysis
SSRF чаще всего прячется в функционале:- preview URL;
- webhook validation;
- import-from-URL;
- image proxy;
- PDF generator;
- open redirect с последующим fetch.
Дальше вопрос в том, какие исходящие соединения разрешены. Если сервер может ходить внутрь VPC, значит через него можно ходить и нам. Это не гипотеза, это архитектурный факт.
Обнаружение SSRF: где начинается цепочка
SSRF редко выглядит как что-то очевидное. Почти никогда это не endpoint с названием fetch_internal. Обычно это легитимный функционал: загрузка изображения по URL, проверка webhook, генерация отчёта из внешнего ресурса. И именно поэтому его находят не сканером, а вниманием.Первый шаг - найти места, где приложение делает серверный HTTP-запрос по пользовательскому вводу. Это может быть query-параметр, JSON-поле, заголовок. Иногда URL передаётся не напрямую, а внутри структуры, закодированный или обёрнутый в объект. Любой кусок данных, который выглядит как адрес, - кандидат.
URL parameter fuzzing
Работа начинается с простого. Подставляем контролируемый URL и смотрим, что происходит. Если приложение действительно делает запрос, мы увидим задержку, изменение ответа или попытку соединения.Пример базовой проверки:
HTTP:
GET /preview?url=http://attacker-domain.com/test HTTP/1.1
Host: target.com
Дальше начинается аккуратный fuzzing. Меняем схему - http, https, иногда ftp. Пробуем IP вместо домена. Проверяем реакцию на 127.0.0.1, на приватные диапазоны. Некоторые фильтры режут localhost по строке, но не понимают альтернативные формы записи. Например:
- 127.1
- 2130706433
- 0x7f000001
Иногда SSRF не даёт прямого ответа. Запрос выполняется, но результат не возвращается пользователю. В таких случаях уже нужен out-of-band подход.
Out-of-band detection
Если ответ не отражается, проверяем через callback. Поднимается контролируемый endpoint или используется Burp Collaborator. Смысл в том, чтобы поймать факт соединения. Вставляем уникальный поддомен в параметр URL и отслеживаем DNS или HTTP-запрос на своей стороне. Даже если приложение возвращает 500 или пустой ответ, соединение может происходить. Это особенно полезно, когда SSRF асинхронный. Например, webhook validation или фоновые задачи. Пользователь не видит результата, но сервер всё равно ходит по указанному адресу.Когда callback подтверждён, дальше уже начинается интереснее. Нужно понять:
- выполняется ли redirect;
- разрешены ли нестандартные схемы;
- есть ли ограничения на порт;
- какие заголовки добавляет сервер.
SSRF может быть прямым, может быть слепым, может работать только через POST или только через JSON. Иногда он завязан на специфичный формат - например, URL должен заканчиваться на .jpg или быть в белом списке доменов. Это не конец, это просто дополнительный слой, который придётся обойти.
Когда факт SSRF установлен, мы получаем контролируемый канал из доверенной зоны. И дальше начинается вторая фаза - обход фильтров и выход к внутренней сети. Именно там обычно лежит Redis, metadata endpoint и всё остальное, что превращает веб-баг в облачный инцидент.
Слепые сценарии SSRF, где прямой вывод данных недоступен, могут потребовать измерения задержек и time-based подходов - детальный разбор таких техник с примерами триггеров можно найти в материале по SSRF с временными триггерами.
Bypass фильтров: когда простого localhost уже недостаточно
Если SSRF находится за один вечер, это редкость. Обычно к нему прилагается фильтр. Иногда примитивный, иногда довольно аккуратный. Чёрный список localhost, запрет на приватные диапазоны, белый список доменов, проверка схемы. Всё это выглядит разумно, пока не начинаешь проверять реализацию.SSRF почти никогда не упирается в сам протокол. Он упирается в парсинг URL и в то, как приложение решает, что адрес безопасен.
DNS rebinding
Классика, которая до сих пор работает там, где разработчик проверяет домен только один раз - до запроса. Логика простая. Приложение резолвит attacker.com, получает внешний IP, проверка проходит. Потом при фактическом соединении DNS возвращает уже внутренний адрес. И backend уходит внутрь сети.Рабочая схема требует контроля над DNS и таймингами, но в микросервисной среде это не экзотика. Если сервер кэширует DNS коротко или вообще не кэширует, rebinding становится стабильным инструментом.
Фильтры, которые проверяют только строку host, rebinding не ловят. Они видят attacker.com и считают, что всё в порядке. А реальный IP уже не совпадает с первоначальной проверкой.
Redirect chains и parser confusion
Многие приложения разрешают внешние домены, но запрещают внутренние IP. При этом они следуют HTTP-редиректам. Это открывает довольно прямой вектор: первый запрос идёт на разрешённый домен, который возвращает 302 на внутренний адрес.Если фильтр проверяет только исходный URL, а не каждый шаг редиректа, SSRF уходит внутрь.
Есть ещё более интересный слой - разница между тем, как URL парсится в коде проверки и как он используется в HTTP-клиенте. Разные библиотеки обрабатывают специальные символы по-разному. Примеры с @, #, encoded-символами или двойной схемой иногда приводят к тому, что фильтр видит один хост, а клиент подключается к другому.
Типичная конструкция для тестирования выглядит так:
HTTP:
http://allowed-domain.com@127.0.0.1:6379/
Ещё один слой - mixed scheme parsing. Некоторые фильтры проверяют строку на наличие http или https и не анализируют вложенные схемы. При поддержке альтернативных протоколов это может сыграть позже, особенно когда дойдём до gopher.
IPv6 и альтернативные представления
Чёрные списки приватных IPv4-диапазонов - частый случай. Но далеко не все фильтры учитывают IPv6. Если внутренняя инфраструктура слушает на ::1 или на link-local адресах, их можно использовать.Кроме того, IP можно представить в разных форматах. Десятичный, восьмеричный, шестнадцатеричный. Некоторые парсеры нормально приводят их к канонической форме, некоторые - нет. И если проверка и фактическое соединение используют разные механизмы нормализации, фильтр начинает вести себя странно.
Вся эта возня не про трюки ради трюков. Она про то, чтобы превратить SSRF из условного доступа к внешнему URL в полноценный внутренний прокси. Когда фильтры обойдены, приложение начинает ходить туда, куда из интернета не достучаться.
И вот в этот момент начинается реальная разведка внутренней сети. Нужно понять, какие сервисы доступны из контекста приложения. Redis на 6379, Memcached на 11211, Elasticsearch на 9200 - это уже не теория, это конкретные цели.
В этой же плоскости исследования стойкости SSRF полезно опираться на разбор актуальных техник обхода фильтров, где показано, как DNS-ребайндинг, IPv6 и URL-парсинг могут расширить возможности атаки в 2026-м. Подробнее в статье: "SSRF в 2026: новые техники эксплуатации и bypass фильтров"
Internal Network Enumeration: когда веб-приложение становится сканером
Как только SSRF стабильно ходит внутрь, задача меняется. Нам уже не важно, что возвращает фронтенд. Нам важно, какие сервисы доступны из контекста приложения. Это тонкий момент. Мы не внутри VPC напрямую, мы смотрим на сеть глазами backend’а. И его сетевые права определяют границы атаки.Port scanning через SSRF
Классический nmap тут не работает. Но HTTP-клиент на сервере уже делает TCP-соединения. И по реакции можно понять, открыт порт или нет.Базовая идея простая: меняем порт в URL и смотрим поведение. Если соединение установилось, но сервис не HTTP - приложение может вернуть ошибку уровня протокола. Если порт закрыт - чаще всего будет timeout или connection refused. Разница в таймингах и типе ошибки даёт сигнал.
Пример запроса для проверки Redis:
HTTP:
GET /fetch?url=http://127.0.0.1:6379/ HTTP/1.1
Host: target.com
Так же проверяются 11211, 9200, 8080, 8000 и любые внутренние сервисы, которые могут быть интересны. Главное - фиксировать поведение приложения: разница в статус-кодах, время ответа, текст ошибки. Это и есть наш side-channel.
Если SSRF слепой, без отражения ответа, тогда используется out-of-band подход. Можно направлять запросы на внутренний хост с редиректом на контролируемый сервер и отслеживать попытки соединения. Это сложнее, но при стабильной SSRF вполне реально. Сканирование должно быть аккуратным. Массовые переборы портов могут вызвать аномалии в логах или нагрузку на сервисы. Лучше двигаться точечно - по типичным портам и предполагаемым целям.
Cloud Metadata Exploitation: когда SSRF выходит за пределы приложения
Как только SSRF подтверждён и фильтры обойдены, metadata endpoint становится одной из первых целей. Потому что это не просто сервис. Это точка, где облако хранит временные креды, роли и контекст инстанса. И если backend может туда ходить - мы можем ходить через него.Metadata-сервис живёт на link-local адресе. В AWS это 169.254.169.254, в GCP - тот же диапазон, в Yandex Cloud аналогичная схема. Из интернета туда не достучаться. Но изнутри инстанса - без проблем. И SSRF как раз даёт такой внутренний взгляд.
AWS IMDS v1 vs v2
В AWS долгое время использовался IMDSv1. Он позволял получать данные простым HTTP GET без дополнительной аутентификации. Если SSRF позволял отправить запрос к metadata, можно было получить IAM role credentials (учетные данные роли AWS IAM) в несколько шагов.Пример запроса через SSRF выглядел бы так:
HTTP:
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ HTTP/1.1
Host: target.com
IMDSv2 усложнил сценарий. Появился токен, который нужно сначала получить через PUT-запрос с заголовком X-aws-ec2-metadata-token-ttl-seconds. Потом этот токен используется в последующих запросах. Казалось бы, SSRF становится бесполезным.
Но многое зависит от типа SSRF. Если уязвимость позволяет контролировать метод и заголовки, IMDSv2 тоже становится достижимым. Если только GET без кастомных заголовков - уже сложнее. Тогда приходится искать обходные сценарии или надеяться, что IMDSv1 не отключён.
GCP и Yandex Cloud metadata
В GCP metadata endpoint требует специальный заголовок Metadata-Flavor: Google. Без него запрос отклоняется. Это дополнительный барьер. Но если SSRF позволяет добавлять заголовки, защита не мешает.Запрос к GCP metadata обычно выглядит так:
HTTP:
GET /computeMetadata/v1/instance/service-accounts/default/token HTTP/1.1
Host: metadata.google.internal
Metadata-Flavor: Google
В Yandex Cloud логика схожа. Metadata endpoint также доступен с инстанса, возвращает IAM-токены или информацию о сервисном аккаунте. Нюансы отличаются, но принцип один - внутренняя доверенная зона.
После получения учетных данных начинается другая игра. Это уже не SSRF, а полноценное использование IAM. Можно читать объекты из контейнера, перечислять ресурсы, запускать новые инстансы, если права позволяют. И всё это - от имени роли, которая изначально предназначалась для backend’а.
Вот здесь цепочка начинает расползаться. SSRF перестаёт быть локальной проблемой приложения. Он становится точкой входа в облачную плоскость управления. На практике именно metadata exploitation часто приносит максимальный импакт. Потому что доступ к Redis даёт RCE в пределах контейнера. А доступ к IAM-роли может дать контроль над всей средой.
Но если Redis тоже открыт и без ACL, можно пойти и по другому пути - через запись данных в файловую систему, подмену конфигурации или прямую инъекцию команд через gopher. И вот там начинается самая технически плотная часть цепочки.
Redis Exploitation: от внутреннего сервиса к выполнению кода
Если на этапе внутренней разведки стало понятно, что Redis доступен без аутентификации, это точка, где SSRF можно превратить в прямое управление сервисом. Redis в облачных средах часто поднимается для кэша или брокера задач. И да, его любят ставить без пароля, потому что он “только внутри VPC”.Через SSRF к Redis напрямую по HTTP не подключиться. Он не говорит на HTTP. Но если уязвимость позволяет использовать нестандартные схемы, появляется возможность работать через gopher.
Gopher protocol: почему он вообще здесь
Gopher - старый протокол, но в контексте SSRF он используется как способ отправить произвольные байты в TCP-соединение. Некоторые HTTP-клиенты поддерживают схему gopher://. И если фильтр её не блокирует, можно сформировать payload, который будет передан сервису почти без изменений.Формат выглядит примерно так:
Код:
gopher://127.0.0.1:6379/_<payload>
Не везде это срабатывает. Некоторые библиотеки режут gopher. Некоторые ограничивают схемы жёстко. Но если проходит - начинается полноценное взаимодействие с Redis.
Redis commands через SSRF
Redis использует RESP-протокол. Команды выглядят как структурированные массивы байтов. Их нужно правильно закодировать, иначе сервис вернёт ошибку. Через gopher мы фактически имитируем клиента Redis.Пример логики запроса:
- формируем команду CONFIG SET
- указываем новый dir
- меняем dbfilename
- сохраняем данные через SAVE
Важно понимать контекст. В Kubernetes Redis может быть отдельным подом без доступа к файловой системе приложения. А может быть sidecar’ом в том же пространстве. Разница принципиальная. В первом случае RCE будет локальным для Redis. Во втором - уже ближе к приложению.
SSH key injection
Классический сценарий - запись публичного SSH-ключа в authorized_keys. Для этого Redis перенастраивается на директорию пользователя и сохраняет ключ как файл. Если процесс имеет соответствующие права, после этого возможен прямой вход по SSH.В контейнерных средах SSH может быть не установлен. Но если это VM или нестандартная конфигурация - такой вектор работает.
Смысл здесь не в самом SSH. Смысл в том, что Redis позволяет писать файлы при определённых условиях. И если есть путь к каталогу, который влияет на выполнение кода, это уже шаг к RCE.
Cron job и module load
Другой сценарий - запись в cron-директорию, если у процесса есть права. Это позволяет выполнить произвольную команду по расписанию. В контейнерных средах cron встречается реже, но на классических VM - вполне.Ещё более гибкий путь - использование Redis modules. Если разрешена команда MODULE LOAD и можно загрузить внешний модуль, появляется возможность выполнения системных вызовов через модульную логику. Это зависит от версии Redis и конфигурации. Не везде доступно, но если доступно - последствия серьёзные.
Есть также Lua scripting внутри Redis. Команда EVAL позволяет выполнять Lua-код в контексте сервиса. Прямого system() там нет, но при наличии определённых модулей или небезопасной конфигурации сценарии расширяются.
Ключевой момент: Redis сам по себе не обязан приводить к RCE. Он становится трамплином, если:
- нет аутентификации или ACL;
- процесс имеет лишние права;
- файловая система доступна;
- инфраструктура допускает side-effect от изменения конфигурации.
Post-Exploitation: из контейнера в облако
Если цепочка дошла до RCE через Redis, дальше начинается то, ради чего всё и затевалось. Контейнер или инстанс уже под контролем. Вопрос теперь не в том, можно ли выполнить команду. Вопрос - какие у этого окружения права и куда можно двигаться дальше.Если metadata доступна локально, RCE упрощает задачу. Теперь можно напрямую обратиться к endpoint’у и получить временные ключи без ограничений SSRF. Для AWS это тот же IMDS, для GCP - metadata.google.internal, для Yandex Cloud - соответствующий link-local адрес. Разница в том, что теперь нет ограничений на метод или заголовки. Полный контроль над HTTP-запросом.
После получения IAM-токенов начинается фаза облачного enum’а. Проверяются разрешения роли: доступ к S3 или Object Storage, возможность читать secrets, список инстансов, права на создание новых ресурсов. Если роль избыточна, lateral movement уходит в плоскость управления облаком.
Pivot из контейнера в облаке часто выглядит так:
| Вектор | Что проверяется |
|---|---|
| IAM роль | S3, очереди, базы данных, запуск инстансов |
| Kubernetes API | Доступ к namespace, secrets, pod exec |
| Внутренние сервисы | Базы данных, message broker, internal API |
| CI/CD токены | Git, registry, pipeline secrets |
Если удалось получить права на создание новых инстансов или контейнеров, это уже устойчивый доступ. Можно развернуть свой ресурс внутри VPC и работать оттуда без необходимости поддерживать исходный RCE.
Есть ещё момент, который часто упускают. Логи и мониторинг. Если RCE выполнен через Redis или SSRF, но нет аномалий в облачных логах, атака может пройти тихо. Поэтому на этапе постэксплуатации полезно оценить, насколько заметна активность. Это не про обход детекта ради обхода. Это про понимание зрелости защиты.
На этом этапе уже видно, насколько архитектура выдерживает компрометацию одного сервиса. И обычно именно здесь становится понятно, что SSRF был не просто веб-багом, а входной точкой в облачную модель доверия.
Заключение
SSRF сам по себе не выглядит пугающе. Один URL-параметр, один серверный запрос. Но в облачной архитектуре это уже не просто веб-баг. Это сетевой доступ из доверенной зоны. И если внутри есть Redis без ACL, metadata endpoint с избыточной ролью или сервисы без аутентификации, цепочка собирается довольно быстро. Не из-за экзотики, а из-за накопленных упрощений.Наша цепочка - это проверка того, насколько изолированы ваши сервисы друг от друга. Если backend может говорить со всем, Redis доверяет всем, а IAM разрешает больше, чем нужно, компрометация одного компонента тянет за собой остальное. В нормальной архитектуре каждый шаг должен встречать барьер: фильтрация исходящих запросов, строгие ACL, минимальные роли, отключённый IMDSv1, сегментация внутри кластера.
Ценность этой цепочки не в том, чтобы показать трюк с gopher или красиво дернуть metadata. Она показывает, как один веб-вход превращается в облачный доступ. И если после аудита приложение перестаёт быть универсальным прокси, Redis - открытым внутренним сервисом, а IAM - избыточным, значит инфраструктура стала взрослее. Всё остальное - детали реализации.
Последнее редактирование: