Protostar Stack Exploits (Solutions 6-7)
Buffer overflow exploit exercises, part three.
Published on 28 August 2012Overview
Protostar is a series of exercises from Exploit Exercises. In addition to three final levels, it has four basic sections: network programming, format strings, heap overflows, and stack overflows.
This series of posts contains solutions and walkthroughs for the stack overflow levels (“Stack”). It assumes basic knowledge of systems programming and is meant to serve as a reference for those stuck on certain levels. This is the final post for Stack.
Solutions
Stack 6
Description (full): Execute shellcode with a restriction on the return address. This mimics a nonexecutable stack by barring any return addresses of the form 0xbfxxxxxx
.
Utilities
We’ll add one more script to our utility belt. This will help us quickly make guesses for buffer overflows.
~/try-address.sh
:
Solution
First, we need to find the proper offset for overwriting the return address. The command below reveals that the return address is 16 bytes after the end of the 64 byte buffer, meaning that 80 bytes are needed before overwriting the return address.
For our solution, we’ll be performing a ret2libc attack. This paper by InVoLuNTaRy offers an in-depth description of the tactic (sidenote: it’s one of the most well-written papers that I’ve ever read and well worth the read). The rest of this section assumes that this paper has been read.
First, we find the addresses of the system()
and exit()
libc functions. exit()
isn’t strictly necessary, but since ret2libc ultimately calls two functions, we prefer to return without a segmentation fault.
We see that our functions have the following addresses:
system()
:0xb7ecefb0
exit()
:0xb7ec50c0
Given the particular stack layout for a ret2libc attack (described in the InVoLuNTaRy paper), our solution will be of the form:
Sanity checks
First, let’s make sure that we can actually execute code.
Working netcat solution
There are two obstacles to a working solution.
- Environmental variables with spaces don’t work. All of the following would fail:
system()
will drop our root privileges (man system)
Instead, we can wrap our call to netcat in another C program, naming an executable without spaces and restoring our privileges.
system()
doesn’t seem to be using the contents of our environmental variable. If we look at the environmental variables, though, we see that we were close enough: RUN
is the next variable. We then adjust our estimate for the address of RUN
by 20 bytes.
Finally, through a remote machine:
Stack: Level 7
Description (full): Execute shellcode with further restrictions on the return address. Any address of the form 0xbxxxxxxx
raises an error.
Overview
The return value of a function is usually stored in the eax
register. The key to this level is the call to return strdup(buf)
: this means we can find the buffer (e.g. our shellcode) in the eax
register when getpath()
returns.
The code restricts us from returning to any code on the bottom of the stack (0xbxxxxxxx
) and advises us to return to the .text
section. In particular, we’ll want to return to a call eax
instruction, which will then call our shellcode.
If we play with the numbers like in earlier levels, we’ll find that 80 bytes are needed before the return address. Our buffer overflow will look like this:
Solution
In earlier levels, we found that opening a shell with gets()
. However, we can still use it to validate our strategy:
If we give it a shot with our nc
shellcode, we’ll find the following error: bin/sh: forward host lookup failed: Unknown host
. This occurs as a complication from having options passed into the execve
call.
We can add another wrapper to the netcat.c
used in Level 6 by writing our own shellcode. This shellcode will simply execute /tmp/nc
, an alias for a compiled netcat.c
. This file, in turn, calls netcat and listens via port 8080.
~/sc-tmp-nc.asm
:
On a remote machine: