文章

Three roads lead to Rome

Linan Hao of Qihoo 360 Vulcan Team

Preface：

In the past two years, I did some work on browser security, mainly focus on Fuzzing, as to user mode vulnerability hunting, fuzzing is performing well in the quality of the bugs and the CVE production.

Until some big players involved, and a growing number of fuzzers were published online, vulnerability hunting requires a more rigorous approach. What’s more, the MemGC used by Microsoft Edge make it much more difficult to find a bug by the way of fuzzing than before. Only a little bugs which are exploitable that find by fuzzing always killed by other bug hunters, because as time goes on, our fuzzers become the same.

So, earlier this year, just after pwn2own 2016, I put more focus on manual audit, and it works:)

At first, I think the bugs is enough, enough for two years. Well, around July, the bugs were patched at a speed of 2+ per month. (MS、ChakraCodeTeam、ZDI、Natalie、360…).

The bug we mentioned in this paper is one of those dead bugs.Though it has been fixed, the skills used to exploit it are interesting, I think.And the way of exploit this bug experienced several versions.

Root Cause:

The issue is in this function:

There is a lot of places reference this logic. JavascriptArray::EntryReverse is just one of these trigger paths. Developers assume the type of Array is Var Array, they think the param(prototype) pass to ForEachOwnMissingArrayIndexOfObject must be Var Array,

Just as follows:
Of course, normally when an Array assign to proto, it will be converted to a Var Array as default,

But the appearance of Proxy in ES6 makes the logic more complex, many assumptions maybe not correct anymore.

The detail of Proxy is as follows:

It can monitor many types of events, in other words, it can interrupt some operation and doing our own work in the middle of that process, then return some data which is controlled by us.

There is such a handler:

The code prototype = prototype->GetPrototype(); will enter into the trap process, then entry our self-defined JavaScript callback. If you return an array of JavascriptNativeIntArray type, the default assumption will not stand, and result in a variety of problems

In fact, not only the type of JavascriptNativeIntArray, if it is not an array of JavascriptArray type, it will be a problem, because the difference between implementation and expectation

After this, we can make a fake object in Array_A[x] which could be pointed to any address.

2. Out of bounds Read

Set Array_A to JavascriptArray type

Set Array_B to JavascriptNativeIntArray type

The size of element in JavascriptNativeIntArray is 4 bytes, so read data through the type of Var will result in OOB.

Why not make an issue of Array_A?

Because the final assignment is done through SetItem, Even if Array_A is initialized to JavascriptNativeIntArray/JavascriptNativeFloatArray, Eventually, it will be converted to JavascriptArray type based on item’s type

The following part we will discuss the three ways to exploit this vulnerability :

0x1:

At first, I have no idea about how to use the ability of OOB, And I just have some information leak bugs at hand.

So my plan is: exploit = leak + fakeObj

The bug below can leak the address of any object, of course, also has been fixed

So it is necessary to use the limited space accurately in the process of confusing object.

For condition 2, we can base on 1:

Make a fake UInt64Number object, and trigger the function JavascriptConversion:: ToString by calling interface of parseInt to read the virtual table of the next object, then leaks chakra’s base address.

Finally, by making a self-defined Uint32Array to implement the full address read and write, it worth mentioned that controllable space of Array. Segment is limited, it cannot write down all the fields of Uint32Array and ArrayBuffer.

But in fact, a lot of fields will not be used when doing AAW/AAR, and you can also reuse some of these fields, it’s won’t be a big problem.

0x2:

In October, the last few bugs which can achieve information leak were killed by Natalie…

Then comes up with the following two plans, take full advantage of the OOB feature, we can use this single vulnerability to complete the exploit.

The Array Object in JavaScript is inherited from DynamicObject, which has a field auxSlots, as follows:

In IsDirectAccessArray, it’s easy to know if the data that aValue point to is a specific vtable, it doesn’t operate other fields, the result returned is TRUE or FALSE.

IsDirectAccessArray is referenced in function JavascriptArray::ConcatArgs, And the code flow will goes into different branches according to its return result, then we can indirectly detect the return state of IsDirectAccessArray in JS layer.

Because we already know the chakra’s address, only we need to do is to find a place with 29 in the module.

type_addr = chakra_base + offset_value_29

Finally, we can forge a custom Array, and then achieve AAR/AAW

0x3:

At present, the key objects in the Edge browser are all managed by MemGC, which is quite different from simple reference-count based pattern, MemGC will automatically scan the dependencies between objects,Fundamentally end up the UAF era…

But, is that perfect? Objects protect by MemGC won’t be UAFed anymore?

According to the mechanism of MemGC, there are several cases that can’t be protected by MemGC, and one of the cases is as follows:

This is an ordinary object maintained by MemGC, addr_A points to the start of the object, Addr_B points to some place inside the object.

Object2 is another object that is maintained by the GC, it has a field addr_A which point to Object1’s head

At this time, if we free Object1 in the JS layer, and trigger CollectGarbage, we will notice that it is not really being released.

However, if so

The Object1’s referenced field in Object2 is Object1.addr_B, pointing inside the Object1, then Object1 could be freed at this time.

And a dangling pointer appeared in Object2

After some kinds of fengshui, you can use the Object2 to access the freed content of Object1, result in UAF.

Exploit:

Conclusion:

This paper describes some exploit techniques used in chakra script engine vulnerabilities, and using three different ways to explain them, they are not independent, skills can be merged into a more compact and stable exploit. The bug we mentioned in this paper also been reported to Microsoft by Natalie, and was fixed on November patch day, just one day before pwnfest. The corresponding information is CVE-2016-7201. The bug used in pwnfest will be discussed after Microsoft fix it.