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

HTB Instant writeup: реверс APK, JWT bypass и путь к root через уязвимый мобильный API

HTB Instant writeup: реверс APK, JWT bypass и путь к root через уязвимый мобильный API
Время чтения: 10 мин.

Три ошибки в одном проекте: hardcoded JWT-токен с ролью Admin прямо в коде Android-приложения, открытая Swagger-документация на поддомене и эндпоинт для чтения логов без фильтрации путей. Машина Instant на HackTheBox (difficulty: medium, автор — tahaafarooq) показывает, как мобильное приложение превращается в точку входа для полного захвата Linux-сервера. Разработчик оставил тестовый токен в production-сборке, API отдаёт файлы без проверки пути, а SSH-ключ лежит без passphrase. Три промаха — и root у атакующего. В этом HTB Instant writeup разберём каждый шаг: от декомпиляции APK до расшифровки Solar-PuTTY и получения root-пароля.

Карта атаки: полная цепочка kill chain

Прежде чем переходить к командам — полезно увидеть атаку целиком. MITRE ATT&CK — открытая база тактик и техник атак, где каждому действию злоумышленника присвоен T-код (например, T1190 — эксплуатация публичного сервиса). Вот как раскладывается машина Instant по этому каталогу:

  1. Разведка — сканирование портов через nmap, обнаружение HTTP и SSH. Скачивание APK с сайта.
  2. Credential Access — реверс APK через jadx-gui, извлечение вшитого JWT-токена (T1552.001 — Credentials In Files: секреты, хранящиеся прямо в файлах приложения).
  3. Initial Access — использование найденного токена для доступа к admin-эндпоинтам API (T1078 — Valid Accounts: применение легитимных учётных данных).
  4. Collection — path traversal через API-эндпоинт, чтение приватного SSH-ключа пользователя (T1005 — Data from Local System).
  5. Lateral Movement — подключение к серверу по SSH с украденным ключом (T1021.004 — SSH).
  6. Credential Access — нахождение и расшифровка зашифрованного бэкапа Solar-PuTTY для извлечения root-пароля (T1555.005 — Password Managers, T1140 — Deobfuscate/Decode Files).
  7. Privilege Escalationsu root с расшифрованным паролем.

Вся атака выполняется в учебной CTF-среде HackTheBox без защитных средств. В реальном внешнем пентесте APK обычно скачивают из Google Play или получают от заказчика, а LFI в API перехватывается WAF-правилами. Здесь защита отсутствует намеренно — чтобы цепочка была видна от начала до конца.

Реверс Android APK: находим JWT токен и скрытые эндпоинты

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

  • ОС: Kali Linux 2024.x, Parrot OS или любой дистрибутив с инструментами пентеста
  • RAM: минимум 4 ГБ (8 ГБ рекомендуется — jadx-gui жрёт память при декомпиляции крупных APK)
  • Инструменты: jadx-gui или jadx CLI (GitHub: skylot/jadx, активно поддерживается, 42k+ звёзд), curl, SSH-клиент, Python 3
  • Сеть: активное VPN-подключение к HackTheBox (.ovpn-профиль)
  • Записи в /etc/hosts: на старте — 10.10.11.37 instant.htb, позднее добавим ещё два поддомена

Сканирование портов и первый контакт с сайтом

Сканирование портов — стандартное начало любого пентеста: определяем, какие сервисы на машине принимают соединения. Запускаем nmap -p- --min-rate 10000 10.10.11.37 (флаг -p- проверяет все 65535 портов, --min-rate 10000 ускоряет процесс за счёт интенсивной отправки пакетов).

Два открытых порта — 22 (OpenSSH 9.6p1 Ubuntu) и 80 (Apache 2.4.58). По версиям видно, что на сервере Ubuntu 24.04. SSH на этом этапе бесполезен — нет ни пароля, ни ключа. HTTP-сервер перенаправляет на instant.htb, поэтому добавляем запись: echo "10.10.11.37 instant.htb" | sudo tee -a /etc/hosts.

На сайте — статический лендинг мобильного приложения Instant Wallet. Никаких форм, интерактива — только ссылка на скачивание instant.apk. Брутфорс поддоменов через ffuf ничего не даёт. Значит, ответы спрятаны внутри APK.

Декомпиляция APK: hardcoded-секреты и скрытые поддомены

APK (Android Package Kit) — формат установочных файлов Android, по сути ZIP-архив с Java-байткодом. Декомпилятор jadx-gui превращает байткод обратно в читаемый исходный код. Открываем скачанный instant.apk и запускаем поиск по строкам (Navigation → Text Search) с запросом instant.htb — ищем все URL и доменные имена, которые приложение использует.

Находим: — Два ранее неизвестных поддомена: mywalletv1.instant.htb (API-бэкенд мобильного кошелька) и swagger-ui.instant.htb (Swagger UI — интерфейс для просмотра и тестирования API-эндпоинтов). — Hardcoded JWT-токен в классе AdminActivities — полноценный токен авторизации, вшитый в исходный код для тестовых целей.

