CVE-2020-6820: Firefox use-after-free in Cache

Posted by Maddie Stone, Project Zero (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
Bug-Introducing CL: Unknown
Proof-of-Concept: N/A
Exploit Sample: N/A
Access to the exploit sample? No
Reporter(s): Francisco Alonso @revskills working with Javier Marcos of @JMPSec

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 ReadStreams 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()

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.

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. 

Is the exploit method known? N/A
Exploit method: Unknown.

How do you think you would have found this bug? It seems likely that this vulnerability could have been found by:
  1. Manual code auditing/variant analysis after seeing the patch for Bug 1507180 which is a similar UaF in the same module, or
  2. 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. 

Areas/approach for variant analysis: 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:

Potential detection methods for similar 0-days: 
Firefox has crash logs for common crashes here. We could potentially find failed exploitation attempts in these crash logs.  

Other references: N/A

No comments:

Post a Comment