solutions

Разбор HabyBeap с VolgaCTF 2022 Qualifier

В середине мая состоялся отборочный этап VolgaCTF 2022, бинарные задания для которого делала команда SPRUSH из МИФИ. Сегодня разберём решение одного из этих тасков.


Судя по описанию таска и интерфейсу бинарного файла, перед нами типичная задача на эксплуатацию кучи под Linux с glibc. Но версия libc одна из последних, и в ней есть новая защита, которая реализует XOR с указателем, находящимся в освобождённом чанке. 

Посмотрим что находится внутри бинаря, и найдём все примитивы, которые нам доступны.


Перед нами довольно хороший набор для работы с кучей. Мы можем выделять, удалять, редактировать и просматривать чанки. Следующий шаг, который нам надо сделать — это понять, можем ли мы контролировать размеры создаваемых чанков.


Чанки создаются с размерами 0х79 и 0х68, а создать их можно всего 16. Также можно заметить, что у нас есть переполнение на куче из-за того, что вне зависимости от размера создаваемого чанка, в него можно записать 0х78 байт. Но для эксплуатации нам этот примитив не понадобится.

Посмотрим на реализацию функций работы с уже созданными чанками. Например, как происходит освобождение.


Сразу же в функции освобождения находим первую полезную для нас ошибку. После освобождения памяти указатель в глобальном массиве не зануляется. Значит, потенциально мы сможем им воспользоваться. Посмотрим, сможем ли читать данные в освобожденных чанках.


Отлично, у нас есть возможность читать данные до нуль-байта в освобожденных чанках. С помощью этой уязвимости мы можем получить утечку libc, а также утечку XOR-ключа для преобразования адресов в чанках. Посмотрим также код редактирования чанка:


Мы можем редактировать всего 6 байт в начале чанка, а также можем делать это для освобожденных чанков. Данного количества байт нам вполне хватит для эксплуатации.

💡 Идея эксплуатации:

  1. Выделить чанк, освободить его и получить XOR-ключ (подробнее про эту технику защиты кучи — https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/),
  2. Заполнить tcache большими чанками, чтобы положить очередной чанк в unsorted bin и получить утечку libc (unsorted bin libc leak),
  3. Сделать tcache-poisoning и выделить чанк на __free_hook,
  4. В выделенный чанк записать адрес функции system и освободить заранее созданный чанк со строкой /bin/sh.

Реализуем всё это.

Для начала сделаем утечку libc:

В предоставляемой версии libc утечка из чанка в unsorted-bin’e содержит нуль-байт в младшем байте, что не позволит так просто прочитать её через puts. Для этого мы запишем 1 не нулевой байт в освобождённый чанк.

Далее получим xor-ключ.

Его можно получить в исходном виде из первого освобождённого чанка, так как он будет указывать на NULL, потому что до него не было никаких чанков в корзине.

Делаем tcache-poisoning, используя ключ для XOR'a.

Запускаем наш эксплоит:
PWN