** stack smashing detected ** ** stack smashing detected ** ** stack smashing detected **

canary

Canary 主要是为了防止溢出而开发的栈保护机制 看了一些canary的由来这个很有意思

17世纪,英国矿井工人发现,金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱;而当瓦斯含量超过一定限度时,虽然鲁钝的人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为“瓦斯检测指标”,以便在危险状况下紧急撤离。
摘自https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/

 我们在逆程序的时候常常会看到像是这样的东西:

v4 = __readfsqword(0x28u);
return __readfsqword(0x28u) ^ v4;

或者在gdb调试的时候回发现

   0x400882    mov    rax, qword ptr [rsp + 0x108]
   0x40088a    xor    rax, qword ptr fs:[0x28]
  0x400893   jne    0x4008a9
    
   0x4008a9    call   __stack_chk_fail@plt <0x400650>

原理上就是在开始的时候往栈里放一个随机的值像是0xdd687609fc59dc00 这样看起来就不像是正常值末尾带着一个nullbyte的值 在函数结束的时候再次在fs或者gs寄存器取出canary和栈上的canary比较如果不同那么 call __stack_chk_fail@plt 之后会call abort 结束程序

编译的时候可以加上以下命令来关闭

-fno-stackprotector

绕过方法一下主要有 泄露和破坏__stack_chk_fail函数两种

在最后介绍一个利用方法 ssp leak

Leak-Canary

泄露canary也就是利用某些方法读出栈上的canary 根据不同的环境 泄露方法有很多

通过溢出泄露

char buf[0x10];
read(0,buf,0x100);
puts(buf);

通过上述代码或者相似代码,我们可以通过溢出到canary的最后一字节\x00来leak canary

通过任意地址读来泄露

这个就比较多了..就不再解释了

通过多次试验爆破

fork 的程序内存布局与原程序相同所以cnary也是相同的,如果有多次机会的话那么就可以不停地partial write看看会不会崩溃

x64尝试次数应该是256*7还是可以接受的

通过覆盖掉要进行比较的值

这个是是在*ctf2018的时候看到的babystack

线程局部存储(Thread Local Storage)是一种机制,通过该机制分配变量,以便每一个现存的线程都有一个变量实例。

某些情况下,在新创建的线程中canary的比较值在栈上,我们可以通过盖掉该值过canary

my blog of babystack

Destroy Canry __stack_chk_fail

盖掉__stack_chk_fail的got为一些其他没影响的函数即使没过检测触发了也不会影响到我们的后续操作

SSP LEAK

虽然是绕不过了但是我们可以利用__stack_chk_fail在程序结束之前输出一些东西

我们先来看看__stack_chk_fail

__stack_chk_fail (void)
{
  __fortify_fail_abort (false, "stack smashing detected");
}

跟进__fortify_fail_abort

__fortify_fail_abort (_Bool need_backtrace, const char *msg)
{
  /* The loop is added only to keep gcc happy.  Don't pass down
     __libc_argv[0] if we aren't doing backtrace since __libc_argv[0]
     may point to the corrupted stack.  */
  while (1)
    __libc_message (need_backtrace ? (do_abort | do_backtrace) : do_abort,
                    "*** %s ***: %s terminated\n",
                    msg,
                    (need_backtrace && __libc_argv[0] != NULL
                     ? __libc_argv[0] : "<unknown>"));
}

这里的__libc_argv[0]也就是程序名字在程序一开始的时候被设置在栈上 经过进一步研究发现他的地址也在栈上所以我们如果可以有可以覆盖到足够长的地方我们就可以用我们想泄露的地址覆盖原本的地址 那么就会在触发__stack_chk_fail同时泄露.

demo传送门