Предлагаю вникнуть в бинарные уязвимости и эксплойтостроение на примере просто ROP (return oriented programming) – одного из методов эксплуатации уязвимостей в ПО. Это достаточно мощный инструмент, с помощью которого можно обойти различные методы защиты. Конкретно мы будем разбираться с гаджетами и ROP-цепочками.Что такое ROP-гаджетыПо умолчанию гаджет завершается RET (это инструкция возврата), которая расположена в ОЗУ в коде программы или распределяемой библиотеки. В процессе кибератаки мошенники делают так, чтобы все гаджеты выполнялись через инструкции возврата, а для этого они определенным образом формируют их цепочки (последовательности) – их и называют ROP-цепочками или ROP-chain.Вот так могут выглядеть гаджетыДопустим, мы возьмем необходимые нам гаджеты в цепочку. Ее отправим в стек и добьемся выполнения любого кода. Код может быть вообще любым, но нам нужно вызвать шелл (оболочку). Под linux это делается с помощью execve() (аналог system()) – его и будем применять для создания системного вызова.Linux System CallsСистемные вызовы помогают получить определенные системные функции. Полный их список с присвоенными номерами вызываем следующим образом:Зачем нужен номер? Зная его, мы сможем вызвать конкретную системную функцию:unistd.h представляет собой файл заголовка, через который мы получаем доступ к API нашей ОС. Его содержимое можно увидеть через простой редактор nano.Системные вызовы для x86 и x64Что содержится в unistd_32.h?В unistd_32.h ровно 336 системных вызоваВидим, что перед нашим системным вызовом стоит 11.Идем дальше – прототип системного вызова execve().Прототип системного вызова 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.Запускаем клиент WinSCPRSA-ключИщем в папке 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.Ищем xor eax5. Снова нужен EAX, но сейчас записываем туда 11.Ищем inc eaxСейчас в EAX записано значение 0, с помощью инкрементации увеличиваем его до 11.6. Смотрим, что с EBX.Обращаемся к EBX7. Добавляем 'bin/sh'.8. Вызываем int 0x80.Прерывание int 0x809. Суммируем и печатаем.Вместо адреса system() подставляем адрес сишной библиотеки. Нам нужен адрес, по которому мы искали гаджеты. Используем "info proc map" – эта команда позволит увидеть адресное пространство и понять, где libc загружен в память.Находим адрес libcАдрес выяснили – libc загружен по адресу 0xb7e97000.И теперь код эксплойта полностью. Как проверить, что все верно?Эксперимент завершился успешноУра, root получили! Наш эксплойт целиком состоит из цепочки гаджетов ROP-chain. Об их классификации можно узнать здесь, а про ROP – здесь.Больше интересных тем по кибербезопасности вы найдете на нашем форуме https://codeby.net. Всех желающих приглашаем на обучение – преподаватели нашей школы разработали курс по анонимности и безопасности в интернете. Узнать подробности и записаться: вам сюда.