软件漏洞分析技巧分享

作者:riusksk【TSRC】

在日常分析软件漏洞时,经常需要耗费比较长的分析时间,少则几小时,多则数天,甚至更久。因此,经常总结一些分析技巧是非常有必要的,针对不同的漏洞类型采取不同的分析思路和技巧,可以有效地提高分析速度。对于一些被曝出来的热门0day,网上一般都会有分析文章,但一般都是“结论性分析”,也就是直接帖漏洞代码,指出哪里出错,而非“思路性分析”。如果你经常分析漏洞的话,会发现占用你分析时间的往往不是分析漏洞代码,而是定位漏洞代码。所以说,调试分析漏洞有时就是看下断点下得准不,再加上一些胡猜乱想来推测,最后才是分析漏洞代码了,如果熟悉汇编指令,这个就不是问题了。

下面是笔者就以往分析过的若干实例漏洞,总结出的一些小技巧。不过,技巧甚多,篇幅有限,此处仅列举一些比较个人常用的方法,也欢迎各位分享自己的一些分析技巧,大家共同学习探讨。

技巧一:快速定位JS代码调用的IE类成员函数

CVE-2011-0027 Microsoft Data Access组件整数溢出漏洞是Pwn2Own 2010黑客大赛中被用来攻破IE8浏览器的漏洞,其中关键的漏洞触发代码如下:

localxmlid1 = document.getElementById('xmlid1').recordset;    // 获取xml元素xmlid1的recordset,即数据库表的记录集
localxmlid1.CacheSize = 0x40000358;    // 设置能够被保存的记录条数,此值最终造成整数溢出

现在我们就介绍一种快速定位上述两行代码将对应调用的IE类成员函数,首先用IDA加载漏洞文件msado15.dll,并允许加载微软符号表,然后选中“Function name”一栏,按“Alt + T”快捷键弹出搜索框,输入搜索关键字“cachesize”:

通过按“Ctrl+T”可继续搜索下一个,最后找到两个相关函数:

CRecordset::put_CacheSize(long *)
CRocordset::get_CacheSize(long)

我们对CRecordset::put_CacheSize下断点验证下前面的猜测,用Windbg附加IE进程运行后打开poc.html,确实断在put_CacheSize,通过查看参数可以看到poc.html中设置的CacheSize值0×40000358,说明CRecordset::put_CacheSize确实是设置CacheSize值的函数:

0:005> g
Breakpoint 1 hit
eax=40000358 ebx=04bdcfd8 ecx=6e61d340 edx=00000000 esi=01fbf144 edi=00000000
eip=6e6ac957 esp=01fbeb58 ebp=01fbf040 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
msado15!CRecordset::put_CacheSize:
6e6ac957 8bff            mov     edi,edi
0:005> dd esp
01fbeb58  6e62f3ec 04bdcfd8 40000358 00000000
01fbeb68  01fbf074 04bdcfd8 11000011 00000000
01fbeb78  03570ae8 004ad070 00538a30 00000088
01fbeb88  00470000 00000002 03570760 01fbec84
01fbeb98  76fc3193 00470138 76fc316f 764736b8
01fbeba8  00000000 00470000 03570768 00518a78
01fbebb8  004767b8 004768e4 00559258 00476db8
01fbebc8  76f8d74d 0051d968 00472a98 01fbedc0

我们对CRecordset::put_CacheSize下断点验证下前面的猜测,用Windbg附加IE进程运行后打开poc.html,确实断在put_CacheSize,通过查看参数可以看到poc.html中设置的CacheSize值0×40000358,说明CRecordset::put_CacheSize确实是设置CacheSize值的函数:

技巧二:通过页堆快速定位堆漏洞代码

页堆是windows 2000 引入的调试支持功能,简称DPH(Debug Page Heap),启用该机制后,堆管理器会在堆块后增加专门用于检测溢出的栅栏页,当数据溢出触及栅栏页便会立刻触发异常,此时往往就是触发漏洞的最及时的位置,它不仅适用于堆溢出,对于其它类型的堆漏洞也是适用的。

