CVE-2020-6820: Firefox use-after-free in Cache
Maddie Stone, Project Zero (Originally posted on Project Zero blog 2020-08-05)
The Basics
Disclosure or Patch Date: 03 April 2020
Product: Mozilla Firefox
Advisory: https://www.mozilla.org/en-US/security/advisories/mfsa2020-11/
Affected Versions: pre-74.0.1
First Patched Version: Firefox 74.0.1 and and Firefox ESR 68.6.1
Issue/Bug Report: https://bugzilla.mozilla.org/show_bug.cgi?id=1626728
Patch CL: https://hg.mozilla.org/mozilla-central/rev/6639deb894172375b05d6791f5f8c7d53ca79723
Bug-Introducing CL: Unknown
Reporter(s): Francisco Alonso @revskills working with Javier Marcos of @JMPSec
The Code
Proof-of-concept: N/A
Exploit sample: N/A
Did you have access to the exploit sample when doing the analysis? No
The Vulnerability
Bug class: use-after-free (UaF)
Vulnerability details:
There is a UaF of the CacheStreamControlParent
when closing its last open read stream. The read stream is the response returned to the context process from a cache query. If the close or abort command is received while any read streams are still open, it triggers StreamList::CloseAll
. If the StreamControl
(must be Parent to get a UAF in browser process, the Child would only provide in renderer) still has ReadStream
s when StreamList::CloseAll
is called, then this will cause the CacheStreamControlParent
to be freed. The mId
member of the CacheStreamControl
parent is then subsequently accessed, causing the use-after-free. The execution flow is shown below.
StreamList::CloseAll /* PATCHED FUNCTION */
CacheStreamControlParent::CloseAll
CacheStreamControlParent::NotifyCloseAll
StreamControl::CloseAllReadStreams
For each stream:
ReadStream::Inner::CloseStream
ReadStream::Inner::Close
ReadStream::Inner::NoteClosed
…
StreamControl::NoteClosed
StreamControl::ForgetReadStream
CacheStreamControlParent/Child::NoteClosedAfterForget
CacheStreamControlParent::RecvNoteClosed
StreamList::NoteClosed
If StreamList is empty && mStreamControl:
CacheStreamControlParent::Shutdown
Send__delete(this) /* FREED HERE */
PCacheStreamControlParent::SendCloseAll /* USED HERE IN CALL TO Id() */
I did not get a working trigger for this vulnerability that didn’t involve shutting down the process. The difficulty is getting there to still be a ReadStream
when StreamList::CloseAll
is closed. It seems that others, including Firefox engineers, also haven’t found a way to trigger this bug and similar bugs that require winning this race condition that doesn’t involve shut down (here, here, and here). If you have found a way to trigger without shutting down the process, please share.
Patch analysis:
The patch fixes the vulnerability by setting mStreamControl
to nullptr prior to calling CacheStreamControlParent::CloseAll
. This then prevents the call to CacheStreamControlParent::Shutdown
and the freeing of the CacheStreamControlParent
.
Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):
It seems likely that this vulnerability could have been found by:
- Manual code auditing/variant analysis after seeing the patch for Bug 1507180 which is a similar UaF in the same module, or
- Fuzzing IPDL interfaces. Fuzzing may be slightly more likely due to the difficulty that even Firefox engineers have had in reproducing this bug.
(Historical/present/future) context of bug:
- CVE-2020-6820 was used as a chain with CVE-2020-6819 (bug report), where 6819 is the renderer exploit and 6820 is the sandbox escape. According to this comment, these two vulnerabilities were found by visiting a URL known to be affiliated with a threat actor, (assumedly) using ASAN Firefox builds.
- Bug 1507180 is a very similar UaF to this vulnerability. It was patched in December 2019 with an explanatory comment. The bug with ASAN crash wasn’t derestricted until June 2020, but someone may have root caused the bug based on the patches and found this other variant. It’s interesting that engineers for both this vulnerability and the previous bug 1507180 struggled to reproduce the crashes, this likely led to the “race condition” wording of the advisory.
The Exploit
Is the exploit method known? N/A
Exploit method: Unknown
The Next Steps
Variant analysis
Areas/approach for variant analysis (and why): The IPDL interface for the Cache subsystem is very complex. Since there have already been two very similar bugs found in the closing process involving streams, it would be worthwhile to look for other use-after-frees here too.
Found variants:
- Bug 165115: UAF in
StreamControl::CloseReadStreams
, but the code is dead code. FF removed it.
Structural improvements
Since the vulnerability was disclosed, Firefox has already begun reducing the use of raw pointers in the Cache module. https://bugzilla.mozilla.org/show_bug.cgi?id=1627892
0-day detection methods
Firefox has crash logs for common crashes here. We could potentially find failed exploitation attempts in these crash logs.