--enable-blink-features=MojoJS. If this feature is activated, the browser will expose a
arguments.length. The JS optimiser incorrectly assumes that the maximum length of arguments is
65534, while actually the it can be larger. From this wrong estimation, optimiser evaluate that
arguments.length >> 16will always be
0(which is incorrect).
3of an array with length
1, the bound checking will be done and no operation will be done. In the example below, it will output expected result
arguments.length >> 16is always
xour arguments length, we can define
x = 65537, so
x >> 16is actually
i, we know that
(x >> 16) * i == i. Meanwhile, optimiser assumes that
((x >> 16) * i)will evaluate to
(0 * i)which is always
arr[(x >> 16) * i], optimiser will assume that it will always evaluate to accessing index
0, hence bounds-checking is not needed. Though in reality, it actually evaluate to accessing index
['y']. Does this means that our exploit does not work? This is related to JIT paradigm in Chrome.
funfunction, eliminating the bounds checking.
Writefunction to write a blob data, the browser process will retrieve the BlobData using asynchronous function, and provide a callback to it. In the provided callback, FileWriterImpl is providing a reference to
thisor the FileWriterImpl instance itself with
base::Unretained(this)creates an unchecked reference of the
FileWriterImplinstance. This could be dangerous if the
FileWriterImplinstance is already freed when the callback is called, as it will continue its execution while referring to a stale pointer that refer to an already freed object.
freeof the unretained reference.
GetBlobDataFromBlobPtr(), it will call an asynchronous function
GetInternalUUIDto get the blob UUID, then call our provided callback. Fortunately (or unfortunately?), the
GetInternalUUIDis a mojo interface method. This means that renderer can define the implementation of
GetInternalUUIDif it passes a renderer-hosted Blob implementation instead of browser-process-hosted blob.
GetInternalUUID, we can destroy the renderer handle to FileWriter. This will trigger immediate destruction (free) of FileWriterImpl. Thus, when
GetInternalUUIDreturns, it will call the callback while using the provided
base::Unretained(*FileWriterImpl), which is a stale pointer of an already freed object. Normally this will cause the browser process to crash.
(enabled_bindings_ & BINDINGS_POLICY_MOJO_WEB_UI)is true). By using OoB memory access bug, we can write and set the
BINDINGS_POLICY_MOJO_WEB_UIvalue. Then, we can reload the page so
enabled_bindings_value accordingly to enable MojoJS binding. Then reload the page.
index.html, we are setting up the out-of-bound read/write bug by exploiting CVE-2019-5782. Then, at first we visit the page, we enable the Mojo binding, and reload the page. Now that the Mojo binding is enabled (not undefined), we call the
crashfunction, we are registering a blob with id
blob_0to blob registry, then we define our custom Blob implementation with a malicious implementation of
getInternalUUID. Finally we call the
Writefunction with a custom renderer-hosted blob implementation.
getInternalUUID, we free the
FileWriterImplinstance. When the function return and the execution is passed to
DoWrite, the freed / stale pointer will be used, causing the browser to crash.