HTB Runner
Эксплуатируем баг в TeamCity и совершаем runC Escape
Наша цель — получение прав суперпользователя на машине Runner с учебной площадки Hack The Box. Уровень сложности задания — средний.
warning
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
Разведка
Добавляем IP-адрес машины в /
:
10.10.11.13 runner.htb
И запускаем сканирование портов.
Справка: сканирование портов
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
#!/bin/bashports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -A $1
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A
).

По результатам сканирования имеем три открытых порта:
- порт 22 — служба OpenSSH 8.9p1;
- порт 80 — веб‑сервер Nginx 1.18.0;
- порт 8000 — веб‑сервер Nagios NSCA.
C SSH ничего не поделать, поэтому переходим к вебу. На порте 8000 необходим какой‑то эндпоинт, так как получаем ответ 404, а на порте 80 нас встречает сайт‑визитка.


Точка входа
На сайте ничего интересного найти не удалось, поэтому приступим к сканированию.
Справка: сканирование веба c ffuf
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем следующие параметры:
-
-w
— словарь (я использую словари из набора SecLists); -
-t
— количество потоков; -
-u
— URL; -
-H
— HTTP-заголовок.
Место перебора помечается словом FUZZ.
Задаем все параметры и запускаем сканирование:
ffuf -u http://runner.htb/ -H 'Host: FUZZ.runner.htb' -w subdomains-bitquark-top100000.txt -t 256

Однако утилита выводит все варианты из словаря, поэтому добавим фильтр по размеру страницы (параметр -fs
).
ffuf -u http://runner.htb/ -H 'Host: FUZZ.runner.htb' -w subdomains-bitquark-top100000.txt -t 256 -fs 154

В итоге находим один поддомен, поэтому обновляем запись в файле /
:
10.10.11.13 runner.htb teamcity.runner.htb

Перед нами TeamCity 2023.05.3. В этой версии есть известная уязвимость CVE-2023-42793, которая позволяет атакующему обойти авторизацию, создав своего администратора в сервисе. Используем первый найденный в Google эксплоит и получаем логин и пароль добавленного в систему администратора.
python3 exploit.py -u http://teamcity.runner.htb -t token.txt

Теперь можно авторизоваться в TeamCity и осмотреться в сервисе.

Точка опоры
Первым делом просмотрим зарегистрированных пользователей и настройки бэкапов.


Теперь выполним бэкап и скачаем архив для анализа локально на своей машине.

Просматривая файлы в архиве, находим базу пользователей, в которой есть хеши паролей, а также какой‑то приватный ключ SSH.


После перебора имен пользователей TeamCity успешно логинимся по SSH с приватным ключом пользователя john
.
ssh -i id_rsa john@runner.htb

Продвижение
Теперь нам необходимо собрать информацию. Я буду использовать для этого скрипты PEASS.
Справка: скрипты PEASS
Что делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Privilege Escalation Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют систему на автомате и выдают подробный отчет о потенциально интересных файлах, процессах и настройках.
Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование. В выводе много всего, давай пройдемся по важным моментам.
Есть несколько портов, которые прослушиваются только для localhost.

В каталоге /
есть две интересные директории: containerd
и portainer
.

Portainer — это платформа для управления доставкой контейнерных приложений, которая может использоваться в сочетании со средами вроде Docker и Kubernetes.

Эта платформа доступна как раз на портах 9000 и 9443, которые прослушиваются на локальном хосте. Чтобы получить доступ к Portainer, нам нужно будет прокинуть порт 9000 на свой хост с помощью SSH.
ssh -i id_rsa john@runner.htb -L 9000:127.0.0.1:9080 -N
Теперь весь трафик, который мы пошлем на локальный порт 9000, будет туннелирован на порт 9080 указанного хоста (в данном случае 127.0.0.1) через хост SSH.

У нас нет никаких учетных данных для входа, поэтому я решил вернуться к найденной в бэкапах базе и перебрать сохраненные в ней хеши. Просто запуск hashcat приведет к тому, что утилита не сможет самостоятельно определить алгоритм хеширования и предложит несколько вариантов.
hashcat hashes.txt rockyou.txt

Выбираем самый популярный — режим 3200 — и повторяем брут.
hashcat -m 3200 hashes.txt rockyou.txt

Спустя минуту hashcat находит пароль пользователя matthew, от имени которого получается авторизоваться в Portainer.

Локальное повышение привилегий
Переходим в окружение Primary и на странице Dashboard смотрим, что нам доступно.

Первая идея, которая меня посетила, — проверить, нет ли уязвимости CVE-2024-21626 в runС младше версии 1.1.11. Этот баг позволяет выйти из контейнера.
RunC — это среда выполнения, которая применяется для создания и запуска контейнеров с разными параметрами изоляции, такими как пространства имен, контрольные группы, Linux Capabilities и так далее. Один из этих методов — использование отдельной файловой системы в качестве корневой ФС контейнера (это достигается через chroot).
Рассматриваемая уязвимость заключается в утечке файлового дескриптора, который может быть использован новым контейнером, чтобы создать рабочий каталог в пространстве имен хостовой файловой системы и тем самым обойти chroot. В данном случае утечка дескриптора происходит через /
, который содержит файловые дескрипторы текущего процесса, а runC, в свою очередь, создает дескриптор /
, доступный через /
по номерам 7, 8 или 9.
Таким образом, если при создании контейнера в качестве рабочего каталога указать дескриптор /
, то реальным рабочим каталогом контейнера станет хостовый путь /
. А это поможет дотянуться до любого файла хостовой файловой системы через вот такой обход путей:
../../../../
В Portainer уже есть два готовых образа, будем использовать один из них. На странице Images выбираем образ и на странице Image details копируем ID образа.


Затем переходим на страницу Containers и создаем контейнер. В поле Image указываем ID образа, а в расширенных настройках контейнера — рабочий каталог /
.


После старта контейнера подключаемся к нему и пробуем читать файл /
на хостовой системе.


По имени хоста видно, что мы вышли из контейнера и дотянулись до файла с хоста. Осталось получить последний флаг.

И машина захвачена!