吴石:几种软件缺陷的可能利用方法

来源:腾讯

腾讯科技讯 3月19日消息,在为期两日的网讯网络安全技术峰会上,IM及协议安全专家吴石分享了他在软件缺陷上的研究成果,以及在IM的第三方软件存在的发现等话题。

以下为吴石讲演实录:

吴石:我的题目是几种不常见的软件缺陷,但是内容可能有一些出入。第二,我也是一个初学者,PPT里可能也存在很多问题,也希望各位专家指出来。

我们先看看常见的软件缺陷:第一,栈溢出。就是在栈中申请一段内存,一般是数组或字符串,在对这段内存做操作的时候,错误的写操作可能导致栈中也特殊意义的地址被用户的输入内容所控制。最早发现是一些字符串操作的函数中,比如strcat,后来又发现在Strncpy如果不正常操作的话也会出现这个问题。最后有一个Windows UNicode处理的函数如果不正常使用也会出现这样的问题。下面我介绍的是整数溢出的问题。

整数溢出是多发于的情况,特别是一些加、乘的操作出现在内存前面就要特别注意了。加或者乘出来的数不一定比原先两个数大。还有一个正负数比较的问题,或者是符号扩展的问题。即使现在这个问题仍存在于很多软件中。但是在很多流行软件中已经很少出现了,比如微软的软件、国外大公司的软件。但是在国内软件这个问题依然是很多的。这个问题在JAVA软件中也经常存在。例如银行系统,系统错误处理,把别人帐号上扣掉的金额,一个正的金额加到你的帐号上。

第二,heap overflow。这是现代程序C语言主要申请分配方法,所以他比栈溢出比例大的多。微软做了很多防护措施,所以它利用起来是非常复杂的。尤其是WindowsXP2之后的版本,比如vista。堆管理主要利用两张表,freelist、lookaside,freelist[0]代表着一些不规则的可以利用的chunk,尤其是比较大的chunk。freelist[1] – freelist[n]代表2的整数次方可以利用的堆中的chunk。利用这样堆溢出的问题,你需要对Windows堆管理非常熟悉。比如有人通过freelist[0]这个链表成功利用。目前有一个immdbg的程序对这种研究利用是很有帮助的。因为他把堆分配的内容都可以显示出来。对vista软件的攻击,理论上应该是不存在的。因为vista对堆管理有严格控制,但是有很多软件使用自己的内存管理方法,比如OFFICE,他们自己堆管理方法和内存方法是和vista不一样的,这些方法往往采用教科书的方法或者以前系统的方法,所以他们这些方法是有可能被利用起来。

第三,未初始化的问题。栈上的问题由德国人在06年详细讨论过。头一次压栈的时候,在栈上写需要内容,然后函数退出,导致栈顶上移,有问题的函数压栈时正好利用这段栈空间,如果函数中发现了未初始化问题,比如数组,那么其内容刚好是我们刚写入的内容的栈空间,就可能被利用。先把堆里的大部分内容写成自己需要的内容,未初始问题发生时,比如堆里指针的内容就可能指向我们需要的内容。目前这个问题是大量存在的,OFFICE存在了很多。比如这个月微软补丁,excel那一个补丁里就包括很多这样的问题。你可以对比新旧的OFFICE软件,你发现OFFICE2007有一些新加的代码就是做初始化工作的。

第四,二次释放或者叫double free问题。内存泄露是现代软件大敌,特别是服务器软件。有很多程序员害怕发生这样的问题,申请内存时总是想释放它,结果释放多了几次,这样也会有安全问题。曾经在linux上的方法很巧妙、经典,但是在目前Windows上比较难以利用。很多软件采用自己管理内存方法,那么就很可能被利用到。

我们现在可以讨论一下可以利用软件问题,就是可以执行任何代码软件的充分条件,就是将任意4字节内容写到任意内存地址。如果满足这个条件的话,它肯定是可以被利用的。这个条件叫做充分条件。在栈的溢出或者堆溢出,将任意4字节内容写如某些特定的内存地址,而这些地址有特殊含义。由于代码流或者数据流在X86里是混合的。还有一些更特殊的情况,任意1字节写进某些特定的地址,这些地址刚好有特殊含义,可能也会引发这个问题。所以我们总结一下,离充分条件越近的,越有可能这个问题被利用,可以被执行任意代码。

关键问题就在于写内存操作,而不在于读内存。写内存操作可以改变很多东西,读内存的话,即使发生异常的话,这些问题也往往不能被利用。当我们要从大量程序中快速找到可以利用的漏洞的时候,要首先找到那些写操作引起的异常。第二,比较少出现的异常。未初始化问题发现,就要了解一下未初始化缺省填充的字符。这是一个例子,我们可以详细分析一下哪些结果是被利用的,根据我们原则:第一,写操作的过程。这个看起来比较复杂,而且可能是一个整数溢出的问题。

