地狱怪客

[转]用IDAPython进行漏洞挖掘

原地址:http://zery.wang/2017/09/11/SearchOverflow/

作者代码有问题,

for Function in Functions:
FuncAddr = LocByName(Function)
if FuncAddr == BADADDR:
break

需要改成

for Function in Functions:
FuncAddr = LocByName(Function)
if FuncAddr == BADADDR:
continue

直接break还怎么遍历=.=

前言

初学IDAPython的时候,参考了HW的mo大神的作品,以及bugscam,而这些都是IDC脚本,所以打算将这些漏洞挖掘的功能用IDAPython实现出来,也算是熟悉IDA里的API用法,于是就有了searchOverflow这个工具。

IDAPython

IDA不光有强大的反编译技术,同时还将大量反编译功能作为API。最初IDA上使用的是IDC脚本来操作这些API,后面开发了IDAPython插件,将这些API交给了强大的Python。文档见这里

更多详细的用法也可以参考《IDA pro权威指南》一书。

思路

mo大神的脚本进行漏洞挖掘的思路很简单,检索代码段中所有能产生缓冲区溢出的危险函数,并向上回溯其交叉引用,也就是调用这些危险函数的点,最终将这些信息输出出来。

这个思路的确是简单粗暴,绝大部分源码漏洞扫描工具也是这么想的,但是由于脚本只是检索危险函数,还需要人工对每个调用点进行排查。

因此为了减少这部分工作量,我们需要借助IDA强大的功能让脚本来对调用点进行排查,以strcpy(dest,src)为例,排查的思路即检查destsrc的长度,如果src不大于dest,那显然这个地方就算是用了危险函数,也是不存在任何问题的。如果src大于dest,说明这个地方可能存在问题,具体需要继续回溯传入变量是否为外部可控。

且不说判断参数长度,回溯参数变量来源这个工作实在是巨复杂,我张口就来,这东西你能写出来的话,可以考虑出个商业级漏洞挖掘工具了。

所以searchOverflow里只选取了几种参数的情况来判断长度,把回溯参数来源这个工作还是留给人工来做吧。

具体实现

其实光从思路来看,具体实现就是在python里用IDA提供的各式各样的API去完成如下工作:

1)检索危险函数
2)获取传入函数的第一和第二个参数
3)确定该参数大小

对于第一条来说,需要LocByName(),RfirstB()/DfirstBRnextB()/DnextB()这几个API,前一个是检索函数名关键字对应的地址,后四个可以查找对该地址的交叉引用,只需这两个API即可遍历出所有危险函数的交叉引用。

在能够确定危险函数调用的位置后,需要获取传入该危险函数的参数,传参有很多种方式,比如push eax,或者mov [esp+4],eax,因此不能只以push关键字来确定,应以call危险函数处的SP(StackPointer)为基准,往前回溯使SP发生变化的指令,并且满足如下条件:

1
2
spd – (index * 4) == spdBase
#第一个参数的话index为1,第二个参数index为2

则可以确定该指令为对应危险函数call处的传参指令。

传入的参数也有很多种情况,对于参数为一个地址(有offset修饰)的情况,只需处理该地址处变量大小;对于push eax,或者mov [esp+4],eax这种情况,需要往前回溯对寄存器eax有过赋值操作的指令,并回溯到最初来源,试图找到形如mov eax,offset 0xdeadbeef或者lea eax,[ebp+4]的情况;对于push [eax],由于IDA在静态分析,这种情况需要的回溯更复杂,要以静态的方式来确定运行时的数值,差不多相当于实现一个比IDA还强大的反编译工具了,因此这种情况不考虑。

最后一步,就是要来确定参数长度。对参数也分三种情况考虑:参数在栈中,即参数来源为[ebp+n]类似形式;参数在固定地址,如堆和静态变量;参数为一个结构体的成员。在实际中,将栈也当作一种结构体来处理,因此最终其实只算做两种情况。

但得益于强大的IDA,确定一个参数的缓冲区大小的方法并不复杂,由于IDA在静态分析时基于栈分析,IDA在每个函数开头对缓冲区中的变量名进行命名时会使用其偏移,如:

1
2
3
4
5
.text:00404000 sub_404000 proc near
.text:00404000
.text:00404000 var_8 = dword ptr -8
.text:00404000 var_4 = dword ptr -4
.text:00404000 arg_0 = dword ptr 8

那么就可以计算出var4的长度为8-4=4h

整个过程大致是这样,当然在某些细节还有比较蛋疼的处理,bugscam里在确定缓冲区大小时对于一些情况用最大值和最小值来处理,增加了很大容错。

不得不说的

虽然我上面说了那么多,但显然这个东西其实问题不少,除了上面提到几个明显无法处理的情况外,对于一些自己封装实现的危险函数,或者很多win api,这些函数不在检索的范围内,自然没法搜到这些函数存在的问题。此外,某些版本的编译器在处理这些字符串处理函数时会使用硬编码的方式,比如之前在逆向时经常看到的repe movsb这类指令,其实就是编译器将strcpy函数的实现直接编码到调用的位置,这种情况显然也不能被searchOverflow检查到。

结束&展望?

searchOverflow并不算原创,基本上都是参考了bugscam。bugscam是Halvar Flake大神在2003年完成的,Halvar Flake大神多次在BlackHat上也分享过二进制漏洞挖掘相关议题,但在这十多年来却没有对bugscam进行过任何更新,我在开始写searchOverflow这个工具前也想过,直到完成后,我才明白为什么Halvar Flake为什么放弃了这条路,整个基于IDAPython或者IDC的自动化漏洞挖掘都局限于IDA这个工具,依赖于传统的反编译技术,而就目前来说,传统反编译技术上已经很难有大的提升。

不过就二进制漏洞挖掘上来说,并非只有这条路,站在二进制漏洞挖掘的十字路口上,近几年正在慢慢崭露头角的符号执行技术,也许是另一条路。

所以searchOverflow大概也不会再更新了,至少它最初的目的已经达到了。

码字很辛苦,转载请注明来自人生在世《[转]用IDAPython进行漏洞挖掘》

评论