canary是栈溢出的防护机制,一般是位于ebp的上方,用于检测栈帧是否有溢出状态,用图形表示:
因为一个栈帧往往是ebp作为结尾的,所以canary一般是尾随的(也有特殊情况,具体看作者的汇编情况),在32位下是4个字节,64位下是8个字节(最低位都是\x00)
那么这个保护机制也是很皮的,它为了防止别人暴力输出它,于是有个\x00的截断符,canary的最低位是0x00,这么做为了防止canary的值泄漏,举个64位的例子:
从fs段中取出最初的canary值,然后从栈中取canary放到edx中,二者异或,如果不相同则跳转到___stack_chk_fail函数,这个函数底层是这样的:
可见,__libc_message 的第二个%s输出的是argv[0],argv[0]是指向第一个启动参数字符串的指针,所以,猜想只要我们能够输入足够长的字符串覆盖掉argv[0],我们就能让canary保护输出我们想要地址上的值。
canary大概就是这些内容,接下来常见的题型是怎么样的呢?
一、通过某些方式泄漏canary:
1、格式化字符串泄露:大体思路就是通过格式化字符串读取canary的值,然后在栈溢出的padding块把canary所在位置的值用正确的canary替换,从而绕过canary的检测。
2、fork爆破:对fork而言,作用相当于自我复制,每一次复制出来的程序,内存布局都是一样的,当然canary值也一样。那我们就可以逐位爆破,如果程序GG了就说明这一位不对,如果程序正常就可以接着跑下一位,直到跑出正确的canary。
3、数组下标越界(待续,还没学到)
二、故意触发___stack_chk_fail:
1、ssp攻击:根据上面学到的,只要我们能够输入足够长的字符串覆盖掉argv[0],我们就能让canary保护输出我们想要地址上的值,举个例子:
但是有时打印flag的话可能打印不出来(这种情况下一般有副本,放副本的地址,打印副本的flag即可)。
OJ有道题可以试试:
就是栈溢出保护,然后0x600d20的地址里面的东西会变,我们通过查找发现它有一个副本,于是可以利用不变的副本去搞出flag。
从这里我们可以知道那个划线的就是我们的argv[0]的位置,因为可以看到装的就是我们的程序命名字符串。这里我们需要找到flag的地址,然后直接覆盖即可:
所以直接暴力填,肯定会有一个填到的,最后验收下:
2、King攻击(自命名的):通过破坏canary的值,触发___stack_chk_fail库函数,然后我们只要替换这个函数的got表为我们的system函数或者getflag函数等后门函数,并且故意让canary检查失败就可以劫持程序的控制流了。(题目来自于hgame,week2)