首页
社区
课程
招聘
[原创]CVE-2014-9707-GoaHead堆溢出漏洞形成分析
发表于: 2017-4-11 15:27 5286

[原创]CVE-2014-9707-GoaHead堆溢出漏洞形成分析

XFF 活跃值
2
2017-4-11 15:27
5286

前言

实验主机: Linux 4.0.0-kali1-686-pae #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) i686 GNU/Linux(关闭ASLR、NX)

工具: GNU gdb (Debian 7.7.1+dfs- 5) 7.7.1(带peda插件)

漏洞程序

版本: Version: 3.1.3-0

Embedthis Software GoAhead 3.0.0版本至3.4.1版本中存在安全漏洞,该漏洞源于程序没有正确处理以.字符开始的路径部分。远程攻击者可借助构造URI利用该漏洞实施目录遍历攻击,造成拒绝服务(基于堆的缓冲区溢出和崩溃),也可能执行任意代码。

 

漏洞分析

先把我们的漏洞程序GoAhead运行起来,然后打开另一个命令行终端,输入ps -ef查看当前GoAhead的PID,便于我们gdb附加

     可以看到GoAhead程序的PID8742,然后我们使用Gdb附加GoAhead程序gdb attech 8742 

  附加成功,程序断了下来,然后我们输入命令C 让程序继续运行.

运行我们的Poc程序,可见GoAheadService触发了一个SIGABRT,触发SIGABRT的原因:

1.Free没有初始化的地址或者错误的地址,

2.堆越界,

3.assert

 

bt 栈回溯,看调用流程,因为是堆溢出,所以栈上的数据是完整的

src/目录下的是GoAhead程序源代码中的函数,可以看见程序在WebNormalizeUriPath函数中调用系统函数Free时发生了异常,并且调用WebNormalizeUriPath函数时传递的参数中有我们的畸形字符串

函数调用链是这样的:

MAIN(goahead, int argc, char **argv, char **envp)---> websServiceEvents(&finished) ---> socketProcess() ---> socketDoEvent(WebsSocket *sp) ---> socketEvent(int sid, int mask, void *wptr) ---> readEvent(Webs *wp) ---> websPump(Webs *wp) ---> parseIncoming(Webs *wp) ---> parseFirstLine(Webs *wp) ---> websNormalizeUriPath(char *pathArg) ---> wfree(dupPath)[释放dupPath堆块的时候发生异常,说明dupPath指向的堆块被破坏]

因为这是开源软件所以我们可以对照着源代码追溯漏洞发生的原因.根据分析调用链发现#14处的函数调用并没有参数传递所以从这个函数往上的调用都可以直接排除,并且可以看出从readEvent函数开始连续传递wp参数,说明wp参数蛮可疑的,在源代码中看看Webs到底是什么类型.

Webs是一个结构体,存放着我们的畸形Get数据包。

我们看到WebNormalizeUriPath函数参数是我们的畸形字串,所以可以到它的上层函数parseFirstLine函数中看一下调用WebNormalizeUriPath函数时的具体流程.

在此函数中调用websNormalizeUriPath函数时给函数传递了一个变量path,此变量中存放的也就是我们的畸形字符串,那么变量path中的值又是如何来的?分析函数我们找到path是一个局部变量,被初始化为Null,然后调用websUrlParse函数把path的地址当做参数传递进去,调用完此函数后调用的就是websNormalizeUriPath函数,所以我们推测path中的畸形字符串应该是被websUrlParse函数写入进去的,为了准确我们可以动态调试一下

重启GoAhead,ps -ef查看PID,gdb attech PID附加程序,因为我们有源代码,所以在gdb中直接用websUrlParse函数名进行下断,若我们没有源代码的话可以用IDA反汇编,在函数地址处进行下断,这里直接用函数名下断点b websUrlParse

断点设置成功,输入C命令让程序继续运行,运行POC给GoAheadWebServer发送畸形Get数据包,gdb马上就断了下来

此时websUrlParse函数的参数如上图所示,path变量对应着ppath,因为此函数传入的是path变量的地址是一个指针,所以我们看到的是ppath,我们看一下此时path变量中的值是什么,

此时path中的值为0x00000000,然后反编译websUrlParse函数gdb-peda$ Disas websUrlParse,查找ret指令的地址,并且在函数末尾处下一个断点b *0xb7fb614e

 

然后执行C命令直接运行到函数末尾处


此时我们再看看path中的值,可以看到path确实是被websUrlParse函数设置为了指向我们的畸形字符串.

注意:这里为什么需要解引用?

:因为0xbffff104中存放的是path的值,而path本身就是一个指针,我们需要取这个指针中的内容所以需要解引用.


websNormalizeUriPath传入的参数是我们的畸形字符串,再来分析websNormalizeUriPath函数

此函数中分配堆空间的地方有3处,strcpy函数使用有2处

    第一处:逻辑比较简单,应该没什么问题

   第二处:这一块的逻辑比第一处的逻辑稍微复杂一点,问题可能会出在这里


先调试获取分配给dp的内存到底有多大(len + nseg + 1),在websNormalizeUriPath处下断点,C继续运行,断点断在websNormalizeUriPath函数处.


gdb-peda$ disas websNormalizeUriPath      //反汇编websNormalizeUriPath函数


根据查找几个关键函数可以定位到0xb7fb653c处对应的大概就是walloc(len + nseg + 1)),传入参数存放在eax处,在0xb7fb653c处下断,C命令继续执行,中断于0xb7fb653c


查看当前eax的值eax == 0x42,也就是说程序分配了0x42(66)个字节大小的空间,并让path指向其首地址


再调试运行到拷贝segments元素到dp内存的循环内,循环调试看最后到底拷贝了多少字节的数据到dp中,我们在这条指令len = (int) slen(segments[i])对应的汇编位置下一条处下断,看slen函数返回值

b *0xb7fb6586  


我们还应该在这个for循环结束的时候下一个断点,这样我们循环结束以后程序就不至于跑飞,我们在wfree(dupPath)对应的汇编位置下断

b *0xb7fb65a8


关键断点设置完毕以后我们就开始循环执行,观察每次循环执行slen函数的返回值

循环结束,程序命中了我们设置在释放函数处的断点,然后我们来计算一下拷贝的总长度,看是否大于dp指向的堆空间的大小(0x42(66))

拷贝总数:0x0 + 0x7 + 0x32 + 0x32 = 0x6B

因为0x6B > 0x42所以此次的拷贝存在堆溢出,我们单步运行程序,程序在释放dupPath指向的堆空间的时候出发了异常,

单步前dupPath中的值

异常触发原因应该就是之前的堆溢出覆盖了dupPath指向的堆空间,导致堆空间被破坏,Free堆的时候触发unlink操作,会把当前要释放的堆块进行脱链,脱链的过程取决于堆块的前向指针与后向指针,但是因为前向指针与后向指针被覆盖为了无效指针,所以发生异常.

找到了溢出现场,那是什么原因造成的拷贝数据过多呢?


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//