缓冲区溢出*攻击*

时间:2021-11-20 06:59:11
大凡上网久点的网人对缓冲区溢出应该是知道的,因为它的确是一个众人皆知、非常危险的漏洞,它是个不分什么系统、什么程序,都广泛存在的一个漏洞。以缓冲区溢出为类型的安全漏洞是最为常见,也是被黑客最多使用的攻击漏洞。所以了解缓冲区溢出方面的知识对于黑客也好或者管理员也好也或者是一般的网人也好都相对是有必要的。
►缓冲区溢出的概念和原理
缓冲区是内存中存放数据的地方。在程序试图将数据放到机器内存中的某一个位置的时候,因为没有足够的空间就会发生缓冲区溢出。而人为的溢出则是有一定企图的, 攻击者写一个超过缓冲区长度的字符串,然后植入到缓冲区,而再向一个有限空间的缓冲区中植入超长的字符串可能会出现两个结果,
一是过长的字符串覆盖了相邻的存储单元,引起程序运行失败,严重的可导致系统崩溃


另有一个结果就是 利用这种漏洞可以执行任意指令,甚至可以取得系统root特级权限

大多造成缓冲区溢出的原因是程序中没有仔细检查用户输入参数而造成的。

缓冲区是程序运行的时候机器内存中的一个连续块,它保存了给定类型的数据,随着动态分配变量会出现问题。大多时为了不占用太多的内存,一个有动态分配变量的程序在程序运行时才决定给它们分配多少内存。这样想下去的话,如果说要给程序在动态分配缓冲区放入超长的数据,它就会溢出了。 一个缓冲区溢出程序使用这个溢出的数据将汇编语言代码放到机器的内存里,通常是产生root权限的地方,这就不是什么好现象了。
仅仅就单个的缓冲区溢出惹眼,它并不是最大的问题根本所在。但 如果溢出送到能够以root权限运行命令的区域,一旦运行这些命令,那可就等于把机器拱手相让了。

►缓冲区溢出漏洞攻击方式

缓冲区溢出漏洞可以使任何一个有黑客技术的人取得机器的控制权甚至是最高权限。一般利用缓冲区溢出漏洞攻击root程序,大都通 过执行类似“exec(sh)”的执行代码来获得root 的shell

黑客要达到目的通常要完成两个任务,就是在 程序的地址空间里安排适当的代码和通过适当的初始化寄存器和存储器,让程序跳转到安排好的地址空间执行

●在程序的地址空间里安排适当的代码:
其实在程序的地址空间里安排适当的代码往往是相对简单的,但也同时要看运气如何。 如果说要攻击的代码在所攻击程序中已经存在了,那么就简单的对代码传递一些参数,然后使程序跳转到目标中就可以完成了。攻击代码要求执行“exec(‘/bin/sh’)”,而在libc库中的代码执行“exec(arg)”,当中的“arg”是个指向字符串的指针参数,只要把传入的参数指针修改指向“/bin/sh”,然后再跳转到libc库中的响应指令序列就OK了。当然了, 很多时候这个可能性是很小的,那么就得用一种叫“植入法”的方式来完成了。当向要攻击的程序里输入一个字符串的话,程序就会把这个字符串放到缓冲区里,这个字符串包含的数据是可以在这个所攻击的目标的硬件平台上运行的指令序列。

缓冲区可以设在像:堆栈(自动变量)、堆(动态分配的)和静态数据区(初始化或者未初始化的数据)等的任何地方。也可以不必为达到这个目的而溢出任何缓冲区,只要找到足够的空间来放置这些攻击代码就够了。

●将控制程序转移到攻击代码的形式:

所有的这些方法都是在寻求改变程序的执行流程,使它跳转到攻击代码,最为基本就是溢出一个没有检查或者其它漏洞的缓冲区,这样做就会扰乱程序的正常执行次序。通过溢出某缓冲区,可以改写相近程序的空间而直接跳转过系统对身份的验证。原则上来讲攻击时所针对的缓冲区溢出的程序空间可为任意空间。但因不同地方的定位相异,所以也就带出了多种转移方式。

1、  unction Pointers(函数指针)
在程序中,“void (* foo) ( )”声明了个返回值为“void” Function Pointers的变量“foo”。Function Pointers可以用来定位任意地址空间,攻击时只需要在任意空间里的Function Pointers邻近处找到一个能够溢出的缓冲区,然后用溢出来改变Function Pointers。当程序通过Function Pointers调用函数,程序的流程就会实现。这个你可调用Linux下的superprobe程序体验一下。

2、  ctivation Records(激活记录)
当一个函数调用发生时,堆栈中会留驻一个Activation Records,它包含了函数结束时返回的地址。执行溢出这些自动变量,使这个返回的地址指向攻击代码,再通过改变程序的返回地址。当函数调用结束时,程序就会跳转到事先所设定的地址,而不是原来的地址。这样的溢出方式也是较常见的。你在使用漏洞扫描(Unix下的SATAN或者NT下的Retina)器时,最好是多注意“stack smashing attack”的字样。呵呵。

