Основы разработки шеллкодов для Windows. Часть первая.

evilcore

CD-диск
Пользователь
Регистрация
03.10.2018
Сообщения
18
Оценка реакций
7
Баллы
10
В этой статье рассматривается техническая разработка шеллкодов. Понимание как они создаются и работают поможет вам написать собственный шеллкод. Также, вы сможете модифицировать уже готовые эксплоиты.
Для ее понимания желательно иметь опыт (хотя бы небольшой) программирования на каком-нибудь компилируемом нативном языке (Си, Паскаль, в идеале - Ассемблер). Скрипты не считаются.
Часть первая рассматривает самые основы, ничего нового и эксклюзивного вы тут не найдете, но она нужна для понимания дальнейших материалов.

Вступление
Допустим, у вас есть рабочий эксплоит для Internet Explorer или Flash Player, который открывает калькулятор винды. Практической пользы с этого, конечно же, мало. Хочется добавить туда еще какой-то функционал , скажем, выполнения команд или скачивание других файлов из инета. Можно, конечно, заюзать что-нибудь найденное в интернете, или сгенерировать код с помощью модуля Метасплойта msfvenom. Но, для начала, желательно понимать, как устроен шеллкод и как именно он работает.

Для тех, про первый раз слышит этот термин, процитирую Вики: шелл-код (англ. shellcode, код запуска оболочки) — это двоичный исполняемый код, который обычно передаёт управление командному процессору, например '/bin/sh' в Unix shell, 'command.com' в MS-DOS и 'cmd.exe' в операционных системах Microsoft Windows. Шелл-код может быть использован как полезная нагрузка эксплойта, обеспечивающая взломщику доступ к командной оболочке (англ. shell) в компьютерной системе.

Короче говоря, шеллкод это часть бинарного кода, который мы можем использовать как пейлоад (полезную нагрузку) для эксплойта. Что такое бинарный код? Для начала, посмотрите на этот код на языке Си:

C:
#include <stdio.h>
 
int main()
{
    printf("Hello, World!\n");
    return 0;
}
(Не закрывайте вкладку со статьей, дальше все не настолько плохо))

На Ассемблере это выглядит примерно так:
Код:
_main PROC
    push ebp
    mov ebp, esp
    push OFFSET HelloWorld ; "Hello, World!\n"
    call _printf
    add esp, 4
    xor eax, eax
    pop ebp
    ret 0
_main ENDP
Следует отметить, здесь есть главная процедура программы main, и вызов функции printf(которая выводит строку). Этот же код выглядит на бинарном уровне так:


Т.е. "55 8B EC 68 00 B0 33 01 ..." это бинарный код нашей программы.

Как использовать шеллкод в эксплоите?
Возьмем в качестве примера простой эксплойт, уязвимость переполнения буфера (на стеке).

C:
void exploit(char *data)
{
    char buffer[20];
    strcpy(buffer, data);
}
Что делает эта программа? Она выделяет на стеке (это такая область памяти) буфер размером 20 байт, и копирует туда строку, на которую указывает параметр функции. Фунция strcpy никак не проверяет размер данных, соответственно, мы можем использовать эту уязвимость.
Основная идея заключается в следующем: нужно отправить приложению строку размером более 20 байт, которая содержит ваш шеллкод. Функция запишет 20 байт в буфер, а все остальное (ваш шеллкод) будет помещено в стек программы. Ваша строка перезапишет кусок важных данных в стеке (например, сохраненный EIP или указатель функции) с пользовательским адресом памяти. Выполнение программы переместится на ваш шеллкод из стека и начнет выполнять именно его, а не код, который изначально вложил в программу ее автор. Но для этого нужно понимание, как именно работает память, адресация и прочие моменты, иначе вы получите только падение программы с неизвестной для юзера ошибкой.

Специфические аспекты шеллкода
Было бы слишком просто написать код, делающий то, что нам нужно, скомпилировать и использовать как шеллкод. Существую некоторые специфические аспекты, которые отличают шеллкод от обычной программы:
1. Мы не можем использовать абсолютные смещения для строк
2. Мы не знаем адреса функций (например, printf)
3. Мы должны избегать некоторых конкретных символов(например, нулл-байтов)

Рассмотрим все это подробней
1. В коде на С/С++ вы можете объявить строку как глобальную переменную, которая будет находится в секции .data , по какому-то там (выделенному компилятором) адресу.



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

2. В C/C ++ легко вызвать функцию. Мы указываем компилятору директиву #include <имя> для использования определенного хидера и вызываем функцию по ее имени. Компилятор и линкер делают всю работу за нас: они находят адреса функций в библиотеках, добавляя их к нашему проекту, позволяя не задумываться о том, что "скрыто под капотом".


