探索黑客技术攻防,实战研究与安全创新

导航菜单

漏洞解析之ExploitMe

 说到漏洞,大家第一反应可能是CVE或者KB后带着一大串高深莫测的数字,看了就不敢碰了。本文为大家带来的是一个为考试而生的ExploitMe的解析,难度不难,很适合用来开题,消除Exploit的神秘感。

有人气吞山河地断定,凡是软件都有漏洞,这是一个哲学层面的问题,这里我们只关心什么样的漏洞可以溢出。漏洞可以粗分为local和remote两种类型,后者在入侵中更为常见,但要求也更苛刻,必须是允许输入才可能被溢出的。有朋友说,直接patch或者工具修改不行吗?行,不过仅限于local,为什么呢?我们当然可以随意修改本机上的软件,但要利用漏洞入侵服务器,最开始只能通过I/O进行交互,无法从进程层面或者二进制码层面进行访问。不支持I/O的软件,就算存在漏洞,我们也无法直接利用。

选择正确的切入点很重要。一个可能被利用的remote型软件,要么开有端口,要么支持文件读取,本文的ExploitMe就属于后者,它要读取同目录下名为exploit.dat的文件。

怎样知道它读取什么文件呢?一是看软件说明,软件毕竟是供人使用的,或多或少都会有介绍,看看说明中是否提及软件会读取什么文件。在入侵某个系统时,熟知这个系统的输入环境往往会让工作如鱼得水。要是某位奇葩管理员装的全是剑走偏锋的玩意怎办呢?那就只好花点时间找到相同的软件,照样配置好后逆向研究。以本ExploitMe为例,OD载入,下断CreateFileA,看一眼堆栈,入图1所示。

008.png

这时堆栈里保存着完整的输入参数,看到FileName,嗯,懂了,建立一个名字为exploit.dat的文件。有了输入,接着是找溢出点。溢出点通常要同时满足两个条件,一是程序需要使用栈里的某一段数据作为执行地址,二是输入的数据能够覆盖到那一段栈地址。首先找找有没有将栈数据作为地址执行的,找到了这里,如图2所示。

009.png

这里有两个call,分别读取eax和edx作为地址,而且这两个寄存器的值都与栈有关!

一个是esp+0x20,一个是esp+0xA4,先记好这两个值。

现在要验证这两处栈地址是否能被输入覆盖,我们看一下程序将文件读到哪里去了。重新载入,下断ReadFile,来到图3所示的位置。

010.png

看来不太妙。读入Buffer的地址是esp+0x130,后面通过一个rep指令复制到ebp,离我们想要的地址都挺远。能不能简单粗暴的增大文件直到覆盖特定栈地址呢?如果程序对读入文件大小没有限制是可以的,但这里我们发现了第二个不太妙的地方,如图4所示。

011.png

程序会比较文件大小,若超过0x84就GameOver。现在有两个选择,一是继续寻找有没有溢出点,一是用全0填一个0x84大小的exploit.dat文件。第二种有点碰运气,不过取巧才是黑客的浪漫,所以这里选第二种。

运行,程序崩溃了!对于程序员,程序崩溃是一件遗憾的事,但对于黑客,崩溃如同听见胜利女神的召唤。找到崩溃日志,如图5所示。

012.png

内存地址0x0040116A处发生读取异常,数值就是我们填入的0(如果不确定,可以多次变化文件的填写内容作为验证)!0x0040116A正好是上文提及的两个Call之一。回溯一下这个Call的值是怎么来的。首先是esp+0xA4,如图6所示。

013.png

那么,esp+0xA4是怎么来的呢?上下文中没有esp+0xA4的专门赋值,但是算一算,文件最大可达0x84,在这个范围以内的栈地址都可覆盖,也就是只要在esp+0x24到esp+0xA4有可用的赋值即可。往上找到一处,如图7所示。

014.png

这一段栈地址从esp+0x24开始,赋值的是ebp指向的值,而且长度覆盖了esp+0xA4。上文说过,buffer的数据通过一个rep指令复制到ebp里去,这样就对上了。文件内容通过ReadFile读入buffer,再被复制到ebp指向的内存地址,最后通过rep赋值给esp+0x24开始,共计0x84大小的栈地址。也就是说,文件的最后一个DWORD,恰好可以控制0x0040116A处Call的调用地址,只要将栈首地址esp+0x24填入,就可以获得0x80大小的操作空间。

可以控制地址是好事,可是由于软件缺乏jmpesp等跳板指令,不得不把栈首地址硬编码,降低了exploit的适用性,如果操作系统加入ASLR(内存地址随机布局)机制,理论上能很好地对抗硬编码的exploit。

知道了怎么利用这个漏洞,那该怎么防呢?

这次漏洞之所以能成功溢出,归功于多次的内存复制,而这些多次内存复制没有功能上的需要,很可能是出题人为了方便解题故意“放水”,但在一个繁大复杂的系统中,出现这种低级错误并不奇怪。在调试过程中,我们发现这个程序至少做了两个防溢出的措施,最明显的就是文件不得大于0x84,可惜少算了一个DWORD,甚至可能只是少写了一个等号,只要修改为不得大于等于0x84即可堵上这个漏洞。

另一个措施做得比较隐蔽,如图8所示。程序会将使用过的buffer清0,这对于保存栈数据绝对是坏消息,如果栈数据在溢出前被清0,那么就算覆盖了返回地址,也只会导致程序崩溃,而无法为我所用。幸亏这个程序的清0措施有两大问题,一是最后一段,即直接造成溢出的那一段没有清0;另一个是清0的范围有误,只设置了0x80大小,比合法的文件输入上限0x84,刚好小一个DWORD,黑客还是可以从远端控制CALL地址,虽然利用起来更为困难,但仍无法彻底消除溢出隐患。

015.png

这样,一个ExploitMe即被我们成功解析了。学习溢出,对于漏洞既要知其然,也要之前所以然,才能不断地做到知识积累。

(完)

本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法行为。