Переполнение буфера в heap. Серия вторая

kas1e

Xakep, номер #059, стр. 059-074-2


Глобальная таблица смещений также связана и с PLT (Procedure Linkage Tables), у которой тоже свои приколы, и чтобы их понять, желательно почитать ELF спецификацию. GOT не имеет как таковой стандартной структуры, поскольку она процессоро-семантичная. Это значит, что на x86 это так, на mips так, а на vax это вот так. Если же говорить о x86, то нулевая запись в таблице зарезервирована под динамические структуры и ссылается по символу _DYNAMIC. Записи первая и вторая также зарезервированы (зачем - читай ELF спецификацию :)).

Для чего я писал про эту таблицу, надеюсь, понятно. Ведь нам нужно каким-то образом находить адрес free() функции. Делаться это будет старым дедовским способом, а именно через objdump:

[root@heap_part2]# objdump -R ./vuln | grep free

08049570 R_386_JUMP_SLOT free

Конечно, все будет несколько проще, если используется статическая компиляция, но это очень маловероятно, особенно в повседневной жизни. В общем, теперь самое время написать наш суперкод :).

Непосредственно эксплуатация

Для начала слегка видоизменим уязвимую программу из первой части статьи, чтобы стало ясно, как наша уязвимость может быть использована злодеями. Изменим ее, добавив strcpy функцию. Но пусть эта функция не вводит тебя в заблуждение своей привычностью в стековых переполнениях. В данном случае мы используем ее непосредственно для переброски в буфер данных, расположенных в heap (в принципе ничего удивительного :)). Соответственно, и переполнение у нас "чистое" и самое что ни на есть "классическое". Убираем все memset() и прочее, делаем подобие работоспособной процедуры, работающей с аргументами:

#include <stdio.h>

main(int argc, char * argv[])

{

char *buf1;

char *buf2;

// сделаем buf1 и buf2 в хипе (через malloc())

buf1=(char *)malloc(60); // 60

buf2=(char *)malloc(0); // можно и 0

// заполняем buf1 первым аргументом

strcpy(buf1, argv[1]);

// освобождаем выделенную память под buf1

// здесь и происходит ключевой момент

free(buf1);

// освобождаем выделенную память под buf2

free(buf2);

}

Компилим (gcc heap_overflow.c -o heap_overflow) и пробуем (./heap_overflow `perl -e 'print "B" x 59'`). В ответ - пустота. Никаких проблем. 60 байт у нас есть. Теперь кинем на пару байт больше:

./heap_overflow `perl -e 'print "B" x 61'`

Появится сообщение "Segmentation fault". Вот оно! Если посмотреть через gdb, то опять же видно, что проблема возникает при вызове функции free (chunk_free()):

[root@heap_part1]# gdb -q ./heap_overflow

(gdb) r

Starting program: /heap_part2/heap_overflow

Program received signal SIGSEGV, Segmentation fault.

0x4207acc0 in chunk_free () from /lib/i686/libc.so.6

(gdb) quit

The program is running. Exit anyway? (y or n)

[root@heap_part1]#

Рассмотрим более детально, что же происходит с heap в нашем случае. У нас есть 2 буфера, и вот как они располагаются:

HEAP

|-------------------------------------------|

| 1. начальные данные malloc |

Назад на стр. 059-074-1  Содержание  Вперед на стр. 059-074-3