Exploits & Vulnerabilities
CVE-2020-17053: Use-After-Free IE Vulnerability
We analyze how CVE-2020-17053 was found and how it works.
In my previous blog titled CVE-2020-1380: Analysis of Recently Fixed IE Zero-Day, I discussed how that vulnerability was caused by a type inference error in the browser’s JIT engine, which can be exploited by neutering ArrayBuffer and resulted in a use-after-free (UAF) vulnerability. While analyzing the root cause of this vulnerability, I found another path to trigger a similar UAF vulnerability by neutering ArrayBuffer — but this time, without the need for the JIT engine. This bug was submitted to Microsoft in September via the Zero-Day Initiative and fixed in November’s Patch Tuesday as CVE-2020-17053. In this blog, I will attempt to explain this new vulnerability.
Set Item to TypedArray
Recall that in the proof-of-concept code of CVE-2020-1380, the code snippet “arr[0] = value1,” which is generated by JITed code will trigger a UAF bug by invoking an overridden valueOf callback function:
The JIT engine executes the operation of setting an item to TypedArray. What about setting an item to TypedArray without the JIT in jscript9.dll?
If setting an item to TypedArray is not executed in JIT, jscript9.dll uses the following code snippet in Interpreter:
This function has four major steps:
- Call function Js::JavascriptConversion::ToNumber() for the first time
- Check if TypedArray's ArrayBuffer is detached
- Compare the index of setting element with TypedArray's length
- Call function Js::JavascriptConversion::ToNumber() for the second time and set the value to ArrayBuffer
What makes me curious is why it calls the function Js::JavascriptConversion::ToNumber() twice?
Maybe the answer is the first call of Js::JavascriptConversion::ToNumber(), which is a trial call that tries to invoke a potential callback function in user code. An attacker can exploit this callback opportunity to free the TypedArray's ArrayBuffer and get a UAF vulnerability. After this first trial conversion, it checks if the TypedArray's ArrayBuffer is detached, which is a necessary check and prevents memory of ArrayBuffer from being freed in the user code callback.
After the check, it calls the conversion function again, which will save the conversion result to ArrayBuffer. Figure 3 shows the aforementioned code execution flow:
Now we can see what the protentional problem is. The code only checks the first conversion call, but what about the second conversion call? Is it possible for the user-defined callback function to have a different code execution flow in each callback?
Neutering ArrayBuffer Without JIT
The proof of concept of CVE-2020-17053 is shown in Figure 5:
The following steps can trigger this bug:
- Function pwn is used to set the item to a specified index element of TypedArray
- Object obj has an overridden valueOf callback function, which is used to free ArrayBuffer by Worker, like CVE-2020-1380
- Call function pwn and set the second parameter as obj; when code 'arr[index] = value'[EC(1] is executed, native function Js::JavascriptConversion::ToNumber() is called and the valueOf callback function will be invoked twice
- In the first valueOf callback, the operation of neutering ArrayBuffer will not be executed because the variable “flag” is 0. At the end of the first valueOf callback, the flag is set to 1
- In the second valueOf callback, the operation of neutering ArrayBuffer will be executed because the variable “flag” is equal to 1, resulting in a UAF vulnerability because there is no detached check of ArrayBuffer after the second conversion call in function Js::JavascriptConversion::ToNumber().
At last, we get a freed memory of ArrayBuffer, which leads to a UAF vulnerability. All kinds of TypedArray can trigger this bug, but they all have been fixed in the November patch.
Exploitation Analysis
Because of the controllable memory size of the freed ArrayBuffer, this UAF vulnerability is easy to exploit using the following steps:
- Use LargeHeapBlock to occupy the freed memory (allocated in CRT heap) by creating many JavaScript Array objects
- Set LargeHeapBlock's allocCount to 0 using the valueOf callback function return value
- Call function CollectGarbage() manually to free the LargeHeapBlock object, which allocCount has set to 0; this will result in a second chance to get UAF in the IE custom heap
- Create many JavaScript Array objects again; this time, it will exploit the second chance of UAF in the IE custom heap and make two JavaScript Arrays point to the same buffer.
- Use the two JavaScript Arrays to achieve read/write primitives and finally, code execution.
Conclusion
By analyzing the exploits of CVE-2020-1380 in the wild, we can speculate that attackers are choosing to target jscript9.dll instead of vbscript.dll or jscript.dll, as they did in the past. In this blog, I explained how I found another similar UAF vulnerability that does not use the JIT engine through simple reverse engineering. The security issues surrounding jscript9.dll may not be over.
Trend Micro™ Deep Security™ and Vulnerability Protection can protect systems from attackers that attempt to take advantage of these vulnerabilities, defending customers via the following rule:
- 1010602 - Microsoft Internet Explorer Memory Corruption Vulnerability (CVE-2020-17053)
Trend Micro™ TippingPoint™ protects users from exploits that target this via the following rule:
- 38412: HTTP: Microsoft Internet Explorer Worker Use-After-Free Vulnerability (CVE-2020-17053)