Переполнение буфера в heap. 1 серия
kas1e
Xakep, номер #057, стр. 057-074-4
Эти 4 функции и есть "костяк". Правда, существует и много других, работающих с heap вообще: cfree(), tmpnam(), getenv(), strdup(), atexit() и т.д. Для примера "распотрошим" malloc() функцию:
malloc() использует brk syscall (поднимает планку для хипа). Если либу компильнуть статически и отдебажить malloc, то станет видно, что после всех своих действий она вызывает этот самый brk(). malloc также и "выравнивающая функция". Причем, помимо обычных данных, под которые мы выделяем память, она добавляет некоторые другие значения: размер области, указатель на следующую область и т.д. (см. структуру malloc_chunk{}). Т.е. функция malloc выделяет несколько больше памяти, чем указано в ее аргументе. Посмотрим простенький пример:
#include <unistd.h>
int main()
{
int buf1, buf2;
buf1=malloc(0); // в первый буфер выделяем 0
buf2=malloc(0); // во второй буфер выделяем 0
printf("buf1 = %p\nbuf2 = %p\n",buf1,buf2); // покажем разницу в адресах
}
Компильнем и запустим:
[root@heap_part1]# gcc test_malloc.c -o test_malloc
[root@heap_part1]# ./test_malloc
buf1 = 0x8049660
buf2 = 0x8049670
[root@heap_part1]#
Вроде из heap мы выделили себе 0 байт, a автоматом произошло выравнивание и засовывание нужных самой malloc() данных. Эти самые данные представлены такой структурой:
#define INTERNAL_SIZE_T size_t
struct malloc_chunk
{
INTERNAL_SIZE_T prev_size;
INTERNAL_SIZE_T size;
struct malloc_chunk * fd;
struct malloc_chunk * bk;
};
Вот что находится в этой структуре:
- размер предыдущего куска
- размер текущего куска
- struct fd
- struct bk
Далее расположилось место непосредственно под переменную и метку окончания куска. Фактически, как некоторые уже наверно догадались, эксплуатация заключается в подделке этих malloc() данных. И вот теперь, когда мы выяснили, что такое heap и как с ним работают функции, рассмотрим реальный пример.
Классическое переполнение
Необходимо понять одну важную деталь: переполнение в heap - это многогранные вариации разных техник и переполнений. Например, используя переполнение в heap, но с применением "стековых" функций, мы сможем написать практически аналогичный стековому эксплоит, но в то же время он будет иметь отношение к heap. В этой же статье я буду рассматривать чисто "heap" переполнения (когда затираются данные malloc функции).
А теперь рассмотрим простейший пример:
/* ---heap_overflow_base.c--- */
/* переполнение буфера в heap */
#include <stdio.h>
main()
{
char *buf1;
char *buf2;
// создадим buf1 и buf2 в хипе (через malloc()) размером в 16 байт
buf1=(char *)malloc(16); // 16
buf2=(char *)malloc(16); // 16
// заполним вторую переменную 16 ascii символами 'A'
memset(buf2, 'A', 16);
// покажем содержимое buf2 до переполнения
printf("before overflow: buf2 = %s\n", buf2);
// заполним buf1 25 символами B (под первый буфер было отведено только 16 байт!)
// 25, т.к. 16 сам по себе буфер + 8 байт данные malloc() и 1 тот, который
// запрется на втором буфере (т.е. переполнит его)
memset(buf1, 'B', 25);
// покажем содержимое buf2 после переполнения