下面讲讲我的心得,另类栈溢出的问题。这是一个递归函数,与一般的栈溢出不同的是向下溢出,由于栈内存空间是有限的,这个一直压栈的过程会导致内存读写到栈分配内存空间之外,找出问题的关键在于代码流程图中找到可以由用户控制的“圈”。一般情况下,这种会造成程序崩溃,某些特殊情况下会造成执行恶意代码。这种情况是很特殊特殊的。就是两个现成的栈紧紧挨在一起。第二,栈每次分配空间都比较大。第三,编译器要比较老的那种才可以。新的编译器会检查这样的情况。

我们考虑完栈溢出的话,可以考虑相应的堆溢出情况,堆溢出情况更加复杂,但是更有灵活性。在一部分情况下有可能被利用,如果你利用这种方法去寻找的话。

安全问题可以借用的数学理论,因为数学方法是人类经过几千年总结出来的,具有普遍意义和前瞻性。我们可以看到安全问题很大部分是由攻击者和研究者发现一条可以控制的代码或者数据流,这些是程序员没有想到的。传统堆栈溢出基本上可以看作是系统或者库函数的比较不明显的后门,攻击者或研究员对这些后门比一般程序员了解。基本上一个程序可以看作是两张图,一张叫做数据流图、一张叫做代码流图,发现安全问题就是在这两张图上发现一条路径,发现这条执行路径的话,可以执行攻击者所需要的功能。

针对这样的问题,数学上准备了相应的理论,图论这方面的研究早就有了。我认为以后研究者所做的工作就是根据自己经验,以图论理论进行指导,利用计算机快速找到一条安全问题的路径。而厂商要做的就是利用自己强大的计算能力,快速遍历所有可知性路径来证明软件安全性。这是我的一点心得体会。

第三个可以借用的理论叫做编译原理。C/C++程序都需要经过编译优化措施执行,在这个编译过程中编译程序事实上获得了很多有用的信息,如何在软件上安全利用这些信息,目前很多公司进行了很多有意义的尝试。但是离真正有用还是有一定差距。这种方法理论上应该比Fuzzing的方法好,他知道软件结构可以较快发现软件的问题。

下面又是我的一个体会,我比较倾向应用这样方法得到一些可用信息后,自动构造一些测试用例去测试,这样的话,测试性能和人力成本就会大幅降低。

第四个可以借用的数学理论就是人工智能和统计理论。目前的话,有很大的问题。因为样本库太少。我们可以首先做模板方法,比如前面提到+或者×出现内存分配前面,我们可以做一个模板,用这个模板去扫描原代码,试图发现这样的问题。从这些模板匹配方法再慢慢过度到模式匹配方法。但是人工智能和统计理论对其他的一些网络安全问题有很大的作用。比如说DDOS或者靠数量来使得网站、程序、崩溃的这些方法,它应该是会取得很大的作用。它对网络流量的分类目前还是取得一定的成功,不能说很大的成功。

下面举一个例子,这是MSOFFICE内存管理。以2007为例,以Msopvalloccore分配内容,以msofreepv释放内存,每次释放内存时都会改变地址值,让地址值走向政正确的位置。分配内存时,读出freelist第一个元素,符合申请的话,就把内存空间提供给需要申请的函数。同时把freelist下一个元素作为表头。释放内存时,他是把指向释放内存的地址作为表头,把前一个指向下一个元素。可能遇到的问题,有一个double free问题,这在OFFICE内存方法上是一个知名弱点,因为他对这个问题没有检查,很有可能造成代码执行。具体方法我下一页PPT会提到。这样就启发我们在很多软件里都自己写内存管理方法,这样的话,一些经典问题就有可能在自己写的管理方式中被利用。尽管OS层已经不存在这个问题。

攻击的思路。实际上这是一个代码程序解释。首先我们申请很多内存,在地址上填上我们所需要的内容,把这些内存释放掉。为了保证程序成功的可能性更大一点,我们也做这样一个工作。申请一段内存,两次free后,OFFICE内存管理会错误把表地址作为释放后可以利用的内存地址,再申请一段比较小的内存,填上需要覆盖的内存地址。再请几段小内存,OFFICE内存管理会错误把需要的内容填到需要覆盖的内存。ms第二个内存问题,未初始化利用。他和double free利用也差不多。这个提醒微软不仅要把系统安全搞安全就可以了。在EXCEL2003就有这样的低级错误。堆溢出的问题在这套内存管理体系也是存在,堆溢出而且是不受OS这种保护的。它是可以很容易的利用的。

