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

HTB Checker прохождение: от SQL-инъекции в Teampass до root через обход TOTP

HTB Checker прохождение: от SQL-инъекции в Teampass до root через обход TOTP
Время чтения: 11 мин.

Четыре уязвимости, три сервиса, два пользователя — и цепочка, где SQL-инъекция с CVSS 7.5 в итоге отдаёт root-шелл. Машина Checker на HackTheBox (уровень Hard) собрана хитро: ни одно звено не работает само по себе. Инъекция в Teampass вытаскивает хеши, хеши открывают BookStack, BookStack через SSRF читает файлы на сервере, а в файлах — TOTP-секрет для SSH. Каждый шаг по отдельности выглядит «ну и что?». Вместе — полная компрометация хоста. Ниже разбираю каждый этап: от первого nmap до cat /root/root.txt.

Карта атаки: от initial access до privilege escalation

Прежде чем запускать команды, стоит посмотреть на атаку целиком. Для тех, кто впервые видит T-коды: MITRE ATT&CK — открытая база тактик и техник, которые применяют атакующие. T1190, T1552 и т.п. — уникальные идентификаторы каждой техники. Удобно для отчётов и для понимания, на каком этапе kill chain ты находишься.

Этап Тактика Техника (T-код) Действие
1 Initial Access Exploit Public-Facing Application (T1190) SQL-инъекция в API Teampass
2 Credential Access Credentials In Files (T1552.001) Извлечение хешей паролей и кредов BookStack
3 Initial Access Valid Accounts (T1078) Вход в BookStack с украденными учётными данными
4 Discovery File and Directory Discovery (T1083) SSRF + PHP Filter Oracle для чтения файлов
5 Credential Access MFA Interception (T1111) Чтение TOTP-секрета из бэкапа
6 Lateral Movement SSH (T1021.004) SSH-подключение с паролем + одноразовым кодом
7 Privilege Escalation Race condition в бинарнике Отравление shared memory для исполнения от root

Три из семи этапов — credential access. Это HTB Checker writeup, который наглядно показывает: компрометация одного сервиса каскадом роняет остальные, когда креды переиспользуются или хранятся небезопасно. Цепочка SQL injection to RCE здесь не прямая — она проходит через четыре промежуточных звена.

Требования к окружению

  • ОС: Kali Linux 2024+ или Parrot OS (любой дистрибутив с предустановленным набором инструментов)
  • RAM: от 2 ГБ — тяжёлых вычислений на стороне атакующего нет
  • Сеть: активное VPN-подключение к HackTheBox (интерфейс tun0)
  • Инструменты: nmap, hashcat или John the Ripper, oathtool (пакет oath-toolkit — установка: sudo apt install oathtool), Python 3 с pip, git
  • Опционально: Burp Suite Community Edition для инспекции HTTP-запросов

Разведка: BookStack, Teampass и rate limiting

[Применимо: внешний пентест, CTF-лаборатория]

Сканирование начинается стандартно. Запускаем nmap -p- --min-rate 10000 10.10.11.56 — полное сканирование всех 65535 TCP-портов с ускорением. Ожидаемый результат: три открытых порта — 22, 80 и 8080. Детальное сканирование nmap -p 22,80,8080 -sCV 10.10.11.56 (флаги -sC — стандартные NSE-скрипты, -sV — определение версий) покажет:

  • Порт 22: OpenSSH 8.9p1 — по версии определяем Ubuntu 22.04 (Jammy)
  • Порт 80: Apache, отдаёт 403 Forbidden напрямую по IP
  • Порт 8080: Apache, аналогичная ситуация

Оба HTTP-порта возвращают 403 без правильного заголовка Host. Порт 80 перенаправляет на checker.htb — добавляем запись в /etc/hosts: echo '10.10.11.56 checker.htb' | sudo tee -a /etc/hosts. После этого на порту 80 открывается BookStack — open-source CMS на PHP/Laravel для ведения документации. В HTML-исходнике страницы входа видна версия v23.10.2. На порту 8080 работает Teampass — веб-менеджер паролей, тоже PHP.

Попытка перебрать директории или субдомены упирается в rate limiting — сервер отвечает кодом 429 (Too Many Requests) уже после нескольких запросов. Грубая сила здесь бесполезна — работать придётся точечно.

Без учётных данных в BookStack войти нельзя — страница /register перенаправляет обратно на /login. Зато у Teampass есть API, и он включён. Проверка: curl http://10.10.11.56:8080/api/index.php/authorize возвращает {"error":"Method GET not supported"} — если бы API был выключен, ответом было бы «API usage is not allowed». Версия Teampass определяется через changelog.txt в корне приложения: копирайт 2009-2022 указывает на диапазон между 3.0.0.10 и 3.0.0.22.

SQL-инъекция в Teampass: эксплуатация CVE-2023-1545

