栈破坏检查:金丝雀Canary
栈破坏检查:金丝雀Canary
Canary /kəˈneəri/ n. 金丝雀
英国矿井饲养金丝雀的历史大约起始1911年。当时,矿井工作条件差,矿工在下井时时常冒着中毒的生命危险。后来,约翰·斯科特·霍尔丹(John Scott Haldane)在经过对一氧化碳一番研究之后,开始推荐在煤矿中使用金丝雀检测一氧化碳和其他有毒气体。金丝雀的特点是极易受有毒气体的侵害,因为它们平常飞行高度很高,需要吸入大量空气吸入足够氧气。因此,相比于老鼠或其他容易携带的动物,金丝雀会吸入更多的空气以及空气中可能含有的有毒物质。这样,一旦金丝雀出了事,矿工就会迅速意识到矿井中的有毒气体浓度过高,他们已经陷入危险之中,从而及时撤离。
金丝雀是通过汇编来实现的。例如,由于 GCC 的 栈保护器 选项的原因使金丝雀能被用于任何可能有漏洞的函数上。函数序言加载一个魔法值到金丝雀的位置,并且在函数结语时确保这个值完好无损。如果这个值发生了变化,那就表示发生了一个缓冲区溢出(或者 bug),这时,程序通过 __stack_chk_fail 被终止运行。由于金丝雀处于栈的关键位置上,它使得栈缓冲区溢出的漏洞挖掘变得非常困难。
tack-Smashing Protector”机制(在数组后插入一些金丝雀(Canary, 因为大家在矿井作业时,会用金丝雀来预警,如果金丝雀死了,那证明有毒气),在函数执行完成返回之前会检查一下Canary,如果canary被改掉,则不再继续执行,中止程序,避免执行攻击者的代码。)
因此,出现“栈溢出”这个错,实际上是GCC编译器的保护作用!这也是为什么在打印结果之后才报错!
另外,尝试了编译时怎么不开启"Stack-Smashing Protector"。(“Stack-Smashing Protector”默认是打开的!)
如:$ gcc test.c -fno-stack-protector。运行时输出结果后不会报“stack smashing detected”!
缓冲区溢出一个常见的后果是:黑客利用函数调用过程中程序的返回地址,将存放这块地址的指针精准指向计算机中存放攻击代码的位置,造成程序异常中止。为了防止发生严重的后果,计算机会采用栈随机化,利用金丝雀值检查破坏栈,限制代码可执行区域等方法来尽量避免被攻击。虽然,现代计算机已经可以“智能”查错了,但是我们还是要养成良好的编程习惯,尽量避免写出有漏洞的代码,以节省宝贵的时间!
缓冲区溢出
缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上。理想的情况是:程序会检查数据长度,而且并不允许输入超过缓冲区长度的字符。但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所使用的缓冲区,又被称为“堆栈”,在各个操作进程之间,指令会被临时储存在“堆栈”当中,“堆栈”也会出现缓冲区溢出。 (来源百度百科)
缓冲区溢出的危害
可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。缓冲区溢出攻击有多种英文名称:buffer overflow,buffer overrun,smash the stack,trash the stack,scribble the stack, mangle the stack, memory leak,overrun screw;它们指的都是同一种攻击手段。第一个缓冲区溢出攻击--Morris蠕虫,发生在二十年前,它曾造成了全世界6000多台网络服务器瘫痪。
在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。 (来源百度百科)
内存在计算机中的排布方式
内存在计算机中的排布方式如下,从上到下依次为共享库,栈,堆,数据段,代码段。各个段的作用简介如下(更详细的内容总结见嵌入式软件开发知识点总结.pdf):
共享库:共享库以.so结尾.(so==share object)在程序的链接时候并不像静态库那样在拷贝使用函数的代码,而只是作些标记。然后在程序开始启动运行的时候,动态地加载所需模块。所以,应用程序在运行的时候仍然需要共享库的支持。共享库链接出来的文件比静态库要小得多。
栈:栈又称堆栈,是用户存放程序临时创建的变量,也就是我们函数{}中定义的变量,但不包括static声明的变量,static意味着在数据段中存放变量。除此之外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中,由于栈的先进后出特点,所以栈特别方便用来保存、恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存,交换临时数据的内存区。在X86-64 Linux系统中,栈的大小一般为8M(用ulitmit - a命令可以查看)。
堆:堆是用来存放进程中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态分配到堆上,当利用free等函数释放内存时,被释放的内存从堆中被剔除。
堆存放new出来的对象、栈里面所有对象都是在堆里面有指向的、假如栈里指向堆的指针被删除、堆里的对象也要释放(C++需要手动释放)、当然我们现在好面向对象程序都有'垃圾回收机制'、会定期的把堆里没用的对象清除出去。
数据段:数据段通常用来存放程序中已初始化的全局变量和已初始化为非0的静态变量的一块内存区域,属于静态内存分配。直观理解就是C语言程序中的全局变量(注意:全局变量才算是程序的数据,局部变量不算程序的数据,只能算是函数的数据)
代码段:代码段通常用来存放程序执行代码的一块区域。这部分区域的大小在程序运行前就已经确定了,通常这块内存区域属于只读,有些架构也允许可写,在代码段中也有可能包含以下只读的常数变量,例如字符串常量等。程序段为程序代码在内存中映射一个程序可以在内存中有多个副本。
避免缓冲区溢出的3种方法
为了在系统中插入攻击代码,攻击者既要插入代码,也要插入指向这段代码的指针。这个指针也是攻击字符串的一部分。产生这个指针需要知道这个字符串放置的栈地址。在过去,程序的栈地址非常容易预测。对于所有运行同样程序和操作系统版本的系统来说,在不同的机器之间,栈的位置是相当固定的。因此,如果攻击者可以确定一个常见的Web服务器所使用的栈空间,就可以设计一个在许多机器上都能实施的攻击。
栈随机化
栈随机化的思想使得栈的位置在程序每次运行时都有变化。因此,即使许多机器都运行同样的代码,它们的栈地址都是不同的。实现的方式是:程序开始时,在栈上分配一段0 ~ n字节之间的随机大小的空间,例如,使用分配函数alloca在栈上分配指定字节数量的空间。程序不使用这段空间,但是它会导致程序每次执行时后续的栈位置发生了变化。分配的范围n必须足够大,才能获得足够多的栈地址变化,但是又要足够小,不至于浪费程序太多的空间。
测栈是否被破坏
计算机的第二道防线是能够检测到何时栈已经被破坏。我们在echo函数示例中看到,当访问缓冲区越界时,会破坏程序的运行状态。在C语言中,没有可靠的方法来防止对数组的越界写。但是,我们能够在发生了越界写的时候,在造成任何有害结果之前,尝试检测到它。
GCC在产生的代码中加人了一种栈保护者机制,来检测缓冲区越界。其思想是在栈帧中任何局部缓冲区与栈状态之间存储一个特殊的金丝雀( canary)值。
这个金丝雀值,也称为哨兵值,是在程序每次运行时随机产生的,因此,攻击者没有简单的办法能够知道它是什么。在恢复寄存器状态和从函数返回之前,程序检查这个金丝雀值是否被该函数的某个操作或者该函数调用的某个函数的某个操作改变了。如果是的,那么程序异常中止。
https://www.jianshu.com/p/47012df7404f
栈随机化:栈的地址在每次运行时都是随机变化的。(无法再将攻击代码注入到栈上某个指定的位置了,成功阻止了如第一部分的leve2和leve3那种攻击方式)
栈不可执行:栈上不能执行代码指令。(同样,成功阻止了如第一部分的leve2和leve3那种攻击方式)
栈破坏检查:在栈中局部缓存区前面放置一个状态值,也叫做 金丝雀 ,在使用了缓冲区后检查 金丝雀 的值,如果发现其值发生变更则说明栈被破坏了。(这种保护机制是最强的,从根源上阻止了 buffer overflow 攻击)
2、本资源基本为原创,部分来源其他付费资源平台或互联网收集,如有侵权请联系及时处理。
3、本站大部分文章的截图来源实验测试环境,请不要在生产环境中随意模仿,以免带来灾难性后果。
转载请保留出处: www.zh-cjh.com珠海陈坚浩博客 » 栈破坏检查:金丝雀Canary
作者: cjh
手机扫一扫,手机上查看此文章: |
一切源于价值!
其他 模板文件不存在: ./template/plugins/comment/pc/index.htm