_ _ (_) | | __ ____ __ _ _ _ _ __ ___ _ __ _ __ ___ | |_ \ \ / /\ \/ /| || | | || '_ ` _ \ | '_ \ | '_ \ / _ \| __| \ V / > < | || |_| || | | | | || |_) |_ | | | || __/| |_ \_/ /_/\_\| | \__,_||_| |_| |_|| .__/(_)|_| |_| \___| \__| _/ | | | |__/ |_| /---------------------------------------------------------------------------------------\ |>...................[ 漏洞CVE-2012-1889逆向分析 ]...................<| |>......................[ by nEINEI/vxjump.net ]......................<| |>......................[ 2012-07-20 ]......................<| \>...................... [ neineit_at_gmail.com ] ...................... Ms XML core service vulnerability CVE-2012-1889. 程序会崩溃在msxml3.dll模块当中, (12c.604): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=72840ab6 ebx=00000000 ecx=fc774ae9 edx=00000001 esi=72840ab6 edi=0832d460 eip=7284e2ee esp=0832d0e4 ebp=0832d220 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 msxml3!_dispatchImpl::InvokeHelper+0xb3: 7284e2ee ff5118 call dword ptr [ecx+18h] ds:0023:fc774b01=???????? ecx是一个高端地址,显然在32位下这个内核地址空间,用户态不可能出现这样的值,所以我们需要追查一下ecx是由 什么数据影响的。 先简单看一下堆栈情况: 包含msxml3模块几个函数,但看起来似乎供漏洞追踪的参考意义不大。 0:015> kp ChildEBP RetAddr 0876d238 7283120c msxml3!_dispatchImpl::InvokeHelper+0xb3 0876d274 72831d16 msxml3!_dispatchImpl::Invoke+0x9b 0876d2b4 72831caf msxml3!DOMNode::Invoke+0xa4 0876d2e8 728321db msxml3!DOMDocumentWrapper::Invoke+0x5d 0876d344 7283215d msxml3!_dispatchImpl::InvokeEx+0x10f 0876d374 7034a26e msxml3!_dispatchEx::InvokeEx+0x2d 0876d3b0 7034a1b9 jscript!IDispatchExInvokeEx2+0x104 0876d3ec 7034a43a jscript!IDispatchExInvokeEx+0x6a 0876d4ac 7034a4e4 jscript!InvokeDispatchEx+0x98 0876d4e0 7035d9a8 jscript!VAR::InvokeByName+0x139 0876d52c 7035da4f jscript!VAR::InvokeDispName+0x7d 0876d558 7035e4c7 jscript!VAR::InvokeByDispID+0xce 0876d6f4 70355d7d jscript!CScriptRuntime::Run+0x2b80 0876d7dc 70355cdb jscript!ScrFncObj::CallWithFrameOnStack+0xce 0876d824 70355ef1 jscript!ScrFncObj::Call+0x8d 0876d8a0 7035620a jscript!CSession::Execute+0x15f ... 从崩溃的代码向上查看,可以看到 msxml3!_dispatchImpl::InvokeHelper: ... .text:7284E2D9 mov ecx, [eax] // 是由这里的eax污染的 .text:7284E2DB push [ebp+arg_1C] .text:7284E2DE push [ebp+arg_18] .text:7284E2E1 push edi .text:7284E2E2 push 3 .text:7284E2E4 push [ebp+arg_C] .text:7284E2E7 push offset _GUID_NULL .text:7284E2EC push ebx .text:7284E2ED push eax .text:7284E2EE call dword ptr [ecx+18h] // 这里崩溃 继续查看eax的受控制值, msxml3!_dispatchImpl::InvokeHelper: ... .text:7284E2AA lea eax, [ebp+var_1C] // (1)eax赋值 .text:7284E2AD push eax ; pvarg .text:7284E2AE call ds:__imp__VariantInit@4 ; VariantInit(x) .text:7284E2B4 push ebx .text:7284E2B5 lea eax, [ebp+var_1C] // (1)eax赋值 .text:7284E2B8 push eax .text:7284E2B9 push 2 .text:7284E2BB push ebx .text:7284E2BC push [ebp+arg_8] .text:7284E2BF push [ebp+arg_0] .text:7284E2C2 call dword ptr [esi+20h] // (2)这里调用了msxml3!DOMNode::get_definition .text:7284E2C5 cmp eax, ebx .text:7284E2C7 jl loc_72831C30 .text:7284E2CD mov eax, dword ptr [ebp+var_1C.anonymous_0+8] //(3)这里是最后赋值给eax的值 .text:7284E2D0 mov esi, eax .text:7284E2D2 cmp eax, ebx .text:7284E2D4 jz short loc_7284E2FF .text:7284E2D6 push [ebp+arg_20] .text:7284E2D9 mov ecx, [eax] // 是由这里的eax污染的 目前,我们已经可以看出漏洞可能的原因 ebp+var_1c 是一个临时变量,而这个变量并没有被完全的初始化导致,后续直接对该变量指向内存 中的内存进行了引用,最终导致crash. (1)(2)(3)上面我已经标识出和漏洞触发有关的关键点,显然(3)是最后污染的,那么(2)中是由对eax赋值数据的操作的,我们确认是(2)中没有对他进行 调用后的初始化工作。 漏洞引发语句是 document.getElementById("exp").object.definition(1234567); 查看相关的定义 0:000> x msxml3!*definition* 72890b93 msxml3!SymbolManager::endDefinition = 72878881 msxml3!W3CDOMWrapper::get_definition = 72871861 msxml3!DOMNode::get_definition = 72877217 msxml3!DOMDocumentWrapper::get_definition = 728777f4 msxml3!W3CDOMWrapper::get_definition = 72890ad2 msxml3!Symbol::beginDefinition = 72891535 msxml3!SymbolManager::beginDefinition = 72890d3c msxml3!VariableSymbol::endDefinition = 7287747a msxml3!DOMDocumentWrapper::get_definition = 728785ed msxml3!W3CDOMWrapper::get_definition = 728b96c0 msxml3!CXTLRuntimeObject::get_definition = 72891261 msxml3!AttrSetSymbol::endDefinition = 728708db msxml3!Node::getDefinition = 728912f5 msxml3!Symbol::endDefinition = 72878dc7 msxml3!W3CDOMWrapper::get_definition = 通过crash位置及对着信息判断,触发问题的地方是, msxml3!DOMNode::get_definition = 下断点: bu msxml3!DOMNode::get_definition 单步跟踪知道漏洞崩溃,在这里一路我们发现,eax是由下面这条语句赋值的 eax=00000001 ebx=00000000 ecx=72871922 edx=00000001 esi=728c8b60 edi=0876d460 eip=7284e2cd esp=0876d108 ebp=0876d220 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 msxml3!_dispatchImpl::InvokeHelper+0x92: 7284e2cd 8b45ec mov eax,dword ptr [ebp-14h] ss:0023:0876d20c=72840ab6 eax指向的值是:fc774ae9 ,就是崩溃时的ecx内容。 0:014> dd 72840ab6 72840ab6 fc774ae9 0c75ffff 9090f0eb 8b909090 72840ac6 ec8b55ff 8b1075ff 75ff0845 1cc0830c 72840ad6 a045e850 c25dfffd 458b000c 0000c60c 72840ae6 fd4e1ae9 909090ff ff8b9090 56ec8b55 显然,ebp=0876d220, ebp-14 = 0876d20c . 下面这条语句使用的ebp-1c。 .text:7284E2AA lea eax, [ebp+var_1C] // (1)eax赋值 至此,我们已经知道,ebp-0x1c 就是一个临时变量,显示调用VariantInit进行了简单初始化,然后调用(2)msxml3!DOMNode::get_definition 也应该初始化调用这片内存,但实际上没有这样做。导致后续产生的问题。 仔细排查msxml3!DOMNode::get_definition这个函数,可以看到这样本赋值语句,但只是初始化了ebp-1c,而不是ebp-14. msxml3!DOMNode::get_definition+0xb9: 7287191a 8b45e4 mov eax,dword ptr [ebp-1Ch] ss:0023:0876d0a4=00000001 F5后, signed int __stdcall DOMNode::get_definition(DOMNode *this, struct IXMLDOMNode **a2) { struct TLSDATA *v2; // eax@1 struct TLSDATA *v3; // ebx@1 signed int result; // eax@2 Node *v5; // eax@5 char v6; // [sp+10h] [bp-34h]@3 struct TLSDATA *v7; // [sp+24h] [bp-20h]@1 int v8; // [sp+28h] [bp-1Ch]@3 CPPEH_RECORD ms_exc; // [sp+2Ch] [bp-18h]@5 v2 = g_pfnEntry(); v3 = v2; v7 = v2; if ( v2 ) { OMReadLock::OMReadLock((DocumentLock *)&v6, v2, (int)this); v8 = 0; if ( a2 ) { ms_exc.registration.TryLevel = 0; v5 = Node::getDefinition(*((Node **)this + 7)); if ( v5 ) *a2 = (struct IXMLDOMNode *)Node::getDOMNodeWrapper(v5); else v8 = 1; //只清空了这里,int v8; // [sp+28h] [bp-1Ch]@3 ,应该清空更大的内存空间 ms_exc.registration.TryLevel = -2; OMReadLock::~OMReadLock((OMReadLock *)&v6); g_pfnExit(v7); result = v8; } else { OMReadLock::~OMReadLock((OMReadLock *)&v6); g_pfnExit(v3); result = 0x80070057; } } else { g_pfnExit(0); result = -2147467259; } return result; } 所以,漏洞出问题的地方应该是DOMNode::get_definition函数,在进入出错分支后,应该清空栈中参数。而在msxml3!_dispatchImpl::InvokeHelper 当中也应该对其传入产生进行判断。 对比其补丁验证我们的判断: eax=00000000 ebx=00000000 ecx=058147d8 edx=00000000 esi=0581479c edi=01f8d114 eip=6f9a1903 esp=01f8cf84 ebp=01f8cfc8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 msxml3!DOMNode::get_definition+0x72: 6f9a1903 891f mov dword ptr [edi],ebx ds:0023:01f8d114=6f970ab6 0:005> dd 6f970ab6 0:005> dd 01f8d114 (清空为0了) 01f8d114 00000000 01f995d8 00000006 00000000 01f8d124 01f995d8 01f8d164 6f96120c 0581479c 01f8d134 00000000 00000017 00000001 00000001 01f8d144 01f8d368 00000000 01f8d378 01f8d244 相当于: signed int __stdcall DOMNode::get_definition(DOMNode *this, struct IXMLDOMNode **a2) { ... OMReadLock::OMReadLock((DocumentLock *)&v6, v2, (int)this); if ( a2 ) { ms_exc.registration.TryLevel = 0; v5 = Node::getDefinition(*((Node **)this + 7)); if ( v5 ) { *a2 = (struct IXMLDOMNode *)Node::getDOMNodeWrapper(v5); } else { *a2 = 0; //清空**********************************00000000000000************* v3 = 1; v7 = 1; //临时变量都初始化数据值了 } ... } [0x04] .其它 CVE-2012-1889作为一个栈溢出漏洞在当前的日子里面已经算是不多的情况了,通过脚本层面上对栈中数据进行布局使得未初始化数据刚好能够控制 ecx调用完成最后的漏洞那个利用。