Maddie Stone & Ivan Fratric, Project Zero & Clément Lecigne, Google's Threat Analysis Group (Originally posted on Project Zero blog 2020-07-27)

The Basics

Disclosure or Patch Date: 23 September 2019

Product: Microsoft Internet Explorer


Affected Versions: For Windows 10 1903, KB4515384 and previous

First Patched Version: For Windows 10 1903, KB4524147

Issue/Bug Report: N/A

Patch CL: N/A

Bug-Introducing CL: N/A

Reporter(s): Clément Lecigne of Google’s Threat Analysis Group (TAG)

The Code


<!-- saved from url=(0014)about:internet -->
<meta http-equiv="X-UA-Compatible" content="IE=8"></meta>
<script language="Jscript.Encode">

    var spray = new Array();

    function F() {
        // 2. Create a bunch of objects
        for (var i = 0; i < 20000; i++) spray[i] = new Object();

        // 3. Store a reference to one of them in the arguments array
        //    The arguments array isn't tracked by garbage collector
        arguments[0] = spray[5000];

        // 4. Delete the objects and call the garbage collector
        //    All JSCript variables get reclaimed... 
        for (var i = 0; i < 20000; i++) spray[i] = 1;

        // 5. But we still have reference to one of them in the
        //    arguments array

    // 1. Call sort with a custom callback


Exploit sample: be8fdfce55ea701e19ab5dd90ce4104ff11ee3b4890b292c46567d9670b63b82

Did you have access to the exploit sample when doing the analysis? Yes

The Vulnerability

Bug class: JScript VAR object not tracked by garbage collector, use-after-free

Vulnerability details:

The vulnerability is a member of the use-after-free bug class in JScript where variables (represented by the VAR structure) aren’t properly tracked by the garbage collector. In this case, the arguments object is not tracked by the garbage collector during the Array.sort callback. Thus, during the Array.sort callback, it is possible to assign a variable to the arguments object, have it garbage-collected (as long as it is not referenced anywhere else) and still access it later, causing the use-after-free.

Patch analysis: N/A

Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.): Variant analysis

Based on the blogpost from Google TAG, this bug was exploited by the same actor who exploited CVE-2018-8653. CVE-2018-8653 is a use-after-free in isPrototypeOf callback when the this variable is not tracked by the garbage collector. Therefore it’s likely that the actor found this bug by performing variant analysis on CVE-2018-8653, looking for vulnerabilities in the same bug class.

(Historical/present/future) context of bug:

  • Jan 2018 - P0 researcher identifies bug class: JScript variables (in VAR structure) not tracked by garbage collector. Finds numerous bugs (P0 1504, P0 1505, P0 1506, P0 1587) through fuzzer, which is open-sourced.
  • Dec 2018 - First use of an exploit exploiting this bug class is found by Google’s Threat Analysis Group (TAG): CVE-2018-8653. Bug couldn’t be found by the open-sourced fuzzer because the vulnerability uses features not supported by fuzzer at the time.
  • Sept 2019 - CVE-2019-1367 (this bug) is discovered by Google TAG.
  • Oct 2019 - Google TAG discovers fix for CVE-2019-1367 is incomplete. Project Zero discovers trivial variant.
  • Nov 2019 - The incomplete patch and the variant are patched as CVE-2020-1429.
  • Jan 2020 - TAG discovers another in-the-wild exploit sample (CVE-2020-0674) using a trivial variant of CVE-2019-1367/CVE-2019-1429. Microsoft issues advisory. It is used as the sandbox escape with CVE-2019-17026 on Firefox.
  • Feb 2020 - Microsoft patches the exploited 0-day as CVE-2020-0674.

CVE-2018-8653, CVE-2019-1367, CVE-2019-1429, and CVE-2020-0674 all used the sample exploitation method.

The Exploit

Is the exploit method known? Yes

Exploit method:

Given the UAF bug described above, the exploit frees the VAR structures (always allocated as a block of 100 VARs) and replaces the freed memory with a controllable object property name, which is a technique first demonstrated in P0 1587 and also used in CVE-2018-8653. This gives an attacker a powerful primitive: the ability to fake JScript variables.

From there, the exploit constructs a fake RegExp object, which is used to achieve arbitrary memory read-write. The exploit directly replaces regexp.code with their custom compiled RegExp bytecode giving them a write-what-where primitive. The exploit uses regexp.source (unused bSTR) for the read primitive.

The same exploit is used twice: Once to achieve code execution inside the browser, and the second time to escape the browser sandbox using the WPAD service (which runs as a privileged process). This is also a publicly known exploitation technique described here.

The Next Steps

Variant analysis

Areas/approach for variant analysis (and why):

We took two approaches to this variant analysis: manual analysis and a fuzzer. In the manual analysis approach, we manually attempted to free function arguments in every JScript callback that we knew. In the fuzzer approach, we ran another fuzzing sessions with a modified JScript.dll to more easily check this bug class. The modifications included:

  • Freed VARs are modified so that accessing them would cause an immediate crash, and
  • Freed VARs are never allocated again

Found variants:

  • P0 1947 (CVE-2019-1429): Use-after-free where members of the arguments object aren’t tracked by the garbage collector during the toJSON callback.

Structural improvements

  • Bug classes should be fixed comprehensively, not just fixing each vulnerability individually. For this bug classes, that likely would include re-writing the garbage collector.
  • Quality and complete patches need to be prioritized. CVE-2019-1367 was not fixed the first time and the trivial variant also wasn’t patched. This gave the attackers another opportunity to exploit the bug against the users, which they did. Sharing proposed patches with the reporter could help identify these issues earlier.
  • JScript and Internet Explorer are now considered “legacy” software. Remove them from being accessible by default in the Windows operating system to reduce the attack surface.

0-day detection methods

  • Look for any scripts that want to use JScript as their JS engine outside of a local intranet.
  • Look for scripts that use the Enumerator object due to that being Microsoft specific and one of the known methods for exploiting the UAF to get remote code execution.
  • Look for scripts that attempt to trigger CollectGarbage.

Other References