В этой статье я расскажу об алгоритмах управления памятью в Linux, техниках heap exploitation и методах эксплуатации уязвимости Use-After-Free со всеми включенными механизмами защиты. А поможет мне в этом RopeTwo — одна из самых сложных машин с Hack The Box.
В статье «Разбираем V8» я описывал способ эксплуатации намеренной уязвимости в движке V8. Она позволила получить шелл, но, чтобы продвинуться дальше и получить флаг юзера, требуется решить новую сложную задачу — проэксплуатировать уязвимости работы с памятью.
Первым делом подключаемся к тачке по SSH и запускаем скрипт поиска уязвимостей для эскалации привилегий. Лично я предпочитаю использовать LinPEAS.
artex@kali:/home/artex/HTB/RopeTwo# ssh -i key chromeuser@10.10.10.196
artex@kali:/home/artex/HTB/RopeTwo# scp -i ssh/key linpeas.sh chromeuser@10.10.10.196:/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 chromeuser@10.10.10.196:/tmp/linpeas.txt linpeas.txt
Смотрим внимательно отчет, анализируя каждую строчку. В разделе «Interesting Files — 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.
Видим, что в нашем случае мы будем использовать версию libc 2.29.
Также в интернете необходимо найти и скачать версию libc 2.29 с отладочными символами, без них писать эксплоит крайне затруднительно. Думаю, с этим квестом ты справишься.
Теперь надо пропатчить наш rshell командой patchelf --set-interpreter ./ld-2.29.so rshell
, чтобы он стал использовать линкер нужной версии.
Теперь, если ты хочешь запустить rshell c нужной версией libc, используй команду
LD_PRELOAD='libc-2.29.so' rshell
Вывод 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 представлена на скриншоте.
Односвязный список можно представить так.
А двусвязный — так (для small bins Size будет одинаковый, а для large bin еще добавятся указатели fd_nextsize
и bk_nextsize
).
fd
указывает на следующий чанк в списке, а bk
— на предыдущий.
С теорией покончено, теперь попробуем написать примитив произвольной записи, используя UAF. Мы не можем использовать технику Tcache Dup (Double Free), так как в glibc 2.29 появилась защита от нее.
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
1 год7690 р. |
1 месяц720 р. |
Я уже участник «Xakep.ru»
Читайте также
Последние новости