ROP-цепочки и гаджеты: учимся разрабатывать эксплойты
Предлагаю вникнуть в бинарные уязвимости и эксплойтостроение на примере просто ROP (return oriented programming) – одного из методов эксплуатации уязвимостей в ПО. Это достаточно мощный инструмент, с помощью которого можно обойти различные методы защиты. Конкретно мы будем разбираться с гаджетами и ROP-цепочками.
Что такое ROP-гаджеты
По умолчанию гаджет завершается RET (это инструкция возврата), которая расположена в ОЗУ в коде программы или распределяемой библиотеки. В процессе кибератаки мошенники делают так, чтобы все гаджеты выполнялись через инструкции возврата, а для этого они определенным образом формируют их цепочки (последовательности) – их и называют ROP-цепочками или ROP-chain.
Допустим, мы возьмем необходимые нам гаджеты в цепочку. Ее отправим в стек и добьемся выполнения любого кода. Код может быть вообще любым, но нам нужно вызвать шелл (оболочку). Под linux это делается с помощью execve() (аналог system()) – его и будем применять для создания системного вызова.
Linux System Calls
Системные вызовы помогают получить определенные системные функции. Полный их список с присвоенными номерами вызываем следующим образом:
Зачем нужен номер? Зная его, мы сможем вызвать конкретную системную функцию:
unistd.h представляет собой файл заголовка, через который мы получаем доступ к API нашей ОС. Его содержимое можно увидеть через простой редактор nano.
Что содержится в unistd_32.h?
Видим, что перед нашим системным вызовом стоит 11.
Идем дальше – прототип системного вызова execve().
Filename – указатель на строку (у нас это путь к двоичному файлу ptr для "/bin/sh").
ARGV[] – перечень аргументов программы (отсутствуют, потому ставим 0).
Envp[] – аргумент параметров среды (снова 0).
Получаем:
Ассемблер
Разберемся, как у него обстоят дела с командами и регистрами.
Основные регистры:
EIP – служебный регистр, указывает на адрес текущей исполняемой инструкции;
ESP – хранит указатель на вершину стека;
EBP – указатель на фрейм, применяется для адресации данных в стек;
EAX – используется как универсальное хранилище при накоплении данных;
EBX – хранит адреса в памяти (регистр общего назначения, хранит любые данные в оперативной памяти);
ECX – применяется для организации циклов. Перед циклом в него заносят необходимое количество итераций, каждая команда loop уменьшает значение этого регистра на единицу. При достижении нуля цикл завершается;
EDX – аналог EAX, тоже универсальный аккумулятор значений, часто используется для умножения и деления (DIV, SUB).
Последние четыре регистра используются для хранения определенных значений.
Есть еще 6 с аргументами системных вызовов: EBX, ECX, EDX, ESI, EDI и EBP. Если аргументов 7 и больше, ячейка памяти первого сохраняется в EBX.
Снова возвращаемся к execve(). Для ее вызова нужно придумать, где хранить аргументы. Используем для этого те самые шесть регистров.
Для самого системного вызова нам понадобится int 0x80 (80h), где int – прерывание, а 0х80 – его номер.
Обработкой прерываний в linux занимается его ядро, оно же отвечает за выполнение системных вызовов другими программами. Ядро получает сведения о том, какой вызов хочет сделать определенная программа после того, как проанализирует содержимое EAX (туда записывается номер системного вызова).
Чтобы сделать системный вызов:
- перемещаем его номер в EAX (у нас 11);
- сохраняем аргументы в регистрах EBX, ECX и EDX;
- делаем int 0x80 -> EAX (11) => run execve().
Получаем:
Дальше находим гаджеты, которые соответствуют нашим критериям, и собираем ROP-цепочку.
Формируем ROP-chain
Искать подходящие гаджеты удобно с помощью специальных инструментов. Их много, причем одни просто частично автоматизируют работу, а другие умеют сами формировать ROP-цепочки. Я предлагаю использовать в работе ROPgadget или онлайн-сервис ROPShell. Для нашего примера я выбираю второй вариант.
У нас есть два способа: найти гаджеты в программе stack7 или в библиотеке libc. И снова второй вариант лучше, так как чем «тяжелее» программа, тем больше в ней обнаружится гаджетов, хотя не факт, что нужные там будут.
Найдем, какие библиотеки привязаны к нашей программе, с помощью утилиты ldd.
Где находится библиотека?
Пока видим только ярлык, но сама библиотека располагается там же и называется libc-2.11.2.so. Скачиваем ее и загружаем в онлайн-сервис. Для скачивания рекомендую WinSCP – свободно распространяемый графический клиент SFTP под Windows.
Ищем в папке libc-2.11.2.so и загружаем его.
Со своего компьютера загружаем скачанный файл в онлайн-сервис ROPShell. Как это выглядит – смотрим здесь.
Теперь возьмем некоторые фрагменты из ранее написанного эксплойта.
Нам нужна часть кода.
По результатам анализа библиотеки получаем такую картину:
Чтобы сформировать ROP-последовательность, найдем здесь три инструкции:
pop [register] – переносит данные с вершины стека в [register];
xor eax, eax; – записывает в EAX ноль;
inc eax; – добавляет к содержимому EAX на единицу.
Теперь переходим к конструированию ROP-эксплойта.
1. Переполняем буфер на 80 байтов. offset eip.
2. Переходим на RET address.
3. Обнуляем содержимое регистров ECХ и EDX.
Ищем гаджеты с нужными нам инструкциями. Пишем в ROPShell:
Подставляем четырехбайтное значение '\x00\x00\x00\x00', обнуляем содержимое регистра ECX.
И еще:
pop edx; ret
'\x00\x00\x00\x00' в регистр EDX записываем 0.
Одного гаджета с обеими инструкциями "pop ecx; ret" & "pop edx; ret" мы не нашли, продолжаем поиски.
Значения подставляем такие, которые соответствуют выбранным гаджетам.
4. В регистр EAX записываем 0.
Пишем xor, eax в ropshell.
5. Снова нужен EAX, но сейчас записываем туда 11.
Сейчас в EAX записано значение 0, с помощью инкрементации увеличиваем его до 11.
6. Смотрим, что с EBX.
7. Добавляем 'bin/sh'.
8. Вызываем int 0x80.
9. Суммируем и печатаем.
Вместо адреса system() подставляем адрес сишной библиотеки. Нам нужен адрес, по которому мы искали гаджеты. Используем "info proc map" – эта команда позволит увидеть адресное пространство и понять, где libc загружен в память.
Адрес выяснили – libc загружен по адресу 0xb7e97000.
И теперь код эксплойта полностью.
Как проверить, что все верно?
Ура, root получили! Наш эксплойт целиком состоит из цепочки гаджетов ROP-chain. Об их классификации можно узнать здесь, а про ROP – здесь.
Больше интересных тем по кибербезопасности вы найдете на нашем форуме https://codeby.net. Всех желающих приглашаем на обучение – преподаватели нашей школы разработали курс по анонимности и безопасности в интернете. Узнать подробности и записаться: вам сюда.