Maddie Stone, Project Zero (Originally posted on Project Zero blog 2020-08-05)
Disclosure or Patch Date: 03 April 2020
Product: Mozilla Firefox
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
Bug-Introducing CL: Unknown
Exploit sample: N/A
Did you have access to the exploit sample when doing the analysis? No
Bug class: use-after-free (UaF)
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
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
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.
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
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.
Is the exploit method known? N/A
Exploit method: Unknown
The Next Steps
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.
- Bug 165115: UAF in
StreamControl::CloseReadStreams, but the code is dead code. FF removed it.
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.