CVE-2023-1545 — SQL-инъекция в Teampass версий до 3.0.0.23 (по данным NVD). Тип уязвимости — CWE-89 (Improper Neutralization of Special Elements in SQL Command). По классификации OWASP — категория A03:2021 (Injection). Оценка CVSS 3.1: 7.5 (HIGH), вектор AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N. Если раньше не разбирали CVSS-вектор — вот что значит каждый компонент:

  • AV:N (Attack Vector: Network) — атака по сети, физический доступ не нужен
  • AC:L (Attack Complexity: Low) — низкая сложность эксплуатации, никаких особых условий
  • PR:N (Privileges Required: None) — атака от анонимного пользователя, без авторизации
  • UI:N (User Interaction: None) — без участия жертвы
  • C:H (Confidentiality: High) — полная компрометация конфиденциальности

EPSS-оценка — 0.0835. EPSS (Exploit Prediction Scoring System) — вероятность от 0 до 1, что уязвимость начнут эксплуатировать в ближайшие 30 дней. 0.0835 — top 10% среди всех CVE, то есть эксплуатация весьма вероятна. Публичный эксплойт доступен на Exploit-DB (EDB-52094).

CISA-ADP оценивает уязвимость как Track* (есть рабочий PoC, эксплуатация автоматизируема). По данным OSV.dev, уязвимый пакет — nilsteampassnet/teampass, патч выпущен в версии 3.0.0.22.

Как инъекция извлекает данные через JWT-токен

Инъекция работает через поле login в POST-запросе к /api/index.php/authorize. Teampass проверяет имя пользователя SQL-запросом без фильтрации ввода. Атакующий подставляет UNION SELECT, и тот делает три вещи:

  1. Создаёт подменённую запись с заранее известным bcrypt-хешем для пароля h4ck3d
  2. Вместо реальных данных подставляет произвольный подзапрос в позицию поля public_key
  3. Сервер принимает «подменённый» вход и возвращает JWT (JSON Web Token — токен авторизации), в payload которого лежит результат подзапроса
# Ключевая часть инъекции (из публичного PoC, EDB-52094)
inject="none' UNION SELECT id, '$arbitrary_hash', ($1), \
  private_key, personal_folder, fonction_id, \
  groupes_visibles, groupes_interdits, 'foo' \
  FROM teampass_users WHERE login='admin"
# $1 — произвольный SQL-подзапрос
# Результат утекает через поле public_key JWT-токена

Расшифровать JWT можно прямо в терминале: echo $token | cut -d"." -f2 | base64 -d 2>/dev/null | jq -r '.public_key'. Каждый вызов скрипта — один HTTP-запрос и один ответ с данными. Сначала извлекаем количество пользователей, затем их логины и хеши. Результат: два пользователя — admin и bob.

Хеш bob оказывается достаточно слабым для взлома. Команда hashcat -m 3200 hash.txt rockyou.txt (режим 3200 — bcrypt) справляется за разумное время, если пароль есть в словаре rockyou.txt.

Ограничение техники: инъекция работает только при включённом API Teampass. Если API выключен, эндпойнт возвращает «API usage is not allowed» и вектор закрыт. На Teampass 3.0.0.23+ уязвимость закрыта.

От Teampass к BookStack: извлечение учётных данных

Со взломанным паролем bob входим в Teampass. Внутри менеджера хранятся записи, среди которых — учётные данные для BookStack и для SSH-пользователя reader.

Попытка подключиться по SSH: ssh reader@checker.htb, вводим пароль — первичная аутентификация проходит, но сервер запрашивает verification code. Это TOTP (Time-based One-Time Password) — одноразовый пароль, который генерируется на основе секрета и текущего времени. Без кода SSH-сессия не откроется. С точки зрения OWASP это A07:2021 (Identification and Authentication Failures).

SSH пока заблокирован двухфакторкой, но учётные данные BookStack работают. Заходим на порт 80 — и попадаем внутрь CMS с правами пользователя.

Уязвимость BookStack: SSRF и чтение файлов

[Применимо: внешний пентест, веб-приложение на PHP/Laravel]

В BookStack v23.10.2 есть уязвимость типа SSRF (Server-Side Request Forgery — когда мы заставляем сервер делать HTTP-запрос от своего имени, OWASP A10:2021). При сохранении черновика страницы приложение обрабатывает HTML-содержимое и пытается загрузить изображения из тегов <img>. URL изображения полностью контролируется пользователем: цепочка вызовов от $this->intervention->make($imageData) до @file_get_contents($url, false, $context) не содержит валидации. Мы можем заставить сервер обратиться к любому URL, включая внутренние ресурсы.

PHP Filter Oracle: побайтовое чтение вслепую

