canary(FS):栈溢出保护
Canary是金丝雀的意思。技术上表示最先的测试的意思。这个来自以前挖煤的时候,矿工都会先把金丝雀放进矿洞,或者挖煤的时候一直带着金丝雀。金丝雀对甲烷和一氧化碳浓度比较敏感,会先报警。所以大家都用canary来搞最先的测试。
stack canary表示栈的报警保护。在函数返回值之前添加的一串随机数(不超过机器字长),末位为/x00(提供了覆盖最后一字节输出泄露canary的可能),如果出现缓冲区溢出攻击,覆盖内容覆盖到canary处,就会改变原本该处的数值,当程序执行到此处时,会检查canary值是否跟开始的值一样,如果不一样,程序会崩溃,从而达到保护返回地址的目的。
原理
1、 在所有函数调用发生时,向栈帧内压入一个额外的随机 DWORD,这个随机数被称作
“canary”,用 IDA 反汇编时,又被称作“Security Cookie”。
2、 canary 位于 EBP 之前,系统还会在.data 的内存区域中存放一个 canary 的副本。
3、 当栈中发生溢出时,canary 将被首先淹没,之后才是 EBP 和返回地址。
4、 在函数返回之前,系统将执行一个额外的安全验证操作,称作 Security Check。 5、在 Security Check 过程中,系统将比较栈帧中原先存放的 canary 和.data 中副本的值,若两者不同,则说明栈中发生了溢出,系统将进入异常处理流程,函数不会正常返回。
演示
1.查看源码
2.编译,不开canary保护 去掉pie
gcc -no-pie -fno-stack-protector -o test test.c 关闭堆栈保护
3.分析
gcc -no-pie -fstack-protector-all -o test1 test.c 启用堆栈保护,为所有函数插入保护代码
-fstack-protector 启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
加入保护后如下
动态调试分析canary
把gs寄存器中canary的值放在eax寄存器中
把canary的值放到ebp-0xC
xor 异或 比较两个的值 若相等 继续跳转
若修改了canary的值 跳转到
如果 Canary 已经被非法修改,此时程序流程会走到 __stack_chk_fail。__stack_chk_fail 也是位于 glibc 中的函数,默认情况下经过 ELF 的延迟绑定,定义如下。
1 | eglibc-2.19/debug/stack_chk_fail.c |
Canary 的绕过技术
1.泄露栈中的 Canary
Canary 设计为以字节 \x00 结尾,本意是为了保证 Canary 可以截断字符串。 泄露栈中的 Canary 的思路是覆盖 Canary 的低字节,来打印出剩余的 Canary 部分。 这种利用方式需要存在合适的输出函数,并且可能需要第一溢出泄露 Canary,之后再次溢出控制执行流程。
也可以和格式化字符串漏洞结合,获得Canary 的值。
需要ASLR没开启。
2.爆破 Canary
1 | print "[+] Brute forcing stack canary " |
3.劫持__stack_chk_fail 函数
已知 Canary 失败的处理逻辑会进入到 __stack_chk_failed 函数,__stack_chk_failed 函数是一个普通的延迟绑定函数,可以通过修改 GOT 表劫持这个函数。
4.覆盖 TLS 中储存的 Canary 值
已知 Canary 储存在 TLS 中,在函数返回前会使用这个值进行对比。当溢出尺寸较大时,可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过。
bugku canary
源码很简单,有两次read栈溢出,但是需要绕过Canary
绕过方式就是,在第一个read处输入到覆盖canary末尾的\x00,后面的print会之间把canary输出来
注:canary的特性,通过末尾的\x00来截断参数,当\x00被覆盖后,canary也就变成了print参数的一部分。