绕过防御之击败canary保护
canary的认识和原理
函数在开始执行的时候会先往栈底插入 cookie 信息,当函数真正返回的时候会验证 cookie 信息是否合法,即栈帧销毁前测试该值是否被改动,如果不合法就停止运行。在 Linux 系统当中我们将这段 cookie 信息称为 Canary
Canary 是一个低字节位 \x00 的十六进制数。
由于 Canary 在局部变量的后面,在 EBP 或 RBP 的前面,所以当攻击者覆盖返回地址的时候往往也会将 cookie 信息给覆盖掉,这就会导致栈保护检查失败而阻止 payload 的运行,从而避免漏洞被利用成功
1 | /* |
对 canary 的简单叙述
为了更清晰地了解 canary 的保护过程和破解方式,我们通过一道练习去进一步了解 canary 的栈情况。
BUUCTF[NewStarCTF 公开赛赛道]canary
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
canary 总是出现在局部变量的后面和 EBP 或者 RBP 之前,所以我们运行到 read 函数执行之前看一看栈的情况
1 | RBP 0x7fffffffdce0 ◂— 1 |
在 RBP 之前,且末尾以 00 结尾的就是 canary ==>0xde32329de0620700
继续 si 进入到 read 函数当中了解 canary 保护的方法。
1 | 0x555555400aa9 <main+197> mov eax, 0 EAX => 0 |
在函数序言部分会取 fs 寄存器 0z28 处的值存放在 rbp-0x8 的位置,在函数返回前会将该值去除,并与 fs:0x28 的值进行异或,检查 canary 是否被破坏。当我们覆盖掉 Canary 的时候,进行异或的值就不会为 0,验证失败。
因此我们之前直接覆盖 rbp/ebp 的做法就失效了。我们就要寻找其他方式去绕过 Canary。
Canary 的绕过方法
Canary绕过 方法一
Canary 设计以字节 \x 结尾,其本意就是保证 Canary 可以阶段字符串,防止我们输出字符串的时候将 Canary,所以泄露站中的 Canary 的思路是将 Canary 的低字节 \x00 覆盖,然后连同用户的字符串一起打印出剩余的 Canary 部分。当我们打印出完整的 Canary 后,在溢出的时候再将正确的 Canary 填充回去,就可以进行控制返回地址,控制程序流程,从而获取 shell 了。
条件:1)有合适的输出函数 2)泄露 Canary 后程序不会崩溃
由于[NewStarCTF 公开赛赛道]canary 难度过高,我们换一道题目当做练习 BUUCTF bjdctf_2020_babyrop2
我们继续以_**BUUCTF[NewStarCTF 公开赛赛道]canary **_为例
我们可以找到 system 和/bin/sh 去构造后门函数,利用方式就是基本的 ret2syscall,不过我们还需要利用方式。

1 | int __fastcall main(int argc, const char **argv, const char **envp) |
思路:通过第一次覆盖,将前面的字节全部填充满,并将canary之前的最后一个字节也覆盖填充,之后打印出canary的值,之后第二次再相同位置填充canary的值,之后去进行栈溢出操作
Canary2绕过 方法二
canary爆破原理
先对于Canary,虽然每一次进程重启之后的Canary不同,但是用一个进程的不同线程的Canary是相同的,并且通过fork函数创建的在子进程中的Canary也是相同的,因为fork函数会直接拷贝父进程的内存。最低位是0x00,之后初次爆破,如果canary爆破不成功,则程序崩溃,爆破成功则程序进行,由此可以推断Canary是否爆破成功
canary2exp
1 | from pwn import * |
爆破exp分析
1 | from pwn import * |
更新: 2026-01-26 15:46:04
原文: https://www.yuque.com/idcm/wnemg9/mzehwtf2avfgmeiw