A Matching Pair of Use-After-Free Bugs in Chakra asm.jsDecember 22, 2017 | Simon Zuckerbraun
To make use of the module, script calls one or more functions that the module explicitly “exports” using a special syntax.
Since only fundamental numerical types are permitted within the module, if script running outside the module passes a non-numeric argument value, Chakra must first coerce the value into a numeric type before forwarding it to the callee. WanderingGlitch noticed Chakra performs steps in this order when calling a function exported from a module:
1. Ensure that the ArrayBuffer serving as the module’s heap has not been freed.
2. Coerce any non-numeric argument values.
3. Call the exported function.
The PoC looks like this:
The last line of the PoC calls
writeU32, which is a function exported from
asmModule. The first argument passed is
valueOf function. The
valueOf function is invoked immediately before entering
writeU32, and consequently
writeU32 operates upon an ArrayBuffer instance that has already been neutered (its underlying buffer has been freed).
To be precise, this is not a use-after-free condition in the typical sense. By the time the module begins accessing the invalid ArrayBuffer, the memory pointer held by the ArrayBuffer has already been set to
NULL. The module can access memory at any 32-bit offset added to this
NULL base pointer. In other words, script gains unrestricted access to the lowermost 4GB of the address space.
A Converse Bug
Realizing this, I constructed the following PoC:
fun1 neuters the ArrayBuffer before it returns control to the asm.js module. When
writeU32 continues execution, it operates upon freed memory.
In this case we have a use-after-free condition in the usual sense. An attacker could add additional script at the end of
fun1 that reuses the memory of the freed ArrayBuffer for some new, unrelated allocation. Subsequently,
writeU32 could be used to corrupt this new data structure, leading to compromise of the process.
We disclosed both these vulnerabilities to Microsoft in early September of 2017. Upon release of the following month’s patches, they informed us that the October patch contained a fix for both issues, developed in response to a bug report separate from ours. According to the bulletin acknowledgments, the Microsoft Chakra Team also found these bugs and share credit in reporting them. While ZDI lists WanderingGlitch’s report as ZDI-17-928 and my report as ZDI-17-848, both vulnerabilities were assigned the same CVE – CVE-2017-11812. It appears that this separate report was an internal Microsoft finding.
ChakraCore, Microsoft’s open-source version of Chakra, has also received the patch. Examining the code, I was a bit excited to find that these bug reports had prompted the addition of an entirely new opcode in Chakra’s asm.js implementation. Its name: