Сэр, могли бы вы чуть пояснить на счёт передачи строк в область кучи с помощью питона?Доброго времени суток codeby. В предыдущих статьях мы работали с уязвимостями форматных строк, а в этой статье мы познакомимся с переполнением буфера в кучи и посмотрим на процесс эксплуатации как это работает.
Описание ExploitMe
Этот уровень знакомит с переполнением кучи и то, как это может повлиять на поток выполнения кода.
Этот уровень находится в / opt / protostar / bin / heap0
heap0, VM
Исходный код
C:#include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> struct data { char name[64]; }; struct fp { int (*fp)(); }; void winner() { printf("level passed\n"); } void nowinner() { printf("level has not been passed\n"); } int main(int argc, char **argv) { struct data *d; struct fp *f; d = malloc(sizeof(struct data)); f = malloc(sizeof(struct fp)); f->fp = nowinner; printf("data is at %p, fp is at %p\n", d, f); strcpy(d->name, argv[1]); f->fp(); }
Решение
И так рассмотрим исходный код данной программы и определимся с тем, что нам нужно сделать. Собственно суть задачи у нас сегодня такая, нам нужно вызвать мертвую функцию которая никогда не вызывается, функциюwinner(). В случае неудачи будет вызываться функцияnowinner(). Так же у нас определены две структуры данных в глобальной области памяти. Структураdataиfp. В свою очередь они имеют по одному элементу.
Структураdataимеет элементnameобъявленный как массив. А структураfpимеет элементfp, т.е. с таким же названием, и объявлен этот элемент, как целочисленный указатель на несуществующую функцию. Проще говоря это указатель ну функцию. Мы уже встречались с таким указателем. При вызовеfp, указатель будет вызывать любой адрес памяти.
С этими структурами мы и будем работать. Так же нужно сказать, что при описание какой-либо структуры, происходит объявление нового типа в языке. Теперь предлагаю посмотреть на код, который расположен в главной функции т.е. вmain().
Дальше у нас идет объявление переменных для структурdataиfp, а точней говоря объявление указателей структурного типа, для структурdataиfp. После чего происходит присвоение адресов памяти этим указателям. На начальные адреса, поскольку heap — это область динамической памяти, выделяемая на стадии исполнения программы.
Поговорим об этом подробнее…
Функцияmalloc()— это стандартная библиотечная функция в языке Cи, которая выделяет память из участка оперативной памяти, размером равным размеру структурыdataилиfpв области кучи во время выполнения, и возвращает пустой указатель(void *ptr)на это местоположение т.е. указатель на выделенный блок в памяти.
Но прежде, чем это произойдет, сначала происходит вычисление с помощью оператораsizeof, который подсчитает нужное количество байтов для выделения — функцииmalloc(). Соответственно выделится блок на 64 байта для структурыdataи блок в памяти на 4 байта для структурыfp.
Указатель на структуруfссылается на элемент в структуреfp, которому присваивается адрес функции nowinner
Код:f->fp = nowinner;
Затем происходит печать адресов
Код:printf(«data is at %p, fp is at %p\n», d, f);
Далее происходит копирование строки, полученную через аргумент. Копирование происходит в кучу.
Код:strcpy(d->name, argv[1]);
После чего происходит вызов функции nowinner.
Код:f->fp();
Теперь когда картина нам ясна приступим к эксплуатации.
Первым делом откроем программу под отладчиком и дизассемблируем функциюmain()после чего поставим точку останова на адрес возврата из функции. Чтобы программа при выполнении не завершилась.
Код:gdb -q ./heap0 disas main
Посмотреть вложение 37014
Код:break *0x08048500
Теперь запустим программу под отладчиком и скормим ей строку из символов «А».
Код:run AAAA
Посмотреть вложение 37015
Так как мы поставили точку останова на адрес возврата (RET) из функцииmain()и скормили программе символы «AAAA», теперь мы поможем посмотреть карту памяти процесса и найти кучу.
Код:info proc map
Посмотреть вложение 37016
Начальный адрес кучи найден —0x804a000. Теперь посмотрим в памяти на нашу кучу. Выведем 50 ячеек памяти в шестнадцатеричном формате от начального адреса кучи. Но правильнее будет говорить не 50 ячеек, хотя это тоже правильно, а называть их чанками, поскольку куча состоит из чанков.
Код:x/50x 0x804a000
Посмотреть вложение 37017
И мы видим нашу введенную строчку из символов «А», так же можем подсчитать сколько байтов нужно до следующей кучи. Если посмотреть, что расположено по адресу0x08048478мы увидим, что там адрес функцииnowinner().
Код:x/x 0x08048478
Посмотреть вложение 37018
Код:print &nowinner
Посмотреть вложение 37019
Видим, что адреса полностью идентичны. Теперь запустим программу под отладчиком и передадим ей 72 байта чтобы заполнить первую кучу и еще 4 байта, чтобы залезть на вторую кучу. Как и в стеке, память кучи аллоцируется ( распределяется ) непрерывно. Это означает, что если мы будем писать за пределами буфера, мы будем перезаписывать другую структуру данных Использовать будем python.
Код:run `python -c «print ‘A»*72 + ‘B’*4″`
Посмотреть вложение 37020
EIP перезаписан. Посмотрим теперь как это все выглядит в памяти.
Код:x/50x 0x804a000
Посмотреть вложение 37021
Видно, что мы перезаписали указатель на функцию, мы положили туда 4 байта из символов «B». Теперь посмотрим адрес функцииwinner(). После чего можно будет подставить этот адрес в место 4 байт из последовательности символов «B».
Код:print &winner
Посмотреть вложение 37022
Выходим из GDB —quit.
Составляем наш эксплойт и выполняем его.
Код:./heap0 $(python -c "from struct import pack; winner=pack('I',0x8048464); print 'A'*72 + winner")
Посмотреть вложение 37023
Вот таким не хитрым способом можно влиять на поток выполнения кода в программе используя переполнения буфера в куче. Многие наверно заметили, что подход тот же, что и при переполнение буфера в стеке. Да, это действительно так, поменялась лишь область памяти с которой мы работаем. Так же тут стоит сказать, что мы только-только начинаем знакомиться с уязвимостями в такой области памяти как куча (Heap). Впереди нас ждут такие уязвимости как "Write-what-where", "Use-After-Free", "Double Free". Ну и самое главное уровень пройден теперь можно переходить на следующий уровень.
Мы ведь передаём , скармливаем, стринг самой программе? При чем тут питон?
Комментарии
9