什么是堆栈保护技术?
堆栈保护技术(即Stack canary)是用于防护栈溢出攻击的一种保护机制:在栈上的返回地址跟ebp之前加上一个标志位canary,返回时通过验证这个canary是否被改写,从而判断程序是否被栈溢出攻击。
如何开启堆栈保护技术?
用gcc编译时,可以用以下参数来设置:
1 | -fstack-protector 启用保护,不过只为局部变量中含有数组的函数插入保护 |
canary的产生
在开启了堆栈保护技术程序中,我们再ida经常可以看到用如下的指令来获取canary:
1 | mov rax, fs:28h |
其中,fs其实是指向当前栈的TLS结构:
1 | typedef struct |
fs+0x28h即为stack_guard。
而TLS结构是由security_init()初始化:
1 | static void security_init (void) |
security_init()是ld文件中的一个子函数,在ida中可以看到,初始化canary为如下指令(以2.23版本为例):
1 | .text:0000000000003C25 mov rdx, cs:qword_225E68 |
其中,cs:qword_225E68即对应对应于存放_dl_random的地址。
(注:fs寄存器的内容可以通过在gdb中直接输入fsbase得到。)
而_dl_random的地址来自:
1 | .text:0000000000019741 cmp [rsp+78h+var_49], 0 |
这里下个内存断点来跟进(直接关闭aslr):
1 | pwndbg> watch *(0x7fffffffde80+0x20) |
结果跟进到 ► 0x7ffff7df0786 <_dl_sysdep_start+518> jmp _dl_sysdep_start+304 <0x7ffff7df06b0>
里面来,在ida下看看:
1 | .text:0000000000019778 loc_19778: |
可以看到rsp+0x20的值来自[rdx+8]。分析整个函数,可以得知rdx的值是来自于_dl_sysdep_start
函数的第一个参数(有一定的偏移,其值为0x7fffffffdf70,而_dl_random
的值为0x7fffffffe2c9)。
继续追溯可以找到也是_dl_start来自的第一个参数(同样是0x7fffffffdf70)。
最终追溯到ld中start函数的rsp:0x7fffffffdf70。
绕过堆栈保护方法
canary绕过方式总结下有这几种:
- 直接通过相邻的变量puts或者printf,然后leak出来
- 逐位爆破
- 通过覆盖TLS结构(地址可以通过与libc的偏移计算得出)中的canary,从而绕过canary保护
- 劫持___stack_chk_fail的got表项
- 劫持栈上
*** stack smashing detected ***: ./canary terminated
中对应./canary
处的指针,在发生smashing detected时泄漏出关键信息