Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
В этой статье я расскажу об алгоритмах управления памятью в Linux, техниках heap exploitation и методах эксплуатации уязвимости Use-After-Free со всеми включенными механизмами защиты. А поможет мне в этом RopeTwo — одна из самых сложных машин с Hack The Box.
В статье «Разбираем V8» я описывал способ эксплуатации намеренной уязвимости в движке V8. Она позволила получить шелл, но, чтобы продвинуться дальше и получить флаг юзера, требуется решить новую сложную задачу — проэксплуатировать уязвимости работы с памятью.
Первым делом подключаемся к тачке по SSH и запускаем скрипт поиска уязвимостей для эскалации привилегий. Лично я предпочитаю использовать LinPEAS.
artex@kali:/home/artex/HTB/RopeTwo# ssh -i key [email protected]
artex@kali:/home/artex/HTB/RopeTwo# scp -i ssh/key linpeas.sh [email protected]:/tmp
chromeuser@rope2:/tmp$ chmod +x linpeas.sh
chromeuser@rope2:/tmp$ ./linpeas.sh > linpeas.txt
artex@kali:/home/artex/HTB/RopeTwo# scp -i ssh/key [email protected]:/tmp/linpeas.txt linpeas.txt
Смотрим внимательно отчет, анализируя каждую строчку. В разделе «Interesting Files — SUID» видим интересный файл — rshell.
Файлы с включенным битом SUID
Посмотрим внимательнее, что это.
Запускаем rshell
Похоже на restricted shell с включенным битом SUID, это явно наш пациент! Скачиваем и натравливаем на него «Гидру». Не буду приводить здесь весь листинг дизассемблированного кода, я вместо этого сделал упрощенную диаграмму с основной логикой функций rshell.
Диаграмма основных дизассемблированных функций
Как оказалось, это вовсе не restricted shell, а лишь его эмуляция. Нам доступно всего несколько команд: add, edit, rm, ls, echo, id и whoami. Самые интересные из них — первые четыре (позже выяснится, что три). Они позволяют создавать, изменять, удалять и отображать объекты («файлы») и выделять соответствующие им участки в памяти с определенным размером (не более 112 байт) и контентом. Причем команда ls выводит только имена файлов, без содержания. Что ж, все указывает на то, что впереди нас ждет heap exploitation.
В интернете много информации на эту тему, начать свое погружение в нее можно, например, с сайта Дхавала Капила. Первое, что нам нужно, — найти уязвимости в коде, которые можно проэксплуатировать.
На первый взгляд, никаких явных уязвимостей в коде нет. Везде используются либо безопасные функции, либо проверки размерности, а ввод терминируется нулем. Я потратил много времени, прежде чем нашел уязвимость под названием use after free (UAF).
Подробнее о том, что такое UAF, можно почитать, например, в блоге Orange Cyberdefense.
Наш эксплоит мы будем писать с помощью pwntools — незаменимой питоновской библиотеки для создания эксплоитов. Но первым делом нам нужно настроить окружение. Для этого, помимо самого rshell, необходимо скачать с машины RopeTwo библиотеки glibc (стандартная библиотека C, реализующая системные вызовы и основные функции, такие как malloc, open, printf) и ld (библиотека динамической линковки). Это необходимо для полной совместимости версий. Во‑первых, новые релизы glibc часто содержат изменения, устраняющие те или иные уязвимости, а во‑вторых, нам важно, чтобы смещения всех функций совпадали. Ниже приведена таблица версий glibc и возможности использования различных вариантов heap exploitation.
Популярные техники heap exploitation
Видим, что в нашем случае мы будем использовать версию libc 2.29.
Версия libc на сервере
Также в интернете необходимо найти и скачать версию libc 2.29 с отладочными символами, без них писать эксплоит крайне затруднительно. Думаю, с этим квестом ты справишься.
Теперь надо пропатчить наш rshell командой patchelf --set-interpreter ./ld-2.29.so rshell
, чтобы он стал использовать линкер нужной версии.
Теперь, если ты хочешь запустить rshell c нужной версией libc, используй команду
LD_PRELOAD='libc-2.29.so' rshell
Вывод checksec
Вывод checksec заставляет содрогнуться — включены все защитные механизмы! Но мы принимаем вызов!
Full RELRO. Глобальная таблица смещений (GOT) доступна только для чтения. Это означает, что мы не можем перезаписать в ней указатель функции, чтобы изменить ход выполнения программы.
Canary found. В стеке размещается «канарейка» (определенное значение, перезапись которого означает, что стек был изменен), поэтому переполнение стека нам не светит, если только мы не сможем каким‑либо образом заполучить контроль над «канарейкой».
NX enabled. Означает отсутствие областей в памяти, позволяющих одновременную запись и исполнение (RWX). Поэтому мы не можем разместить в адресном пространстве шелл‑код и запустить его.
PIE enabled. PIE — это аббревиатура от Position Independent Executable. Означает, что базовый адрес исполняемого файла меняется при каждом запуске, поэтому без утечки использовать ROP и ret2libc не получится.
Для начала напишем вспомогательные функции, эмулирующие пользовательский ввод.
def add(name, size, content="A"): io.sendlineafter('$ ', 'add '+str(name)) io.sendlineafter('size: ', str(int(size))) io.recvuntil("content: ") io.sendline(content)def edit(name, size, content="A"): io.sendlineafter('$ ', 'edit '+str(name)) io.sendlineafter('size: ', str(int(size))) if int(size) != 0: io.recvuntil("content: ") io.send(content)def rm(name): io.sendlineafter('$ ', 'rm '+str(name))
Тут сразу важно отметить, что в функции add
мы не можем использовать io.send(content)
, как в функции edit
, поскольку для записи контента add использует fgets
, а edit — read
. Поэтому воспользуемся методом io.sendline(content)
, который добавляет в конце перенос строки и головной боли нам (об этом дальше).
Бин — это корзина, в которую попадают освобожденные участки памяти (чанки). Представляет собой список освобожденных чанков, который бывает односвязный и двусвязный. Основное его назначение — быстрое выделение участка памяти (по статистике, в программах часто выделяются и освобождаются участки памяти одинаковых размеров). Вид связности списка зависит от того, в какой бин попал освобождаемый чанк, что, в свою очередь, зависит от его размера. Размер чанка увеличивается кратно 16 байтам (0x20 → 0x30 → 0x40…). Это значит, что младшие 4 бита поля размера чанка не используются. Вместо этого они содержат флаги состояния чанка:
PREV_INUSE
— установленный, означает, что предыдущий чанк используется, и наоборот;IS_MMAPPED
— означает, что этот чанк был выделен mmap()
;NON_MAIN_ARENA
— означает, что чанк не принадлежит main arena.Арена — это структура функции malloc
, содержащая бины для освобожденных из кучи чанков.
Максимальное количество арен для процесса ограничено количеством ядер процессора, которое доступно этому процессу.
На данный момент существует пять типов бинов (цифры приведены для 64-битных приложений):
Для наглядности выполним простой код, который создает и очищает два чанка по 0x30 байт каждый:
add(0, 0x28)add(1, 0x28)rm(0)rm(1)
Посмотрим, как это выглядит в GDB. Мы видим, что оба чанка попали в tcachebin 0x30. Структура tcachebin представлена на скриншоте.
Tcachebin в отладчике
Односвязный список можно представить так.
Односвязный список бина
А двусвязный — так (для small bins Size будет одинаковый, а для large bin еще добавятся указатели fd_nextsize
и bk_nextsize
).
Двусвязный список бина
fd
указывает на следующий чанк в списке, а bk
— на предыдущий.
С теорией покончено, теперь попробуем написать примитив произвольной записи, используя UAF. Мы не можем использовать технику Tcache Dup (Double Free), так как в glibc 2.29 появилась защита от нее.
Источник: xakep.ru