快捷搜索:  网络  渗透  后门  CVE  扫描  木马  黑客  as

记某次从控件漏洞挖掘到成功行使

之前对某ActiveX控件进行漏洞挖掘的时辰发现了一个栈溢出漏洞,近来一时髦起写下了这篇针对该漏洞的从挖掘到成功行使的分享文章。如题,本文将从漏洞挖掘以及漏洞行使两个方面进行分享。话不久不多说,直接进入主题。

漏洞挖掘部分

拿到这个控件以后,先对该控件的属性以及包含的函数等信息进行大致了解,这里推荐一个工具——COMRaider,它可以识别出控件很多信息。这里就不先容使用要领了,下面给出一个识别该控件的截图。

记某次从控件漏洞挖掘到成功行使

该工具还提供fuzz功能,在后续进行逆向阐发的同时可以跑下fuzz,说不定有惊喜(本次fuzz并没有惊喜出现)。

了解过控件以后,就可以进行静态阐发了,过程中使用了IDA以及com.plw插件。

使用IDA加载控件文件,按Ctrl+F11调用插件识别控件中的函数:

记某次从控件漏洞挖掘到成功行使

这些识别出来的函数都是可以在阅读器中直接调用的,所以这些函数的参数都是攻击者可以控制的,需要对这些参数逐个进行追踪以及阐发。当阐发到OpenDevice这个函数时发现了问题。下列将分享该函数具体阐发过程。

使用F5查看该函数的伪C代码:

 记某次从控件漏洞挖掘到成功行使

记某次从控件漏洞挖掘到成功行使

记某次从控件漏洞挖掘到成功行使

追踪参数lFlags的处理过程,在第41行发现该参数进行了与运算后并将效果给到v6:

记某次从控件漏洞挖掘到成功行使

继续追踪v6,发现v6都用在if语句的判定中,可以认为lFlags参数是用作分支判定的。其中有一个分支有些希罕,在第42行:

记某次从控件漏洞挖掘到成功行使

因为v6是由lFlags以及0xF00进行与操作得到的,所以v6不可能取值到3。不知道这是程序的bug照样啥。所以以后可以跳过对该分支的阐发。

追踪参数szAppID的处理过程,对该参数的处理在v6=3的分支中有一部分,但可以不进行阐发,因为根本执行不到。剩下的只有以下图所示的部分:

记某次从控件漏洞挖掘到成功行使

该部分代码在v6=256或768是可以执行到,在第95行发现调用了WideCharToMultiByte函数,将szAppID参数地址中的宽字节字符串转换成多字节字符串后并放入&v21地址中,函数至多可以向&v21地址空间写入v14个字符,而v14是szAppID地址下字符串长度的两倍加二。这里需要判定&v21地址空间实际大小。查看伪C代码最上部分的参数说明:

记某次从控件漏洞挖掘到成功行使

发现v21这个变量后面的地址是sp+0h,即地址为栈顶。而变量String1对应的地址为sp+Ch,并且从伪C代码中可以看出String1并不是v21的一部分,是可以自力使用的变量,要是这么阐发&v21对应的地址空间大小只有Ch,是可以溢出的,这时辰就要通过汇编代码来阐发确认栈顶地址对应的空间大小是否是真的像之前阐发的那样:

记某次从控件漏洞挖掘到成功行使

图中框1将实际接收地址(存在寄存器edi中)压入栈中,由框2可知edi的数据来自esp,在这之前调用了框3中的函数,跟进函数阐发esp的处理:

记某次从控件漏洞挖掘到成功行使

阐发该函数的作用是根据eax中的数据在栈顶之外再开辟对应大小的空间,并将终极的栈顶地址返归到esp,再归到原函数,阐发eax中数据的大小:

 记某次从控件漏洞挖掘到成功行使

从上图框中的汇编代码可知,eax中的数据是大于等于lpString长度乘二加二,查看lpString的偏移量:

记某次从控件漏洞挖掘到成功行使

可知lpString就是szAppID,所以使用WideCharToMultiByte函数不会造成溢出。