Добавляем оба поддомена: echo "10.10.11.37 mywalletv1.instant.htb swagger-ui.instant.htb" | sudo tee -a /etc/hosts.

JWT (JSON Web Token) — стандартный формат передачи данных аутентификации между клиентом и сервером. Токен состоит из трёх частей, разделённых точками: заголовок (алгоритм подписи), полезная нагрузка (payload) и подпись. Декодируем найденный токен через jwt.io или командой в терминале:

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "id": 1,
  "role": "Admin",
  "walId": "f0eca6e5-783a-471d-9d8f-0162cbc900db",
  "exp": 33259303656
}

"role": "Admin" — токен принадлежит администратору. "exp": 33259303656 — срок действия выставлен примерно на 3024 год. Тысячу лет. Разработчик создал токен для тестирования и забыл удалить перед сборкой production-APK. По классификации OWASP это API2:2023 Broken Authentication — некорректная реализация механизмов аутентификации. В терминах MITRE ATT&CK — техника T1552.001 (Credentials In Files).

Ограничение: если приложение обфусцировано инструментами вроде ProGuard или R8 (а большинство production-приложений обфусцированы), строки могут быть зашифрованы — jadx их не покажет напрямую. Тогда понадобится apktool для извлечения ресурсов и конфигурационных XML-файлов, где секреты тоже нередко всплывают.

JWT bypass через мобильный API: от Swagger до чтения файлов

Исследование API через Swagger и admin-эндпоинты

Переходим на swagger-ui.instant.htb в браузере — полная документация API. Swagger отображает все маршруты с описанием параметров. Среди пользовательских эндпоинтов (login, register, profile) обнаруживаются три admin-маршрута:

  • /api/v1/admin/list/users — список всех пользователей
  • /api/v1/admin/view/logs — перечень доступных лог-файлов
  • /api/v1/admin/read/log — чтение содержимого конкретного лога

Без токена все admin-маршруты возвращают 401 (Unauthorized). Проверяем найденный JWT: curl -H "Authorization: <JWT-токен>" http://mywalletv1.instant.htb/api/v1/view/profile | jq (утилита jq форматирует JSON для удобного чтения). Ответ: статус 200, пользователь instantAdmin, роль Admin, баланс кошелька 10 000 000. Токен рабочий.

Через /api/v1/admin/list/users получаем три аккаунта: instantAdmin, shirohige и тестовый string. Имя shirohige запоминаем — именно под этим пользователем мы получим первый shell.

Оставленная открытой Swagger-документация на production — частая находка в реальных аудитах мобильных приложений. Это A05:2021 Security Misconfiguration по OWASP — небезопасная конфигурация по умолчанию.

Path traversal в API: пошаговая эксплуатация

Эндпоинт /api/v1/admin/read/log принимает параметр log_file_name и читает файл из директории /home/shirohige/logs/. Разработчик не реализовал проверку пользовательского ввода — можно подставить ../ (переход на уровень выше в файловой системе) для чтения произвольных файлов. Эта уязвимость называется path traversal (она же directory traversal или LFI — Local File Inclusion) и попадает под A01:2021 Broken Access Control.

Шаг 1. Проверяем наличие логов: GET-запрос на /api/v1/admin/view/logs с JWT в заголовке. Ответ: файл 1.log в /home/shirohige/logs/. Теперь знаем базовую директорию.

Шаг 2. Подтверждаем path traversal — пробуем прочитать /etc/passwd:

curl -s -H "Authorization: eyJhbGciOiJIUzI1NiIs..." \
  "http://mywalletv1.instant.htb/api/v1/admin/read/log\
?log_file_name=../../../etc/passwd" | jq

Если в ответе появилось содержимое /etc/passwd (список пользователей системы) — traversal работает. Три уровня ../../.. нужны, чтобы подняться из /home/shirohige/logs/ до корня файловой системы /.

Шаг 3. Извлекаем SSH-ключ пользователя shirohige. Отправляем запрос с путём ../../../home/shirohige/.ssh/id_rsa. Ответ содержит приватный SSH-ключ в формате PEM. Сохраняем его в файл id_rsa, ставим права chmod 600 id_rsa (SSH отклонит ключ с избыточными правами доступа) и подключаемся: ssh -i id_rsa shirohige@instant.htb.

Мы на сервере. Файл user.txt в домашней директории — первый флаг.

Такая уязвимость path traversal — не выдумка для CTF. CVE-2024-23334 в библиотеке aiohttp (Python, CWE-22 — Improper Limitation of a Pathname) — почти идентичный баг: при включённой опции follow_symlinks нет проверки, что читаемый файл находится внутри разрешённой директории. CVSS-вектор — AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N (атака по сети, без привилегий, без участия пользователя, высокий ущерб конфиденциальности). EPSS (Exploit Prediction Scoring System — оценка вероятности эксплуатации уязвимости в ближайшие 30 дней) — 0.77, это Top 1% среди всех CVE. По данным inthewild.io, CVE-2024-23334 эксплуатируется в дикой природе с марта 2024 года.

