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

Мастерская бравого хакера

Крис Касперски

Хакер, номер #091, стр. 091-116-5


Доработка напильником

Shell-код имеет сложную структуру и обычно состоит из нескольких частей. Например, exploit milw0rm.com/exploits/1075, приложенный в файле 1075.с, использует целых 6 «иероглифических» массивов: dce_rpc_header1, tag_private, dce_rpc_header2, dce_rpc_header3, offsets, bind_shellcode. Первые пять — это служебные структуры, атакующие жертву, срывающие буферу крышу и передающие управление на bind_shellcode. Последний представляет собой «чистый» shell-код, который может быть беспрепятственно заменен любым другим. На самом деле, тут все не так просто, и произвола хоть отбавляй. Как минимум, необходимо убедиться, что мы используем shell-код, совместимый с атакуемой системой, и точки входа у них совпадают. Часто (но не всегда) точка входа расположена в самом начале shell-кода, реже — в его конце или середине. Гораздо хуже, если exploit написан «пионером», и все блоки идут одним большим кусом, внутри которого присутствует и shell-код.

Чтобы определить положение дел, необходимо преобразовать «иероглифический» текст в двоичный файл и дизассемблировать его. Разыскивать соответствующий конвертор совершенно необязательно. Проще переложить эту задачу на плечи компилятору Си, написав простенькую программку из нескольких строк.

Простейший конвертор для преобразования строковых констант в двоичный код

#include <stdio.h>

int main(void)

{

FILE *f;

if (f = fopen("shellcode", "wb"))

fwrite(shellcode, sizeof(shellcode), 1, f);

exit(0);

}

Сам shell-код должен быть размещен в массиве, объявленном как «char shellcode[]» (см. прилагаемый файл hex2bin.c) и приведенным к синтаксису Си (то есть, если shell-код выдернут из perl'а, необходимо удалить точки в конце строковых констант). Компилируем наш импровизированный конвертор, запускаем его на выполнение — и тут же на диске образуется файл «shellcode», который можно загрузить в HTE, IDA Pro или любой другой дизассемблер по вкусу, не забывая, конечно, переключить его в 32-битный режим.

В данном случае мы получим следующий код:

Первые 16-байт shell-кода содержат осмысленный код расшифровщика

00000000: 29C9 sub ecx,ecx

00000002: 83E9B0 sub ecx,-050 ;"P"

00000005: D9EE fldz

00000007: D97424F4 fstenv [esp][-000C]

0000000B: 5B pop ebx

0000000C: 81731319F50437 xor d,[ebx][00013],03704F519 ; "7?o?"

00000013: 83EBFC sub ebx,-004 ; "?"

00000016: E2F4 loop 00000000C (1)

Ага! Вполне типичный расшифровщик. Значит, точка входа в shell-код действительно находится в начале массива, и он может быть беспрепятственно заменен любым таким же. Если же вместо осмысленного кода нас встречает мусор, то нужно последовательно отступать на один байт до тех пор, пока мы не получим что-то удобоваримое. Естественно, для этого необходимо знать ассемблер и хотя бы в общих чертах представлять себе устройство операционной системы.

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