总结绕过canary保护机制的几种方法
canary原意为金丝雀,以前下井工人通常会带一只金丝雀,当瓦斯气体浓度超标后金丝雀会死亡,以此起到预警作用,linux里防止栈溢出的canary机制也是类似
canary是一个介于栈基址ebp和变量之间的数值,程序运行结束时会检查canary的值是否正确,错误则会终止程序运行并打印错误信息
具体在栈中的结构如下
变量1
变量2
canary
ebp
eip
(canary不一定与与基址相邻)
因此如果在开启canary的程序中进行栈溢出必定会污染canary的值,在面对有canary保护的程序时必须想办法绕过或利用canary机制
依然从那个简单的例子开始
test.c
分别用 不使用canary和使用canary保护机制编译一下
1 | gcc -m32 -fno-stack-protector test.c -o test1 |
反编译一下看看两种编译方式的区别
1 | objdump -d test1 |
后者在函数开始时:
1 | mov %gs:0x14,%ecx ;将gs:0x14的值给ecx |
函数快要结束时:
1 | mov -0xc(%ebp),%eax ;从ebp-0xc位置取值给eax |
在有canary机制的程序里溢出看下结果
成功监测到了栈溢出,终止了程序
绕过canary机制
多进程程序canary爆破
每次运行程序时,canary的值都是不一样的,因此爆破canary看似不太可能,但在特定情况下还是可行的。
在程序使用多个子进程时,子进程的崩溃不会影响到父进程,但同一个程序里的canary是一样的,所以爆破就有了可行性
canary_brute.c
1 | gcc -m32 -fstack-protector -no-pie canary_brute.c -o canary_brute. |
程序会反复fork子进程调用vul函数,所以爆破canary存在可行性(主进程不会因栈溢出触发保护函数而退出)。
在32位程序中canary占4字节,且为了防止canary的值被连带泄露,canary的最高位为’\x00’。
栈的结构如下
爆破脚本
实战练习
2017 湖湘杯Pwn100
做法还是爆破canary,爆破出来后结合之前ret2libc中的内容,直接上脚本了
主动触发栈溢出保护
如果程序结束时程序检查canary的值被修改会调到_stack_chk_fail,再看下_stack_chk_fail的源码
1 | void __attribute__ ((noreturn)) __stack_chk_fail (void) |
程序终止并把argv[0]中的东西打印出来,SSP leak(Stack Smashing Protect leak)技术就是利用canary机制,将我们需要的内容放入argv[0]中,当栈溢出并最终打印argv[0]时,我们所需的内容便会打印出来,虽然很巧妙,但是局限性也很大,只能读取程序中特定的内容,更不要说getshell了
实战练习
jarvisoj smashes
劫持_stack_chk_fail
顾名思义,劫持_stack_chk_fail函数,即覆写_stack_chk_fail函数的在got表上的地址,也是故意触发canary机制。
部分内容参考资料ichunqiu月刊第六期