Part two of the three exploit exercises available. This set of challenges focused purely on memory corruption via stack overflows; the challenges here weren’t too difficult, but I wanted to work through them anyway. A post with the remaining levels will follow.
Stack Level 00
Introductory level that introduces stack layout; here, the
modified variable follows a 64 byte array on the stack. If the stack is overwritten, the modified variable will be run over.
1 2 3
Stack Level 01
Another introductory level that forces you to overwrite a particular variable with a specific value. Essentially we need to take level 00 and figure out exactly where to overwrite the modified variable. This can be trivially guessed, considering we know the buf size. Remembering that this architecture is little endian:
1 2 3
It’s also worth noting that, if you’re loading this into gdb, the modified variable is volatile, so if you
p &modified and don’t get the right value, don’t worry. You’re not crazy.
Stack Level 02
Almost a carbon copy of level 1, except this time our input vector is an environmental variable.
1 2 3
Stack Level 03
This level has us redirecting code flow to another function present in the source. The buffer length is the same as the previous three, so we just need to find out where the function lies.
1 2 3 4 5 6
Stack Level 04
Instead of overwriting a function call, we’ll be overwriting a return address. When the function exits, it pops the current stack frame off and returns the saved frame pointer, so we need only overflow the address immediately following EBP.
1 2 3 4 5 6
Stack Level 05
There’s no real flag here aside from getting a shell, so we’ll need some shellcode now. Disclaimer: I spent a good deal of time trying to get shellcode working for this, and would continually brick wall at the same spot:
1 2 3 4 5 6
During one of my googling escapades, I haphazardly clicked this link from someone having the exact same issues with this level. What should be a very simple buffer overflow turned into a very wonky, absurd tumble through some obscure shellcode issues. In the end, I came up with this solution:
1 2 3 4
Stack Level 06
This level forces us to place our shellcode in a specific path; i.e. out of userland. We can’t just stick it onto the stack, and we can’t place it in an environmental variable. Instead, we must use ret2[libc|strcpy|gets|etc] or ROP. It verifies the function’s return address by calling
__built_return_address(0), which will return the current function’s return address.
This one was pretty fun, as I was able to leverage the ret2libc to pull it off. I used c0ntext’s ret2libc demonstration paper as reference. I based most of my work off the previous level, and modified it only by adding system() and exit() addresses. My payload in the end looked like this:
[80 bytes junk | system() address | exit() address | command]
For this example, I have my command stashed in the GETME environmental variable:
1 2 3 4 5 6 7 8
I wasn’t able to get /bin/sh launched with a system() call, and after checking the man page, it appears that system drops root privileges. My second thought was to simply open up a reverse shell:
1 2 3
I now had a root shell listening on port 5555.
Stack Level 07
The final stack level is almost exactly like the previous level, however this time when it returns from getpath(), it returns strdup(buffer), which returns a pointer to a duplicate string of buffer. The address range is also now much more restrictive; disabling any address matching 0xb0000000. The exploit page notes that we should be thinking about a ret2text to exploit this, so I started thinking of ways to ret2plt or ret2dl-resolve, but in the end decided those were likely too advanced for such a simple level. Instead, I objdump’d the binary in search of a POP POP RET, which would bypass the addressing filter and allow me to take control over EIP. The first one I found was at
0x8048492. After some twiddling, I discovered the following would lead me to code execution:
perl -e 'print "A"x80 . "\x92\x84\x04\x08" . "\x90"x8' . "\xcc"x16 | ./stack7. So the payload will look like this
[80 bytes junk | POP POP RET | 8 bytes junk | shellcode]
I dumped the shellcode from level 5 into this, but for some reason the shell would only spawn if running in gdb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
\xac\xf7\xff\xbf is the address of shellcode. Instead I threw shellcode into an environmental variable and leveraged Jon Ericson’s getenvaddr application to find the offset:
1 2 3 4 5 6 7 8 9
Overall these levels were pretty basic, but I’m looking forward to digging into Fusion for more advanced flags.