3、  Longjmp buffers(长跳转缓冲区)
在C语言中包含了一个简单的检验/恢复系统,称为“setjmp/longjmp”,意思是在检验点设定“setjmp(buffer)”,用longjmp(buffer)“来恢复检验点。如果攻击时能够进入缓冲区的空间,感觉“longjmp(buffer)”实际上是跳转到攻击的代码。像Function Pointers一样,longjmp缓冲区能够指向任何地方,所以找到一个可供溢出的缓冲区是最先应该做的事情。

●植入综合代码和流程控制

常见的溢出缓冲区攻击类是在一个字符串里综合了代码植入和ctivation Records。攻击时定位在一个可供溢出的自动变量,然后向程序传递一个很大的字符串,在引发缓冲区溢出改变ctivation Records的同时植入代码(权因C在习惯上只为用户和参数开辟很小的缓冲区)。植入代码和缓冲区溢出不一定要一次性完成,可以在一个缓冲区内放置代码(这个时候并不能溢出缓冲区),然后通过溢出另一个缓冲区来转移程序的指针。这样的方法一般用于可供溢出的缓冲区不能放入全部代码时的。如果想使用已经驻留的代码不需要再外部植入的时候,通常必须先把代码做为参数。在libc(熟悉C的朋友应该知道,现在几乎所有的C程序连接都是利用它来连接的)中的一部分代码段会执行“exec(something)”,当中的something就是参数,使用缓冲区溢出改变程序的参数然后利用另一个缓冲区溢出使程序指针指向libc中的特定的代码段。对缓冲区溢出有兴趣的朋友,有必要找找eEye公司开发的Retina发现IIS4.0的那个缓冲区溢出漏洞引起的很多黑客攻击实例来看看。你可以在http://www.safefan.com(傲气雄鹰网络安全小组)找到关于它们的资料,做为网络管理人员最因该熟悉它们。程序编写的错误造成网络的不安全性也当受到重视,因为它的不安全性已被缓冲区
溢出表现的淋漓尽致了。

缓冲区溢出的防患

缓冲区溢出的漏洞被发现到利用以来一直都是网络安全领域的最大隐患,很多安全人士均对这些漏洞做了仔细的研究,但是缓冲区溢出的完全防止往往因为这样那样的人为或者其它的因素仍显得有点力不从心。在这篇文章里,就目前缓冲区溢出漏洞的几种保护方法做个简单的描述。

►正确的编写代码

我想在编写代码的时候一般不会有人故意想要发生错误的,但是丝毫的错误往往会造成严重后果(C语言多是字符串因0收尾,往往就是一个很不安全例子)。所以正确的编写代码是很关键的。

在编写时以免错误发生最原始的方法就 是用grep来找出源代码中较容易产生的漏洞的库的调用。像对sprintf和strcpy的调用,这两个函数都不会检查参数输入的长度。有的在编写的时候采用了sprintf和strcpy的替代函数来防止,但是还是会有问题发生的。因为这些错误的隐蔽性,所以就出现了查错工具faultin-jection。faultin-jection可以通过人为随时产生一些缓冲区溢出来找到代码的安全漏洞。只能说faultin-jection等类似的工具可以让编写时缓冲区溢出的漏洞更少一点,而完全的没有则是不现实的。因为它们确实不可能找到所有的溢出缓冲区的漏洞。编写时重复的检查代码的漏洞可以使程序更加完美和安全。

►非执行的缓冲区

在老版的UNIX系统中,程序的数据段地址空间是不可执行的,这样就使得黑客在利用缓冲区植入代码时不能执行。但是现在的UNIX和Windows系统考虑到性能和功能的速率和使用合理化,大多在数据段中动态形式的放入了可执行的代码,为了保证程序的兼容性不可能使用所有程序的数据段不可执行间。但可以通过只设定堆栈数据段不可执行,这样就很大程度上保证了程序的兼容性能。UNIX、Linux、Windows、Solaris都已经发布了这方面的补丁。

►检查数组边界

数组边界检查完全没有缓冲区溢出的产生,所以只要保证数组不溢出,那么缓冲区溢出攻击也就只能是望梅止渴了。实现数组边界检查,所有的对数组的读写操作都应该被检查,这样可以保证对数组的操作在正确的范围之内。检查数组是一件叫人头大的事情,所以利用一些优化技术来检查就减少了负重。可以使用Compaq公司专门为Alpha CPU开发的Compaq C编译器、Jones&Kelly的C的数组边界检查、Purify存储器存取检查等等来检查。

所有的缓冲区溢出漏洞都归于C语言的“功劳”。如果只有类型-安全的操作才可以被允许执行,这样就不会出现对变量的强制操作。类型-安全的语言被认定为Java和ML等,但作为Java执行平台的Java虚拟机是C程序,所以攻击JVM的途径就是使JVM的缓冲区溢出。
。。。。。。

►程序指针完整性检查

程序指针完整性检查在程序指针被引用之前检测到它的改变,这个时候即便是有人改变了程序的指针,也辉因为系统早先已经检测到了指针的改变而不会造成指针对利用的。但程序指针完整性检查不能解决所有的缓冲区溢出问题;如果有人使用了其它的缓冲区溢出,那么程序指针完整性检查就不可能检测到了。可话又说回来了,程序指针完整性检查在性能上却有着很大的优势,并且有良好的兼容性。