以CVE-2013-0077 微软DirectShow堆溢出漏洞为例,通过以下命令开启页堆(gflags):

gflags.exe –i player.exe +hpa

开启页堆hpa后,重新附加运行后,在复制数据到堆边界时断下:

(4b8.358): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=000000c3 ebx=003fac98 ecx=00000003 edx=000000f7 esi=001bbdd4 edi=003fb000
eip=7d0706d0 esp=02a5f650 ebp=02a5f658 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00010202
quartz!ParseSequenceHeader+0x114:
7d0706d0 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

上面就是断在复制数据导致溢出的指令,通过分析其所在函数往往很容易定位漏洞代码。如果不开启页堆,直接以默认形式调试的话,你会发现是断在以下指令:

(4c8.6bc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=003f0000 ecx=41414141 edx=03128e40 esi=03128e38 edi=00000012
eip=7c930efe esp=0465f998 ebp=0465fbb8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
ntdll!RtlAllocateHeap+0x653:
7c930efe 8b39            mov     edi,dword ptr [ecx]  ds:0023:41414141=????????

这已经是堆溢出后导致的内存读取异常了,不再是触发漏洞时最原始的场景了。因此开启页堆后,会更方便你去定位漏洞代码。

技巧三:基于堆分配记录定位整数溢出漏洞代码

以CVE-2011-0027 Microsoft Data Access组件整数溢出漏洞为例,这个漏洞就是在Pwn2Own 2010黑客大赛中,荷兰黑客Peter Vreugdenhil利用它来攻破Win7上的IE8浏览器的。

下面是打开poc.html后的异常情况:

(7b8.278): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000036b ebx=0000035b ecx=00000000 edx=00000001 esi=088c8000 edi=00000000
eip=6887746f esp=044dee84 ebp=044dee88 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
mshtml!CImpIRowset::HRowNumber2HROWQuiet+0x23:
6887746f 8906            mov     dword ptr [esi],eax  ds:0023:088c8000=????????
0:005> !heap -p -a 088c8000
    address 088c8000 found in
    _DPH_HEAP_ROOT @ 8821000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 88227ec:          88c7298             d64-          88c7000             2000
    72eb8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77034ea6 ntdll!RtlDebugAllocateHeap+0x00000030
    76ff7d96 ntdll!RtlpAllocateHeap+0x000000c4
    76fc34ca ntdll!RtlAllocateHeap+0x0000023a
    730d975d MSDART!MpHeapAlloc+0x00000029
    6e5406e7 msado15!CRecordGroup::AllocateHRowRange+0x00000085
    6e540650 msado15!CRecordset::PrepareForFetch+0x000000e2
    6e5d44ae msado15!CRecordset::MoveAbsolute+0x000003e3
    6e5680a5 msado15!CRecordset::_MoveFirst+0x0000007d
    6e5d7957 msado15!CRecordset::MoveFirst+0x00000221
    6e54fde6 msado15!CRecordset::Invoke+0x00001560
    71dcdb38 jscript!IDispatchInvoke2+0x000000f0
    71dcda8c jscript!IDispatchInvoke+0x0000006a
    71dcd9ff jscript!InvokeDispatch+0x000000a9
    71dcdb8a jscript!VAR::InvokeByName+0x00000093
    71dcd8c8 jscript!VAR::InvokeDispName+0x0000007d
    71dcd96f jscript!VAR::InvokeByDispID+0x000000ce
    71dce3e7 jscript!CScriptRuntime::Run+0x00002b80
    71dc5c9d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
    71dc5bfb jscript!ScrFncObj::Call+0x0000008d
    71dc5e11 jscript!CSession::Execute+0x0000015f
    71dbf3ee jscript!NameTbl::InvokeDef+0x000001b5
    71dbea2e jscript!NameTbl::InvokeEx+0x0000012c
    71db96de jscript!NameTbl::Invoke+0x00000070
    685aaa7b mshtml!CWindow::ExecuteTimeoutScript+0x00000087
    685aab66 mshtml!CWindow::FireTimeOut+0x000000b6
    685d6af7 mshtml!CStackPtrAry<unsigned long,12>::GetStackSize+0x000000b6
    685d1e57 mshtml!GlobalWndProc+0x00000183
    770e86ef USER32!InternalCallWinProc+0x00000023
    770e8876 USER32!UserCallWinProcCheckWow+0x0000014b
    770e89b5 USER32!DispatchMessageWorker+0x0000035e
    770e8e9c USER32!DispatchMessageW+0x0000000f

根据上面异常的信息,可以知道程序是在向地址为0x88c7298,大小0xd64的堆块写入数据时造成堆溢出了。再根据!heap命令中返回的栈回溯信息可以知道,被溢出的堆块是在CRecordset::MoveFirst函数中调用MpHeapAlloc函数分配的,调用该分配函数的上层函数是CRecordGroup::AllocateHRowRange,对此函数下断,跟进后: (代码请点击“阅读原文查看”,微信字数限制。。。)

该漏洞主要是由于对CacheSize整数值未作有效判断,导致经CacheSize*4+4造成整数溢出,当以CacheSize*4+4结果作为分配堆块的大小时,由于分配过小堆块就会造成堆溢出。

技巧四:基于条件记录断点的漏洞分析技巧

以CVE-2012-0774 Adobe Reader TrueType字体整数溢出漏洞为例,该漏洞主要是在解析TTF字体中的虚拟指令导致的溢出,为了确定导致溢出的是哪条虚拟指令,就可以通过设置条件记录断点来实现。

打开poc触发崩溃后,通过栈回溯定位漏洞函数,此处标记为VulFunction,它就是用于索引虚拟指令处理函数的。

通过快捷键Shift + F4对VulFunction下条件记录断点,记录每次调用VulFunction时对应的虚拟指令索引号

设置好后重新加载AcroRd32.exe运行(不要关闭调试器否则前面设置的断点可能失效),打开poc.pdf运行后查看日志窗口:

导致漏洞的虚拟指令索引号为0×26,通过苹果官方提供的指令集https://developer.apple.com/fonts/ttrefman/RM05/Chap5.html,可以查到字节码0×26对应的虚拟指令为MINDEX,正是该条指令导致的溢出。

技巧五:基于JS日志的漏洞分析技巧

有时在调试IE漏洞时,我们需要样本中的JS代码的执行先后情况,通过添加一些数学函数,比如math.atan2、math.asin等,还可以使用其它函数,然后通过对jscript!Js::Math::Atan(或者其它函数)下断点来输出log,以方便分析者观察执行情况。比如poc中有如下js,其中的math.atan2是我们添加的:

Math.atan2(0xbabe, "[*] Creating object button...");
    var obj = document.createElement("button");

    Math.atan2(0xbabe, "[*] Assigning data to title...");
    obj.title = data.substring(0,0x40000-0x58); 

    Math.atan2(0xbabe, "[*] Let's AppendChild");
    div_container.appendChild(obj);

通过设置以下断点:

bu jscript!JsAtan2 ".printf \"%mu\", poi(poi(poi(esp+14)+8)+8);.echo;g"

运行后在windbg上的输出窗口就会看到:

[*] Creating object button...
[*] Assigning data to title...
[*] Let&#039;s AppendChild

技巧六:通过虚拟机快照来固定堆地址

最早听到这个方法,是instruder在QQ群里面提到的。由于在分析漏洞时,尤其是堆漏洞时,每个重新加载运行时,分配的堆地址都是固定,无论是分析还是写文档,都不太利用于我们分析和描述。因此我们可以先将程序调试已经完成堆分配的某个地址,然后将其保存为虚拟机快照,等我们需要再重新开始调试时,可通过恢复先前保存的快照来重新调试,那么此时的堆地址跟之前分析的地址都是固定的。

技巧七:监控堆分配释放动作来分析UAF、double free漏洞

以之前分配libpng某个double free漏洞的分析为例,主要是通过对释放函数free进行监控并输出记录来分析的。程序崩溃时的栈回溯如下:

重新用windbg加载,通过监控堆块的释放过程,可以发现它在对其中某堆块进行了双重释放,先下断:

bu 3440D279 ".if(1){.echo EnterVulnFunc;gc}"
bu 6e264b6c ".if(1){.echo Free heap block; dd esp l4;gc}"

输出结果:

EnterVulnFunc
Free heap block
0011bc5c  3441e2a2 138f0020 3b906313 10027b64
Free heap block
0011bc5c  3441dc6c 138f0020 3b906313 10027b64
(1508.e84): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=138f0018 ebx=138f0020 ecx=6e287a7e edx=10028a70 esi=008a0000 edi=00000000
eip=77691f88 esp=0011bbe8 ebp=0011bbf8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
ntdll!RtlFreeHeap+0x3a:
77691f88 80780705        cmp     byte ptr [eax+7],5         ds:0023:138f001f=??

可以看到它对同一个堆块进行多次释放。在IE漏洞分析中最常见的还是UAF,如果想快速定造成UAF的IE对象,就需要对对象的分配的释放进行释放,这个可通过调试器脚本来实现,或者像之前h4ckmp同学基于Windbg接口写的EXE工具,可以快速分析出导致UAF漏洞的对象,如下图所示。

技巧八:基于污点追踪的分析方法

污点追踪理论挺好的,但成品往往不容乐观。污点追踪犹如“七伤拳”一般,“先伤己,再伤人”,开发污点追踪工具,不仅费时费力,而且开发完成后,运行比较大的工具往往需要运行很长时间,比如IE、Adobe等软件,有时甚至需要整整一天的时间,这种一般是在调试分析不方便的时候才使用的,主要针对文件格式漏洞。另外,你也可利用pin等动态插桩框架开发出动态分析工具,针对特定函数挂钩,比如堆分配与释放函数,也可以用它来实现快速分析。

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2014-03-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Web项目聚集地

Javascript中的异步

9020
来自专栏北京马哥教育

《linux下crontab的深入分析》

一)cron服务的介绍 cron是Linux系统下一个自动执行指定任务的程序. cron服务要通过命令启动和停止,在Linux上可以通过/etc/init.d/...