В шеллкоде такой халявы нет. Мы не знаем, загружена ли DLL, содержащая нашу функцию, в память, и мы не знаем адрес нашей функции. Теоретически, можно посмотреть адреса функций на своей ОС и заранее записать их в шеллкод. На практике, эти адреса отличаются от версии к версии винды, даже от сервиспака к сервиспаку. Кроме того, в версиях выше ХР (т.е. , можно сказать, везде) существует такая вещь, как ASLR (рандомизация адресного пространства), которая загружает системные DLL каждый раз по новому адресу.

Т.е. мы должны загрузить DLL в память и найти необходимые функции непосредственно из шеллкода. К счастью,в WinApi есть две полезные функции: LoadLibrary и GetProcAddress, которые мы можем использовать для поиска адресов наших функций.

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

Посмотрите на этот код:
Код:
mov eax,0
xor eax,eax
Эти инструкции эквивалентны по результату (обнулить регистр еах), но первая в бинарном коде содержит нулл-байты, а вторая - нет. Еще, существуют варианты, когда в шеллкоде не должно быть символов переноса строк, табуляций и подобного.

Различия Windows и Linux
Проще писать шеллкод для Linux, по крайней мере, в целях обучения. Это связано с тем, что в Linux можно использовать системные вызовы (syscall), такие как write, execve или send, с помощью прерывания 0x80 (как в DOS, или просто воспринимайте это пока как "вызов функции").

В качестве примера, базовый "hello,world" шеллкод в Linux требует следующих шагов:
Укажите номер системного вызова (например,"write")
Укажите параметры syscall (например, stdout, «Hello, world», length)
Вызовите 0x80 для выполнения системного вызова (syscall'a)

В конечном итоге это будет эквивалентно коду на Си: write(stdout, "Hello, world", length).

В Windows это сложнее. Для создания надежного шеллкода необходимы более необходимые шаги.

Получить базовый адрес kernel32.dll (она всегда загружена в процесс)
Найти адрес функции GetProcAddress
Использовать GetProcAddress, чтобы найти адрес функции LoadLibrary
Использовать LoadLibrary для загрузки DLL (например, user32.dll)
Использовать GetProcAddress, чтобы найти адрес функции (например, MessageBox)
Указать параметры функции
Вызвать функцию

Продолжение следует..

Это свободный перевод статьи https://securitycafe.ro/2015/10/30/introduction-to-windows-shellcode-development-part1/ . Вопросы и критику пишите в теме. Для лучшего понимания темы перечитайте базовые основы языка Си, Ассемблер (стековые фреймы, регистры), РЕ формат. Частично эти темы будут рассматриваться в следующих статьях.
 

pixe1

HDD-drive
Пользователь
Регистрация
30.08.2014
Сообщения
30
Оценка реакций
36
Баллы
25
а практике, эти адреса отличаются от версии к версии винды, даже от сервиспака к сервиспаку. Кроме того, в версиях выше ХР (т.е. , можно сказать, везде) существует такая вещь, как ASLR (рандомизация адресного пространства), которая загружает системные DLL каждый раз по новому адресу.
чуть подправлюа/дополню ASLR (рандомизация адресного пространства) на Windows Vista +, ASLR рандомизирует мап самого PE (+ stack heaps), и подгружаемых в него библиотек (DLL) ASLR является флагом компилятора (/DYNAMICBASE) он не является обязательным но может быть энфорснут system-wide посредствам Windows Defender Exploit Guard (WDEG) на Win8+ но и этим есть проблемы -> https://www.zdnet.com/article/microsoft-says-aslr-behavior-in-windows-10-is-a-feature-not-a-bug/

так что все зависит от того какой бинарь эксплуатируешь

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

Quake3

генератор Зла
Модератор
Регистрация
03.11.2010
Сообщения
625
Оценка реакций
205
Баллы
45
CKAP
наверное, имелось ввиду что этого нет в статье, а не на форуме. В принципе, шеллкоды действительно не особо относятся к реверсингу, хотя не знаю, куда ее перенести. В Си-кодинг точно нет, разве что создавать еще раздел по Асм-кодингу.

evilcore
Статья неплохая, главное - продолжайте тему, а то статей вида "пишем хелловорлд" много, а продолжений нет.
 

pixe1

HDD-drive
Пользователь
Регистрация
30.08.2014
Сообщения
30
Оценка реакций
36
Баллы
25
да, я про статью
 
Верх