PWN之canary骚操作

最近做题遇到一些canary的保护的题目,于是想着搜集整理一波有关绕过canary的操作。

Canary保护机制的原理,是在一个函数入口处从fs段内获取一个随机值,一般存到EBP - 0x4(32位)或RBP - 0x8(64位)的位置。如果攻击者利用栈溢出修改到了这个值,导致该值与存入的值不一致,__stack_chk_fail函数将抛出异常并退出程序。Canary最高字节一般是\x00,防止由于其他漏洞产生的Canary泄露 需要注意的是:canary一般最高位是\x00,64位程序的canary大小是8个字节,32位的是4个字节,canary的位置不一定就是与ebp存储的位置相邻,具体得看程序的汇编操作

0x01

泄漏canary:

这个操作大概分两种:

  • 1.先通过覆盖Canary最后一个”\x00″字节,来防止0截断,进而通过printf等函数打印出4/8位的Canary,又或者通过格式化字符串泄漏输出canary,之后,计算好偏移,将Canary填入到相应的溢出位置,实现为所欲为的栈溢出

  • SSP leak,这个是反向思维的操作,通过Canary的报错输出来泄露内存,如下面源代码所示。libcargv[0]应用程序的参数,在Canary出错报错输出中会打印出应用程序的路径,这正是__libc_argv[0]的内容,如果栈溢出到能够覆盖__libc_argv[0],那么Canary就会报错打印相应的指针内容

    "debug/fortify_fail.c"
    void
    __attribute__ ((noreturn))
    __fortify_fail (msg)
     const char *msg;
    {
     /* The loop is added only to keep gcc happy. */
     while (1)
     __libc_message (2, "*** %s ***: %s terminated\n",
     msg, __libc_argv[0] ?: "<unknown>");
    }
    libc_hidden_def (__fortify_fail)
    

0x02

修改canary

由于多线程中Canary存入TLS结构体,而TLS位于多线程内部栈的高地址,并且该结构体与当前栈差距不足一个page,导致我们能对其进行修改,改为我们想要的值,从而绕过检测。

这种操作一般在多线程下进行,而且溢出的字节必须足够大才能溢出到tls的位置从而修改canary 题目详见:https://www.jianshu.com/p/110f715c210f中的babystack

0x03

爆破canary

Canary在设计中存在一个缺陷,这个缺陷或许不是由于Canary的设计问题,而是linux的机制导致的,那就是子进程会继承父进程的Canary,每一次fork的进程内存布局与父进程一致,Canary值也相同

这样当我们子进程由于Canary判断不正确导致程序crash后,父进程不会Crash 我们就完全可以利用这样的特点,彻底逐个字节将Canary爆破出来,爆破模板如下

print "[+] Brute forcing stack canary "

start = len(p)
stop = len(p)+8

while len(p) < stop:
   for i in xrange(0,256):
      res = send2server(p + chr(i))

      if res != "":
         p = p + chr(i)
         #print "\t[+] Byte found 0x%02x" % i
         break

      if i == 255:
         print "[-] Exploit failed"
         sys.exit(-1)


canary = p[stop:start-1:-1].encode("hex")
print "   [+] SSP value is 0x%s" % canary

0x04

绕过canary

这种操作的核心思想就是想办法让他不执行canary的报错或者直接跳过canary的检查

  • 1.利用格式化字符串或者数组下标越界,可以栈地址任意读写,不必连续向栈上写,直接写ebp和ret因此不会触发Canary检查。

  • 2.Hijack __stack_chk_fail 当Canary验证失败的时候是进入到stack_chk_failed函数中,它在该函数中完成报错输出,但是如果我们能够劫持该函数,让它不在完成该功能,那么Canary就形同虚设,我们就可以为所欲为栈溢出了 但需要注意的是:种技术并不是我们一般方式的Hijack GOT表,一般我们HijackGOT是GOT表绑定了真实地址之后,我们修改它,让程序执行其他的函数。 Got表中要绑定真实地址必须是得执行过一次,然而stack_chk_failed执行第一次的时候程序就报错退出了,因此我们需要Overwrite的尚未执行过的stack_chk_failed的GOT表项,此时GOT表中应该存贮这stack_chk_failed PLT[1]的地址

    1. 通过一些其他的机制跳过canary的检查,比如Shanghai-DCTF-2017 线下攻防Pwn题 中:–利用c++异常机制绕过canary检查

参考链接: http://tacxingxing.com/2017/07/13/canary/

https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/

https://bestwing.me/2017-Shanghai-DCTF-final-pwn.html#%E6%80%9D%E8%B7%AF

https://github.com/solei1/solei1.github.io/wiki/Canary-bypass