黑客攻防入门(四)对抗缓冲区溢出攻击
概说
现代的编译器和系统已经实现了很多机制,避免受到缓冲区溢出的攻击,下面介绍Linux上比较新版本的GCC所提供的机制。
1. 栈的随机化
我们在前面文章里提到的一个参数设置:
sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space 就是栈随机化的设置,当它的值为0时,禁止实现栈随机化,这样的情况下,程序在同一计算机系统内每次启动的栈基地址都是固定不变的。
当它的值为1时,将实现栈随机化,栈的位置在程序每次运行时都有变化。 即使许多机器都运行同样的代码,它们的栈地址都是不同的。
栈随机化的实现方式是:
程序开始时,在栈上分配一段0~n字节之间的随机大小空间,程序不使用这段空间,但它会导致程序每次执行时的栈的位置发生变化。
分配的范围n必须足够大,才能获得足够多样的栈地址变化;同时又必须足够小,不至于浪费程序太多的空间。
在Linux中,栈随机化是标准行为,它是更大的一类技术中的一种,这类技术称为地址空间布局随机化,采用这类技术,程序的不同部分(代码段、数据段、堆栈)都会被加载到存储器的不同部分。
顽固且有耐性的攻击者可以通过枚举的方法来蛮力克服随机化,它反复用不同的地址进行攻击,来猜测栈的地址。如果它建立一个256字节的nop sled(空操作),枚举2^15=32768个起始地址就能破解2^23的随机化。
对于64位系统,需要尝试2^24=16777216就有点令人生畏了。
2. 栈破坏检测
前面的文章里,我们用gcc编译时都加上了下面的参数。
"-fno-stack-protector" 这个参数用来阻止程序生成栈破坏检测的代码。
这是因为在新版的GCC里加入了一种栈保护者机制,用来检测缓冲区越界,如果我们不禁止这个功能,那么演示缓冲区溢出攻击实例就无法成功。
那么,GCC是怎样实现这个保护机制的呢?
实现的方法是: 在栈帧中任何局部缓冲区和栈状态之间存储一个只有程序本身才知道的随机值,俗称为哨兵,在恢复存储器状态和函数返回之前,程序检测哨兵值是否被覆盖,如果是,那么程序就异常中止。
3.限制可执行代码区域
我们在前面的文章中编译程序也用到了下面的一个参数
execstack
使用这个参数的目的就是将限制可执行代码区域的限制取消,使我们的演示能顺利进行。
这种方法的实现是和虚拟存储器的页表条目有关的,在每个页表条目里有三个权限位用来控制对页的访问,其中XD就是禁止CPU在这个页表所对应的空间里读取指令,亦即是在这个区域里限制可执行代码。
1.现代的处理器都使用虚拟寻址的寻址形式,CPU通过一个生成的虚拟地址来访问内存,这些虚拟地址就是通页表条目来记录管理的。
2.页表条目是由几个权限位(有效位)和一个n位地址字段组成。
4. 总结
我们讲到的这些技术——随机化、栈保护和限制可执行代码,是用于最小化程序缓冲区溢出攻击漏洞三种最常见机制,它们都有同样的属性,就是不需要程序员任何特殊的努力,带来的性能代价都非常小,甚至没有。这三种机制都很有效,三种结合起来大大提高了程序的安全性,不幸的是,仍然有办法能够攻击到计算机。
什么办法呢?
后面的章节见解!黑客攻防入门(五)通用的缓冲区漏洞攻击代码
文/屎壳郞(简书作者)
原文链接:http://www.jianshu.com/p/1b72a28ff24d