Introduction
Today, we’re excited to announce a new security protection in Microsoft Edge and other Chromium-based browsers that defends against attackers being able to leverage an exploit in a Renderer Process to escape the Renderer sandbox. The change prevents attackers from using an exploit to enable the Mojo JavaScript bindings (MojoJS) for their site context within the Renderer. In the following section we will explain what Mojo and MojoJS are, how attackers can abuse them, and how the new behavior prevents attackers from using them.
What is Mojo and MojoJS?
Chromium-based browsers use a multi-process architecture. Within this architecture, web site contents, including all untrusted JavaScript code, are parsed and executed in a special type of “sandbox” process called a Renderer Process. Such processes have no access to various important resources and thus the direct damage that can be done from compromising them is limited. As a result, a primary goal of attackers is to “escape the sandbox” and compromise a higher-privilege process with more access to critical resources, such as the core Browser Process.
For processes to communicate, messages need to be sent across an IPC (Inter-Process Communication) channel. Mojo is the name for the Chromium implementation of IPC functionality. It is an integrated collection of runtime libraries that provides a platform-agnostic abstraction of common IPC primitives. It also provides a bindings library with code generation for multiple target languages.
With respect to the bindings library, of particular interest to this work is the JavaScript bindings API, which we will refer to as MojoJS for the rest of this post. MojoJS allows JavaScript code running within a Renderer Process to access certain Mojo interfaces that are exposed by Chromium.
MojoJS brings with it inherent risks, as it could allow untrusted JavaScript to call arbitrary Mojo interface methods in another process, such as the higher privilege Browser Process. While Mojo interfaces should be secured against malicious use, exploitable bugs do occur. The ability to easily call Mojo interface methods from untrusted JavaScript would make it much easier for attackers to take advantage of any such exploits and gain sandbox escape. For this reason, MojoJS is not enabled for arbitrary web content. Generally, only Renderers for certain trusted functionality, where the corresponding JavaScript itself is trusted, will enable this functionality. The primary example would be Renderers that host WebUI content, where the JavaScript is provided from Microsoft Edge (or other relevant Chromium implementation) source itself and not the outside world. Fuzz testers also use MojoJS via a special command-line option, though this use case is not relevant to or impacted by the new protection.
The Attack Surface
Even though MojoJS is usually disabled, an attacker can enable it if they’ve identified a Renderer exploit that gives them an arbitrary r/w primitive. By altering one of two simple properties of the “RenderFrame”, the object representing the contents of the web document, and then reloading the page, the attacker can force MojoJS to be enabled for their site. At this point, any supported Mojo interface can be called from their JavaScript, allowing them direct access to cross the security boundary to the Browser Process and moving them one step closer to full sandbox escape.
Besides being a relatively easy attack vector, as it only requires flipping a Boolean value or appending a bitwise flag, this approach also has the benefit of not requiring the attacker to have to insert executable code. With MojoJS enabled, execution of the Mojo code is done right from the untrusted JavaScript, and thus is not protected against by mitigations like Arbitrary Code Guard (ACG) (which is available in Edge as part of Enhanced Security Mode). As a result, this is likely one of the first steps most attackers take when turning an exploitable vulnerability in the Renderer into a full sandbox escape attack.
This attack was described in the post Project Zero: Virtually Unlimited Memory: Escaping the Chrome Sandbox. While both the Renderer exploit that allowed them to enable MojoJS and the Browser Process exploit that allowed them to escape the sandbox have been addressed, the ability to so easily enable MojoJS for the Renderer has not been addressed. Until now.
New Protection Layer
To combat this attack vector, we added a new layer protecting the MojoJS enabled/disabled state. While the two simple properties of RenderFrame described above are still used to determine if MojoJS should be enabled for a specific site or not, a new property for the Renderer Process is added. This new property is a Boolean value indicating whether MojoJS can be enabled for any individual site context being processed by the Renderer. This new value is stored in a special memory location that is kept as read-only while the Renderer is processing site contents. As a result, if an attacker attempts to use an arbitrary r/w primitive to alter this value, it will immediately crash the process.
When it comes time for the code to enable MojoJS, if the state values say it should be enabled for the site, the code checks this new “protected” Boolean property before enabling the bindings. If the value is false, the code immediately crashes the Renderer Process as this indicates the Renderer has been compromised. The attacker is prevented from enabling MojoJS, and cannot use this mechanism in their sandbox escape attempt.
Typical users will not notice any change because the new property is set to true for WebUI hosts and other trusted scenarios that use MojoJS.
We contributed this new protection to the Chromium Project in the following change lists:
5161001: Restore base::ProtectedMemory
5218835: Use base::ProtectedMemory to harden MojoJS enablement
As a result, all Chromium based browsers will be able to benefit from this new protection.
We’d like to thank Will Harris from Google for his contributions to this work, including providing background research, helping with the design, and providing prototype code from his earlier explorations into solving this issue. We’d also like to thank the Chromium reviewers who took the time to review these change lists and provide valuable feedback.
Current Status in Microsoft Edge and Next Steps
It has been enabled for 50% of users in Canary, Dev, and Beta channels, and will be rolling out, in coordination with other browsers, to stable users in Edge 123. In addition, currently this protection is only available for Windows 10 and Windows 11. We will be adding support for other platforms in the future.
This is just one of the many ways Edge is protecting users. In an upcoming blog post, we’ll be sharing a detailed analysis on various other attack vectors and how Edge defends against them. Stay tuned!