下一个问题是数组越界读写,这也是一个漏洞发现的主流渠道。因为以前这些有问题的库函数的方法并不能发现漏洞了,既使是整数溢出也非常罕有了。整数溢出目前也是比较少了,但是数组越界在一定范围内还是存在的。目前我找到很多问题还是数组越界读写。如果这个传入的参数存在一个写的操作,这个内容就可能写到任意的内存地址。

下面我讲讲IM软件普通存在的问题。第一个问题,集成第三方软件太多了,这些软件往往不提供源代码,造成开发人员对接口的理解不完整或不正确。因为IM软件有很多东西是自己不擅长的,往往采用第三方软件。但是第三方软件代码质量不一定绝对安全,所以会造成一些安全问题。还有一些是存在在继承的第三方软件,存在于老的版本,老的版本报出安全问题之后,IM软件并没有更新所使用的软件包。IM软件缺省认为客户端或服务器是可信任的或者部分可信任,这就是造成极大安全。特别是比较复杂的功能,如语音视频功能中,安全问题是大量存在的。即时聊天软件有的采用了简化加密算法,这对于对抗暴力破解性能很差。还有一些软件不正确使用加密方法,比如使用了一个固定密钥,密钥存在于客户端制之中,如果有人把密钥提取出来就可以知道聊天内容。第四,软件团队不稳定,造成软件代码质量很差,而且指导思想不一致,可能在一些国产然间中发现大量未完成功能,这些功能都可能是安全隐患。

我今天演讲就到这里,谢谢!

现场问答:

问:首先你说这个编译器里面会产生一些代码或者你刚才说的微软里面存在的问题,编译器为什么会发生这种情况?还有一点,像微软默认申请,有什么说法?像你说的发现很多软件未完成的功能,这种软件一般都比较大,带了很多库,你是全部去看?

吴石:我不知道。主要是看可以执行的地方。

问:你怎么知道他什么地方可以执行?

吴石:首先通过调试器去看,首先要使用它,然后加一个调试器。然后看他可能执行的代码。

问:你一般分析这种东西需要多长时间?

吴石:因为我不是全部分析他的内容,我只是分析代码流如果存在问题的话。

问:关于图论来减少软件BUG这个东西有没有实践?

吴石:我自己目前在做这方面的东西。而且你看IDA软件,我刚才提到一个德国人,他就是把代码流程画成一张图的形式给你看。

问:通过源码生成图还是可执行代码?

吴石:可执行代码。

问:会不会产生很多遗漏的地方?

吴石:这个东西是IDA做的,目前我没有看到遗漏的地方或者比较少。

问:能不能考虑一下通过这种数学的方法解决软件缺陷?

吴石:我刚才提到一家公司是用编译原理方法去解决软件问题和软件安全问题,它的这些东西是在所有大的厂商里现在都在用。所以这个前景还是有的。

问:ms他们会有一个自己的内存管理方法,为什么有操作系统不用,而自己建立?

吴石:因为有一些特殊需求,操作系统内存管理方法往往对他的特殊需求支持的不是很好或者是产生的不是很快,最关键原因他认为这个方法不是很快,所以他用自己的一套方法管理内存。

问:能不能举例说明一下,除了快之外还有没有其他因素?

吴石:如果利用OS方法的话,因为每次申请内存可能跟OS2的N次方,它的内存碎片产生的最少,对堆管理不可利用的产生的最少。如果不是2的n次方就会产生很大的碎片。如果利用自己的内存管理方法,他知道它会产生这样的内存分配,所以他可以避免了这样的问题,导致性能有所提升。

问:我想问一下,刚才提到很多安全问题已经不是像以前简单的危险函数使用的问题,已经是一些比较深层次的问题,现在针对这种情况有没有什么好的方法或者是好的软件能够达到自动化的程度,能达到多少?

吴石:如果有源代码的话,Coverity目前是最好的。但是如果是安全问题,好不了多少。大量还是要靠人工分析,如果完全想完全解决安全问题。他对其他的软件问题,比如内存泄露或者未初始化这些问题做得还是不错。他为什么对安全问题做得不好呢,我估计里面没有一个真正懂安全的人去指导这个工作。

问:能不能介绍一下挖掘功能使用到哪些工具?

吴石:我本来想写一个光速入门的ppt,因为我的笔记们没有带电源,就没有写。挖掘有几个技术条件:第一,对调试很了解。对软件结构很了解。对所想挖掘的功能要比较了解。第二,你要有一点想法或者有一点经验,知道软件在什么情况下可能会出现问题。然后下一步要写一个程序去把这样的问题找出来,而不是自己去找。如果自己去找的话,这样的精力或者时间花费太多,我以前是自己去找,但是效果不是很好。

相关日志

发表评论