29050
来自专栏用户2442861的专栏

使用ThinkPHP框架快速开发网站(多图)

http://blog.csdn.net/ruby97/article/details/7574851/

2.8K20
来自专栏美丽应用

批量文件命名助手:全能批量命名工具

13320
来自专栏Python爬虫与算法进阶

【Python爬虫实战】——爬取今日头条美女图片

笔者是头条的深度使用者,经常用头条完成“看片”大业。若不信的话可以试试在头条搜索街拍,返回的都是一道道靓丽的风景线。 ? 想把图片存下来,该怎么办呢?我们可以用...

2.2K100
来自专栏前端那些事

async和enterproxy控制并发数量

聊聊并发与并行 并发我们经常提及之,不管是web server,app并发无处不在,操作系统中,指一个时间段中几个程序处于已经启动运行到完毕之间,且这几个程序都...

367100
来自专栏逆向与安全

Xposed截获 Android手机QQ密码

   Xposed框架是一款修改系统框架服务的软件,通过它许多功能强大的模块得以实现,且不冲突地同时运作,自从Xposed框架发布以来,安卓手机的可玩性日益激增...

17600
来自专栏Python中文社区

Python爬虫抓取收集考试大纲

專 欄 ❈ Garfield_Liang,Python中文社区专栏作者。 博客地址:http://www.jianshu.com/u/cac1d39abfa9 ...

260100
来自专栏java达人

Kafka漫游记

我是一条消息,从我被生产者发布到topic的时候,我就清楚自己的使命:被消费者获取消费。但我一直很纳闷,把我直接推送给消费者不就行了,为什么一定要先推送到类似队...

27370
来自专栏数据小魔方

R语言爬虫实战——知乎live课程数据爬取实战

本文是一篇R语言爬虫实战练习篇,同样使用httr包来完成,结合cookies登录、表单提交、json数据包来完成整个数据爬取过程,无需书写复杂的xpath、cs...

48660

扫码关注云+社区

领取腾讯云代金券