然后继续追踪转为多字节字符串以后的地址空间&v21,在第93行发现v12=&v21:

记某次从控件漏洞挖掘到成功行使

继续追踪v12,发现在第102行将v12使用到了字符串拷贝函数中:

记某次从控件漏洞挖掘到成功行使

该函数会将v12地址空间中的字符串整个拷贝到&String1中,通过上述阐发发现v12是攻击者可控的,而且没有进行长度制约,这里需要判定&String1地址空间的大小。直接查看该处对应的汇编代码:

记某次从控件漏洞挖掘到成功行使

发现拷贝的目的地址空间为ebp+String1,查看对应的栈空间大小:

记某次从控件漏洞挖掘到成功行使

由此可知ebp+String1对应的地址空间大小为74h,是有限的,所以颇有可能存在栈溢出漏洞。为了验证以上的阐发,编写一个调用该控件函数的页面,触发可能存在漏洞的代码部分需要餍足对应分支的前提,以上阐发可知前提为lFlags&0xF00为768或256,为了溢出笼盖到返归地址,所以szAppID大于等于78h。

编写一个测试页面,内容以下:

记某次从控件漏洞挖掘到成功行使

使用ollydbg附加到要加载该页面的ie阅读器上,在可执行模块中没有找到该控件的文件,为了向存在漏洞的部分下断点,我在ollydbg中配置了停息于新模块:

记某次从控件漏洞挖掘到成功行使

配置实现后,让ie阅读器加载页面,当停息的时辰在可执行模块中查找控件对应的文件:

记某次从控件漏洞挖掘到成功行使

在ida中获取对应代码段的相对于地址,根据相对于地址在ollydbg相对于地位配置断点,此处我在调用拷贝函数前以及函数RETN命令前分别配置了断点:

记某次从控件漏洞挖掘到成功行使

继续执行,直到停息于调用字符串拷贝函数前,单步执行到调用完字符串拷贝函数后,查看EBP下的内容:

记某次从控件漏洞挖掘到成功行使

记某次从控件漏洞挖掘到成功行使

发现以及我之前阐发的同样,偏偏溢出到函数返归地址处。让程序继续执行,发现阅读器已经崩溃:

记某次从控件漏洞挖掘到成功行使

至此可以确定该处确凿存在栈溢出。

漏洞行使部分

该部分将分享怎么样行使上述发现的栈溢出漏洞弹出计算器。因为Win7下开启了ASLR以及DEP两个维护,而ActiveX控件又不具有交互性,所以在Win7下行使不现实,这里以WinXPENSP3为靶机进行漏洞行使。

该栈溢出漏洞行使的大体思路是笼盖函数返归地址作为跳板,然后在目标地址处填入shellcode进行执行。思路很简单,但中间照样有个坑比较尴尬。

起首我先找一个跳板,黑客工具,使用ollydbg附加到要加载网页的ie阅读器上,然后在可执行模块中找一个存在jmp esp指令的体系dll文件,这里凑巧找到的是advapi32.dll:

记某次从控件漏洞挖掘到成功行使

可见jmp esp的地址为\x77DEF049,由漏洞挖掘部分的阐发可知,字符串的第121~124个字符会溢出笼盖掉返归地址,所以这里组织一个payload,前120个字符统一用A加添,以后为“\x49\xF0\xDE\x77”,最后再加添字符B至长度为150,代码以下图:

记某次从控件漏洞挖掘到成功行使

以及漏洞挖掘时同样,分别在函数调用字符串拷贝函数之前以及函数返归之前添加断点:

记某次从控件漏洞挖掘到成功行使

使用ie阅读器加载页面,单步执行到字符串拷贝函数以后,查看栈底中的数据:

记某次从控件漏洞挖掘到成功行使

记某次从控件漏洞挖掘到成功行使

发现栈中的数据以及预期同样,继续执行到函数返归前,然后单步执行到retn以后,发现成功执行到advapi32中的jmp esp处:

记某次从控件漏洞挖掘到成功行使

再次单步执行,查看EIP所指向的地址在溢出字符串中的地位:

记某次从控件漏洞挖掘到成功行使

可以看出,在返归地址以后的第13个字符最先为jmp esp跳转后执行的代码段。所以编写payload字符串为120个字符A,以后为“\x49\xF0\xDE\x77”,然后为12个字符B,最后为4个字符C:

记某次从控件漏洞挖掘到成功行使

使用ie阅读器加载页面,在ollydbg中查看jmp esp后的指令是否为4个C:

记某次从控件漏洞挖掘到成功行使

发现以及预期的效果同样,如许就可以将4个字符C的地位改成shellcode即可。

这里使用一个在exploit-db中找的弹计算器的shellcode进行尝试,页面代码以下:

记某次从控件漏洞挖掘到成功行使

加载页面,让程序停息在调用字符串拷贝函数以后,比较内存中的内容以及shellcode是否一致:

记某次从控件漏洞挖掘到成功行使记某次从控件漏洞挖掘到成功行使

对比发现,上图红框中的0x3F在shellcode中对应的地位应该是0×86,嗯?这是什么情形?归到IDA,发现在拷贝字符串之前调用过WideCharToMultiByte函数,将宽字节字符串转换成多字节字符串,是否是在经过这个函数的时辰改变了0×86的值呢?

经过之前漏洞挖掘时的阐发,WideCharToMultiByte函数将转换后的字符串存在了栈顶,在ollydbg查看下栈顶转换后的字符串内容:

记某次从控件漏洞挖掘到成功行使

缘故起因如之前推测的那样。

缘故起因找到以后,就要想怎么样解决,我第一设法主张是通过MultiByteToWideChar函数将shellcode从多字节字符串转换成宽字节字符串以后放到payload中,让程序运行WideCharToMultiByte解码就能变归原来的多字节字符串了,直接上代码:

记某次从控件漏洞挖掘到成功行使

代码中为了方便对比,将转为宽字节的字符串以及转归多字节的字符串都写入文件中,效果以下图:

记某次从控件漏洞挖掘到成功行使

很显然地可以发现转归后的多字节的字符串与转码前的不同样。。。

检查完代码发现没有什么问题以后,只能猜测两次转换是没法完全得到起初的字符串的。只能抛却这类思路了。

经过量次尝试后,发现\x80之前的字符宽字节字符串以及多字节字符串相互转换是不会改变的。这就想到是否是使用只由\x80之前的字符组成的shellcode就可以免这个问题了。

为了生成如许的shellcode,我使用了kali内里msfvenom命令来生成shellcode:

记某次从控件漏洞挖掘到成功行使

将生成的shellcode添加到页面中:

记某次从控件漏洞挖掘到成功行使

因为shellcode有点长,就不进行在内存中一一对比了,直接在ie阅读器中加载页面,看是否会弹出计算器:

记某次从控件漏洞挖掘到成功行使

 记某次从控件漏洞挖掘到成功行使

成功弹出计算器。

到此该栈溢出漏洞的行使就结束了。

后记

针对上面出现的漏洞,在开发中我们要怎么样避免出现这类漏洞呢?

答:使用安全的字符串拷贝要领

一、使用char * strncpy ( char * destination, const char * source, size_t num );

将destination地址空间的大小减一(为了存放’\0’)的值传给第三个参数num,如许即使source中的字符串长度超过了destination的容量,也只会拷贝num大小的字符串。

例:

char buf[MAX];
strncpy(buf, src, MAX-1);

注:为了不source的长度大于等于num时导致destination最后一个字符不是’\0’的情形,需要在拷贝之前对destination地址空间的内容初始话为’\0’,或者在拷贝以后给destination最后一个字符赋值为’\0’。

2、使用int snprintf( char *buffer, int buff_size, const char *format, … );

使用第二个参数buff_size来标识buffer的容量,如许打印到buffer内里的字符串不会超过buff_size的制约,并且会在字符串的最后或buffer的最后一位赋值为’\0’作为字符串的结束。

例:

char buf[MAX];
 snprintf(buf, sizeof(buf), "%s", src);

*

您可能还会对下面的文章感兴趣: