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

Три ошибки в одном проекте: 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 по этому каталогу:
- Разведка — сканирование портов через
nmap, обнаружение HTTP и SSH. Скачивание APK с сайта. - Credential Access — реверс APK через
jadx-gui, извлечение вшитого JWT-токена (T1552.001 — Credentials In Files: секреты, хранящиеся прямо в файлах приложения). - Initial Access — использование найденного токена для доступа к admin-эндпоинтам API (T1078 — Valid Accounts: применение легитимных учётных данных).
- Collection — path traversal через API-эндпоинт, чтение приватного SSH-ключа пользователя (T1005 — Data from Local System).
- Lateral Movement — подключение к серверу по SSH с украденным ключом (T1021.004 — SSH).
- Credential Access — нахождение и расшифровка зашифрованного бэкапа Solar-PuTTY для извлечения root-пароля (T1555.005 — Password Managers, T1140 — Deobfuscate/Decode Files).
- Privilege Escalation —
su 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-скрипт для перебора:
- Декодировать Base64-содержимое файла
sessions-backup.dat - Извлечь salt (соль — случайные байты для защиты от rainbow-таблиц) и IV (вектор инициализации) из первых байтов
- Для каждого пароля из словаря — вывести ключ через PBKDF2, расшифровать 3DES, проверить результат
- Если расшифрованный текст содержит валидный 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.