В данной статье разберемся с уязвимостью переполнение буфера в куче, а также решим 19-е задание с сайта pwnable.kr.
- PWN;
- криптография (Crypto);
- cетевые технологии (Network);
- реверс (Reverse Engineering);
- стеганография (Stegano);
- поиск и эксплуатация WEB-уязвимостей.
Вдобавок к этому я поделюсь своим опытом в компьютерной криминалистике, анализе малвари и прошивок, атаках на беспроводные сети и локальные вычислительные сети, проведении пентестов и написании эксплоитов.
Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.
Как организована куча
Память может быть занятой (аллоцированной) и свободной. На рисунке представлена динамическая память.
- SSize — размер предыдущего блока памяти, при условии что он свободен.
- Size — размер данного блока памяти, к которому прибалены 2 бита состояния.
- Data — польовательские данные.
- Fd — указатель на следующий свободный блок.
- Bk — указатель на предыдущий свободный блок.
- Free — свободная память.
Таким образом никакие два свободных блока не могут являться соседями. Плюс ко всему, на границе занятой и свободной системной памяти есть специально обрабатываемый свободный W-блок.
Представление блоков в списки (корзины) происходит следующим образом.
Для удаления свободного блока из списка применяется метод unlink.
void unlink(S, BK, FD){
BK = S->bk;
FD = S->fd;
FD->bk=BK;
FD->fd=FD;
}
Выделение и освобождение памяти
Разберем как работает mmap. На первом шаге происходит проверка массивов необходимых размеров (к примеру 24 байт). Если есть необходимый блок, то он отделяется с помощью unlink.
На втором шаге, если данный блок достаточно большой, то он делится на две части. Первая часть аллоцируется, а вторая перераспределяется в другой массив.
На третьем шаге, если не было выделена блока необходимого размера, то проверяется W-блок. Есои он удовлетворяет, то с ним проводятся мероприятия шага два. Если W-блок окаался мал, то для расширения доступной памяти испольуются sbrk() и mmap(). Метод Free полность противоположен mmap.
Переполненик буфера в куче
Переполнение кучи — это тип переполнения буфера, который происходит в области данных кучи. Память в куче динамически распределяется приложением во время выполнения и обычно содержит данные программы. Эксплуатация выполняется путем повреждения этих данных особым образом, чтобы приложение перезаписывало внутренние структуры, такие как указатели на связанный список. Метод канонического переполнения кучи перезаписывает связь динамического выделения памяти (например, метаданные malloc) и использует обмен указателями для перезаписи указателя на программную функцию.
Как пример, в функции Unlink, с помощью FD->bk можно изменить значение произвольного слова в памяти. К примеру, положить шеллкод изменить адрес GOT. Пример переполнения рассмотрим на примере.
Решение задания unlink
Нажимаем на первую иконку с подписью unlink, и нам говорят, что нужно подключиться по SSH с паролем guest.
При подключении мы видим соответствующий баннер.
Давайте узнаем, какие файлы есть на сервере, а также какие мы имеем права.
Давайте посмотрим исходный код.
Так у нас объект B связывается с объектами А и С. Потом происходит ввод и заполнение объекта А. Имея связь объетов А — B — C и контролируя заполнение А, мы можем переполнить кучу и перезаписать объекты В и С. Необходимо найти способ эксплуатации уязвимости. Посмотрим через gdb.
Таким образом, можно записать шеллкод и переписать адрес возврата из main на наш шеллкод. Адрес возврата попадает в esр из регистра ecx, куда податает из ebр-4. Дизассемблируя функцию unlink, замечаем, что ebр-4 можно контролировать пользовательким вводом.
Разберем, как расположены наши объекты в памяти и как работает функция unlink. Каждый объект занимает в памяти 16 байт (по 4 на указатели и 8 на буффер).
Выделение памяти под объекты происходит в строках main+38, main+54 и main+70. Перед каждым вызовом укаатель на стек (esр) увеличивается на 16 и уменьшается на 12, после чего резервируется 16 байт.
То есть между структурами есть свободные 4 байта.
Далее производится линковка и разлинковка объектов.
Таким образом, нам нужно, чтобы при возврате из функции адрес попадет на место расположения heaр+12, которая передаст управления по адресу, где расположен шеллкод.
from pwn import *
s = ssh("unlink", "pwnable.kr", password="guest", port=2222)
ex = s.process("./unlink")
shell_address = p32(0x80484eb)
ans = ex.recv().split("n")
stack_addr = p32(int(ans[0].split(" ")[5],16) + 16)
heap_addr = p32(int(ans[1].split(" ")[5],16) + 12)
payload = shell_address + "A"*12 + heap_addr + stack_addr
ex.sendline(payload)
ex.interactive()
Получаем шелл, читаем флаг, получаем 10 очков.
Вы можете присоединиться к нам в Telegram. В следующий раз разберемся с переполнением кучи.
Автор: RalfHacker