Bug 1542581 Comment 0 Edit History

Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.

Hi,

There is a race condition in the Windows implementation of the CrashGenerationServer leading to a write of a freed object in Firefox main process. This crash server is enabled by default on Firefox release build and any sandboxed processes can send messages on a pipe : "\\.\pipe\gecko-crash-server-pipe.{ParentPID}" (passed to the sandbox in the command line).

The vulnerability exists in the following function:
bool CrashGenerationServer::AddClient(ClientInfo* client_info) {
  HANDLE request_wait_handle = NULL;
  /* ... */ 
  // OnClientEnd will be called when the client process terminates.
  HANDLE process_wait_handle = NULL;
  if (!RegisterWaitForSingleObject(&process_wait_handle,
                                   client_info->process_handle(),
                                   OnClientEnd, // This callback can FREE client_info (no lock)
                                   client_info,
                                   INFINITE,
                                   WT_EXECUTEONLYONCE)) {
    return false;
  }

  client_info->set_process_exit_wait_handle(process_wait_handle); // UAF write (crash)
/* ... */
}

The OnClientEnd callback frees the client_info by calling the following function:
CrashGenerationServer::HandleClientProcessExit(ClientInfo* client_info) { // Called by CrashGenerationServer::OnClientEnd
/* ... code that doesn't block / wait  */
  delete client_info; // FREE client_info
}

Crash:

To compile an ASAN build of the client, you need to remove this line from the build config:
ac_add_options --disable-crashreporter
See attachment the ASAN crash report "race_asan_report.txt" reproduced on a local build (changeset 527669:bd1e28b0143b from Mar 29).


Exploitability:

The exploitation assumes having code execution in sandboxed renderers processes (RCE -> *LPE* step targeted).

The only way to trigger the OnClientEnd callback is to terminate the process being registered.
The pipe is created with maximum one instance (nMaxInstances=1 and FILE_FLAG_FIRST_PIPE_INSTANCE), so you have one shot by process (you can't spray connections to AddClient) but the crash generation server doesn't check if the registered client is already registered and if its PID matches the caller on the pipe (one process can register anyone see HandleReadDoneState).

To exploit this, you can either have two threads in one process, one sending the ProtocolMessage to add the client and the other one terminating the process OR two sandboxed process, one registering the other process and the other killing itself.
If it's not successful, ask the broker to create a new tab (new process), replay your RCE to gain code execution and retry.
If successful, the UAF gives you a HANDLE write at a fixed offset in the broker heap.
By spraying an exploit convenient object (semi controlled by another IPC channel to the broker like IPDL), you can use this vulnerability to corrupt a field (size/flags) in it and with some work, achieve code execution in Firefox main process (Sandbox escape). 

Thank you
Hi,

There is a race condition in the Windows implementation of the CrashGenerationServer leading to a write of a freed object in Firefox main process. This crash server is enabled by default on Firefox release build and any sandboxed processes can send messages on a pipe : "\\.\pipe\gecko-crash-server-pipe.{ParentPID}" (passed to the sandbox in the command line).

The vulnerability exists in the following function:
```cpp
bool CrashGenerationServer::AddClient(ClientInfo* client_info) {
  HANDLE request_wait_handle = NULL;
  /* ... */ 
  // OnClientEnd will be called when the client process terminates.
  HANDLE process_wait_handle = NULL;
  if (!RegisterWaitForSingleObject(&process_wait_handle,
                                   client_info->process_handle(),
                                   OnClientEnd, // This callback can FREE client_info (no lock)
                                   client_info,
                                   INFINITE,
                                   WT_EXECUTEONLYONCE)) {
    return false;
  }

  client_info->set_process_exit_wait_handle(process_wait_handle); // UAF write (crash)
/* ... */
}
```

The OnClientEnd callback frees the client_info by calling the following function:
```cpp
CrashGenerationServer::HandleClientProcessExit(ClientInfo* client_info) { // Called by CrashGenerationServer::OnClientEnd
/* ... code that doesn't block / wait  */
  delete client_info; // FREE client_info
}
```

Crash:

To compile an ASAN build of the client, you need to remove this line from the build config:
`ac_add_options --disable-crashreporter`
See attachment the ASAN crash report "race_asan_report.txt" reproduced on a local build (changeset 527669:bd1e28b0143b from Mar 29).


Exploitability:

The exploitation assumes having code execution in sandboxed renderers processes (RCE -> *LPE* step targeted).

The only way to trigger the `OnClientEnd` callback is to terminate the process being registered.
The pipe is created with maximum one instance (`nMaxInstances=1` and `FILE_FLAG_FIRST_PIPE_INSTANCE`), so you have one shot by process (you can't spray connections to `AddClient`) but the crash generation server doesn't check if the registered client is already registered and if its PID matches the caller on the pipe (one process can register anyone see `HandleReadDoneState`).

To exploit this, you can either have two threads in one process, one sending the `ProtocolMessage` to add the client and the other one terminating the process OR two sandboxed process, one registering the other process and the other killing itself.
If it's not successful, ask the broker to create a new tab (new process), replay your RCE to gain code execution and retry.
If successful, the UAF gives you a `HANDLE` write at a fixed offset in the broker heap.
By spraying an exploit convenient object (semi controlled by another IPC channel to the broker like IPDL), you can use this vulnerability to corrupt a field (size/flags) in it and with some work, achieve code execution in Firefox main process (Sandbox escape). 

Thank you

Back to Bug 1542581 Comment 0