Логотип
Главная | Статьи | Heap Exploitation на Linux: как захватить кучу через use-after-free
Heap Exploitation на Linux: как захватить кучу через use-after-free

Heap Exploitation на Linux: как захватить кучу через use-after-free

16 июля, 2025

56

Эй, хакеры! Сегодня мы вскроем одну из самых сочных тем в бинарной эксплуатации — Heap Exploitation. Забудьте про скучные лекции. Мы разберем, как работает куча в Linux, найдем уязвимость Use-After-Free (UAF) и напишем эксплойт, который даст нам заветный шелл. Хардкор, код и чистая практика. Погнали.

Глава 1: Что такое куча и как с ней работает ptmalloc

Когда твоя программа просит память на лету, она не берет ее из воздуха. Она обращается к ядру за большим куском, а потом менеджер памяти (аллокатор) нарезает его на части по запросу. В мире glibc (стандартной библиотеки C в Linux) этим заведует ptmalloc (pthreads malloc).

Как это работает на пальцах:

  1. malloc(size): Ты просишь size байт. ptmalloc ищет у себя в закромах подходящий свободный кусок (чанк). Если находит — отдает тебе указатель. Если нет — просит еще памяти у ОС.
  2. free(ptr): Ты говоришь: “Больше не нужно”. ptmalloc не возвращает память ядру сразу. Он помечает чанк как свободный и кладет его в специальный “контейнер” — бин (bin). Так он может быстро переиспользовать этот чанк для следующего malloc.

Чанки и Бины — основа основ

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

Свободные чанки хранятся в бинах, отсортированные по размеру. Для нас сейчас важны два типа:

  • Tcache (Thread-Local Caching): Появился в glibc 2.26. Это быстрый кэш для небольших чанков на каждый поток. Работает по принципу LIFO (Last-In, First-Out). Последний освобожденный чанк будет первым выделен при следующем malloc того же размера. Это наша главная точка атаки.
  • Fastbins: Предшественник tcache, тоже LIFO-список для маленьких чанков.

Ключевая идея для эксплойта: когда чанк попадает в tcache, в его пользовательскую область записывается указатель (fd — forward pointer) на следующий свободный чанк в этом же бине. Если мы сможем контролировать данные в освобожденном чанке, мы сможем подменить этот указатель.

Глава 2: Use-After-Free — Дверь в Зазеркалье

Use-After-Free (UAF) — это классика. Ошибка настолько простая в своей сути, насколько и смертоносная.

  1. Ты выделяешь память и сохраняешь указатель на нее.
  2. Ты освобождаешь эту память с помощью free().
  3. Ты (или кто-то другой) по ошибке снова используешь старый, “висячий” указатель для чтения или записи.

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

Глава 3: Практика. Пишем уязвимый код

Хватит теории. Вот наш подопытный — простая программа на C, имитирующая работу с пользователями. В ней заложена бомба замедленного действия.

vuln.c

Компилируем без защит:

gcc -o vuln vuln.c -no-pie -fno-stack-protector

  • -no-pie: Отключает Position-Independent Executable, чтобы адреса функций (вроде win) были фиксированными.
  • -fno-stack-protector: Отключает защиту стека, на всякий случай, хотя мы ее тут и не трогаем.

Глава 4: Эксплойт. Захватываем кучу и получаем шелл

Теперь самое интересное. Как превратить эту UAF в шелл?

План атаки:

  1. Создать пользователя (пункт 1). malloc выделит чанк под struct User. В нем будет имя “guest” и указатель на normal_func.
  2. Удалить пользователя (пункт 2). free(user) освободит чанк. Он попадет в голову tcache-бина для своего размера. Указатель user теперь “висячий”, но все еще указывает на это место.
  3. Записать данные (пункт 4). Мы вызываем malloc с тем же размером sizeof(struct User)ptmalloc (благодаря tcache) немедленно вернет нам тот же самый чанк, который мы только что освободили.
  4. Сформировать пейлоад. Функция read попросит нас ввести данные. Мы должны ввести специальным образом подготовленную строку, которая:
    • Первые 16 байт — любое имя, это неважно.
    • Следующие байты (в зависимости от архитектуры, обычно 8) — это адрес нашей функции win(). Мы перезапишем старое значение user->login_func.
  5. Залогиниться (пункт 3). Мы вызываем login(). Программа использует “висячий” указатель user, который теперь указывает на наши данные. Она доходит до user->login_func() и… вызывает win(), потому что мы подменили указатель!

Пишем эксплойт на Python с помощью pwntools

Это удобнее, чем делать вручную. Сначала нам нужно узнать адрес функции win.

objdump -t vuln | grep win

Вывод будет примерно таким: 00000000004011e6 g F .text 0000000000000029 win
Наш адрес — 0x4011e6.

exploit.py

Запускаем эксплойт:

python3 exploit.py

Если все сделано правильно, ты увидишь заветное:

Вот и все. Мы только что использовали логическую ошибку в коде, чтобы обмануть менеджер памяти, перезаписать указатель на функцию и выполнить произвольный код. Это и есть вся суть heap exploitation — контроль над хаосом выделения памяти. Теперь у тебя есть база. Экспериментируй, копай глубже, изучай другие техники (tcache poisoning, fastbin attack, unsorted bin attack) — и ломай все, что движется. Удачи.