Сама SSRF позволяет обращаться к внутренним адресам, но не показывает ответ пользователю. Здесь помогает техника PHP Filter Chain Oracle — метод побитового вычитывания файлов через PHP-обёртку [php://filter](https://www.php.net/manual/en/wrappers.php.php), даже когда содержимое не возвращается в ответе (blind-чтение). Техника впервые продемонстрирована на DownUnderCTF 2022, инструмент разработан командой Synacktiv и опубликован на GitHub.

Для работы с BookStack скрипт нужно модифицировать: обернуть PHP-фильтр в HTML-тег <img src='data:image/png;base64,...' />, потому что BookStack ожидает изображение в поле html при сохранении черновика. Модификация затрагивает файл requestor.py — строки, где формируется filter_chain.

# Запуск oracle-эксплойта для чтения /etc/passwd
python3 filters_chain_oracle_exploit.py \
  --target 'http://checker.htb/ajax/page/13/save-draft' \
  --file '/etc/passwd' \
  --verb PUT \
  --parameter html \
  --headers '{"X-CSRF-TOKEN":"<token>","Cookie":"<session>"}' \
  --log loot.txt

Параметр --file — путь к целевому файлу. Число 13 — ID созданной страницы в BookStack (нужно заранее создать книгу и страницу). Токен и cookie берутся из авторизованной сессии в браузере или через Burp Suite. Процесс небыстрый: oracle вычитывает файл побайтово, каждый символ — несколько HTTP-запросов. Если /etc/passwd читается успешно — SSRF работает, можно переходить к более интересным целям.

В BookStack среди существующих книг обнаруживается фрагмент скрипта бэкапирования, который рекурсивно копирует домашние директории в /backup/home_backup. Файл .google_authenticator пользователя reader (в котором хранится TOTP-секрет) доступен по пути /backup/home_backup/reader/.google_authenticator.

Обход TOTP: извлечение секрета из бэкапа

Запускаем oracle с --file '/backup/home_backup/reader/.google_authenticator' и через несколько минут получаем seed — строку из 16–32 символов в формате base32. Это секрет TOTP.

Как работает TOTP (RFC 6238): сервер и клиент хранят общий секрет. На основе этого секрета и текущего времени (с округлением до 30 секунд) генерируется шестизначный код. Суть обхода проста: если у атакующего есть seed, генерация кодов тривиальна. Никакой магии — чистая математика.

Утилита oathtool из пакета oath-toolkit делает это одной командой: oathtool --totp -b <SEED>. Флаг -b указывает, что seed в base32, --totp задаёт алгоритм. На выходе — шестизначное число, действительное 30 секунд.

Подключаемся: ssh reader@checker.htb, вводим пароль из Teampass, а в поле verification code — свежесгенерированный TOTP. Шелл получен: uid=1000(reader) gid=1000(reader).

Техника соответствует MITRE T1111 (Multi-Factor Authentication Interception) — перехват MFA не атакой на алгоритм, а компрометацией хранилища секретов. На практике: если бэкапы содержат TOTP-сиды в открытом виде и доступны через SSRF — двухфакторка защищает только от перебора паролей, не от цепочки атак.

Повышение привилегий до root через race condition

После входа как reader проверяем sudo-привилегии командой sudo -l. Ответ: (ALL) NOPASSWD: /opt/hash-checker/check-leak.sh * — можно запускать скрипт check-leak.sh от root без пароля.

Бинарник check-leak, который вызывается из скрипта, проверяет хеши паролей на предмет утечек. Он использует shared memory — механизм IPC в Linux, через который несколько процессов работают с одним участком памяти (вызовы shmget/shmat). Уязвимость — race condition (состояние гонки): между моментом записи данных в shared memory и моментом их чтения привилегированным процессом есть окно, в которое атакующий может подменить содержимое. Эксплуатация требует точного тайминга — нужно перезаписать сегмент именно между двумя операциями.

Ограничение: техника специфична для этого бинарника и не является универсальной. В реальной инфраструктуре аналогичные race condition в shared memory встречаются в кастомных приложениях, но каждый случай требует отдельного анализа тайминга.

Результат — выполнение команд от root. Забираем флаги: cat /home/reader/user.txt и cat /root/root.txt. HackTheBox Checker решение завершено.


Вся цепочка Checker — пример того, как уязвимости средней и высокой критичности, каждая из которых сама по себе не даёт полный контроль, в связке отдают root. SQL-инъекция с CVSS 7.5 на бумаге выглядит как «только чтение данных» — C:H/I:N/A:N. Но именно она запускает всё остальное: от менеджера паролей до TOTP-секрета в бэкапе.

В реальных пентестах таких каскадных цепочек больше, чем одиночных критических RCE. Компании латают CVE с оценкой 9+, но оставляют Teampass на версии 3.0.0.21, потому что «это внутренний сервис, кто туда полезет». А потом SSRF в CMS, которая «ничего критичного не хранит», вытаскивает TOTP-сиды из бэкапов — потому что бэкапы лежат в читаемой директории без ограничений доступа. Каждое звено по отдельности — «некритично». Вместе — root за вечер.

Отдельный урок — про иллюзию безопасности двухфакторки. TOTP защищает от перебора паролей и credential stuffing. Но если seed хранится в plain text и попадает в бэкап, который читается через SSRF — это та же однофакторная аутентификация с дополнительным шагом. Защита MFA ровно настолько прочна, насколько защищён сам секрет. Этот урок из Checker ценнее любого отдельного CVE в цепочке. На курсе IB Basics такие цепочки разбирают системно — от разведки до пост-эксплуатации, с лабами на каждый этап.

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