Privilege escalation: расшифровка Solar-PuTTY и извлечение root-пароля

Поиск зашифрованного бэкапа

После получения shell как shirohige начинаем enumeration — поиск путей повышения привилегий. В директории /opt/backups/Solar-PuTTY/ лежит файл sessions-backup.dat.

Solar-PuTTY — SSH-клиент для Windows, аналог PuTTY с возможностью сохранять пароли и ключи для автоматического входа. Экспортированные сессии шифруются паролем пользователя, и sessions-backup.dat — зашифрованный бэкап с паролями подключений внутри. Формат хранения аналогичен зашифрованным SQLite-хранилищам из мобильных кошельков (wallet) и менеджеров паролей — та же механика «decrypt database → extract credentials».

В kill chain это этап credential access — извлечение учётных данных из менеджера паролей (T1555.005 — Password Managers).

Brute-force пароля шифрования

Solar-PuTTY шифрует экспортированные сессии так: PBKDF2 для деривации ключа из пароля пользователя, затем 3DES (Triple DES) для шифрования данных. Алгоритм описан в публичном исследовании «Reverse Engineering Solar-PuTTY 4.0.0.47». Зная схему, пишем Python-скрипт для перебора:

  1. Декодировать Base64-содержимое файла sessions-backup.dat
  2. Извлечь salt (соль — случайные байты для защиты от rainbow-таблиц) и IV (вектор инициализации) из первых байтов
  3. Для каждого пароля из словаря — вывести ключ через PBKDF2, расшифровать 3DES, проверить результат
  4. Если расшифрованный текст содержит валидный JSON — пароль найден

Запускаем скрипт с sessions-backup.dat и словарём rockyou.txt (стандартный словарь паролей, в Kali лежит в /usr/share/wordlists/). Через несколько секунд скрипт находит пароль, а расшифрованные данные содержат root-пароль в открытом виде.

Финал: su root, вводим расшифрованный пароль, забираем /root/root.txt. Машина пройдена.

Ограничение: Solar-PuTTY — Windows-приложение, и на Linux-серверах в production оно не встречается. Но механика переносима: зашифрованные бэкапы сессий из mRemoteNG, SecureCRT, MobaXterm ломаются тем же подходом — реверсим формат шифрования, перебираем пароль. Во внутренних пентестах такие файлы находятся в профилях пользователей на файловых шарах.

Защита и детект: что пошло не так на каждом этапе

Этап атаки Ошибка Мера защиты
Hardcoded JWT в APK Секреты вшиты в клиентский код SAST-сканирование в CI/CD (Semgrep, Gitleaks); ротация токенов; запрет production-секретов в мобильных приложениях
Открытый Swagger API-документация доступна без аутентификации Закрыть Swagger на production; ограничить доступ по IP или VPN
Path traversal Отсутствие валидации параметра log_file_name Whitelist разрешённых файлов; блокировка ../ на уровне API; WAF-правила
SSH-ключ без passphrase Приватный ключ без защиты Создавать ключи с passphrase: ssh-keygen -t ed25519 -a 100
Слабый пароль бэкапа Пароль из словаря rockyou.txt Длинные пароли от 16 символов; аппаратные ключи; централизованное управление секретами (Vault, CyberArk)

В production-среде с CrowdStrike Falcon или Elastic 8.x+ массовое чтение файлов через API-эндпоинт вызвало бы алерт уже на третьем-четвёртом запросе — аномальный объём файловых операций от веб-процесса детектируется по EDR-телеметрии. А вот hardcoded-токен в APK серверные средства защиты не поймают — это ошибка этапа разработки, и ловить её нужно SAST-сканерами до деплоя.

Instant — одна из тех машин HTB, которые ценны не рейтингом сложности, а паттерном мышления. Цепочка «мобильное приложение → скрытый API → серверная файловая система» — не учебная абстракция. На каждом втором аудите мобильного банковского приложения API-бэкенд задокументирован через Swagger и доступен без дополнительной авторизации. Разница с HTB: в production hardcoded-токен чаще сидит не в Java-классе, а в конфигурационных XML-файлах приложения — и находится через apktool с grep -r "Bearer", а не через jadx.

SAST-сканеры вроде Semgrep и Gitleaks ловят секреты в коде за секунды. Hardcoded-секреты продолжают утекать не потому, что инструментов нет, а потому что нет security gate в DevOps-пайплайне. Разработчик создал тестовый токен, CI/CD не проверил, APK ушёл в production — и Admin-токен со сроком действия до 3024 года лежит в открытом доступе.

С ростом mobile-first архитектур количество подобных кейсов будет расти: API-поверхность расширяется быстрее, чем команды безопасности успевают её покрывать. Если только начинаешь разбираться — на codeby.school есть IB Basics, где фундамент выстраивается от сетевых основ до первых задач без академического тона.

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