学习《计算机网络安全》这本书第六章的一些记录和整理,知识的搬运工
缓冲区溢出攻击原理
操作系统所使用的缓冲区又称为堆栈。在各个操作进程之间,指令被临时存储在堆栈当中,堆栈也会出现缓冲区溢出。当一个超长的数据进入缓冲区时,超出部分就会被写入其他缓冲区,其他缓冲区存放的可能是数据、下一条指令的指针,或者是其他程序的输出内容,这些内容都将被覆盖或破坏。可见一小部分数据或者一条指令的溢出就可能导致一个程序或者操作系统崩溃。第一个缓冲区溢出攻击——Morris蠕虫,曾造成了全世界6000多台网络服务器瘫痪。
C语言常用的strcpy、sprintf、strcat等函数都非常容易导致缓冲区溢出问题。
缓冲区溢出攻击的目的在于扰乱具有某些特权的程序的功能,这样可以使得攻击者取得程序的控制权。如果该程序具有足够的权限,那么整个主机就被控制了。一般而言,攻击者攻击root程序,然后执行类似“exec(sh)”的执行代码来获得root权限的shell。一般有两个操作:在程序的地址空间里安排适当的代码;通过适当的初始化寄存器和内存,让程序跳转到入侵者安排的地址空间执行。
缓冲区在系统中的表现形式是多样的,高级语言定义的变量、数组、结构体等在运行时可以说都是保存在缓冲区内的,因此所谓缓冲区可以更抽象地理解为一段可读写的内存区域,缓冲区攻击的最终目的就是希望系统能执行这块可读写内存中已经被蓄意设定好的恶意代码。</br>
按照冯·诺依曼存储程序原理,程序代码是作为二进制数据存储在内存的,同样程序的数据也在内存中,因此直接从内存的二进制形式上是无法区分哪些是数据、哪些是代码的,这也为缓冲区溢出攻击提供了可能。
代码存储了用户程序的所有可执行代码,在程序正常执行的情况下,程序计数器(PC指针)只会在代码段和操作系统地址空间(内核态)内寻址。</br>
数据段内存储了用户程序的全局变量、数据等。</br>
栈空间存储了用户程序的函数栈帧(包括参数、局部数据等),实现函数调用机制,它的数据增长方向是低地址方向。</br>
堆空间存储了程序运行时动态申请的内存数据等,数据增长方向是高地址方向。</br>
除了代码段和受操作系统保护的数据区域,其他的内存区域都可能作为缓冲区,因此缓冲区溢出的位置可能在数据段,也可能在堆、栈段。</br>
如果程序的代码有软件漏洞,恶意程序会“指挥”程序计数器从上述缓冲区内取指,执行恶意程序提供的数据代码。
栈的主要功能是实现函数调用。因此在介绍栈溢出原理之前,需要弄清楚函数调用时栈空间发生了怎样的变化。每次函数调用时,系统会把函数的返回地址(函数调用指令后紧跟指令的地址)、一些关键的寄存器值保存在栈内,函数的实际参数和局部变量(包括数据、结构体、对象等)也会保存在栈内。这些数据统称为函数调用的栈帧,而且每次函数调用都会有个独立的栈帧,这也为递归函数的实现提供了可能。
缓冲区溢出攻击防范
有几种基本的方法可以保护缓冲区免受缓冲区溢出的攻击和影响
- 通过操作系统使得缓冲区不可执行操作,从而阻止攻击者植入攻击代码。
- 强制写正确的代码。(应该意思是开发人员就注意这一点,从根本上解决这个问题)
- 利用编译器的边界检查来实现缓冲区的保护。这个方法使得缓冲区溢出不可能出现,从而完全消除了缓冲区溢出的威胁,但是相对而言代价比较大。
- 在程序指针失效前进行完整性检查。
上这些方法不能使得所有的缓冲区溢出失效,但能阻止绝大多数的缓冲区溢出攻击。另外,对于操作系统已有的基于缓冲区溢出的漏洞,最好的方法是给系统打补丁。