Kuboid
Open Luck·Kuboid.in
Black Hat2023
Open in YouTube ↗

Reviving JIT Vulnerabilities: Unleashing the Power of Maglev Compiler Bugs on Chrome Browser

Black Hat1,726 views36:35about 2 years ago

This talk explores the security architecture of the V8 JavaScript engine's Maglev compiler, identifying unique attack surfaces introduced by its optimization and deoptimization phases. The researchers demonstrate how to leverage differential fuzzing and code review with CodeQL to discover vulnerabilities in Maglev's graph building, register allocation, and deoptimization logic. The presentation highlights several critical bugs, including type confusion and out-of-bounds read/write, and provides a case study on achieving remote code execution in the Chrome browser. The findings emphasize the importance of rigorous compiler security testing as new optimization layers are introduced.

Exploiting the Maglev Compiler: How V8 Optimization Layers Create New Attack Surfaces

TLDR: The introduction of the Maglev compiler in V8 creates a new, high-performance optimization layer that introduces unique memory corruption primitives. By leveraging differential fuzzing and custom CodeQL queries, researchers identified critical type confusion and out-of-bounds vulnerabilities within Maglev’s graph building and register allocation phases. Pentesters should prioritize auditing these newer JIT optimization stages, as they often lack the mature hardening applied to older components like TurboFan.

Modern browser security is a constant game of cat and mouse played within the Just-In-Time (JIT) compiler. As vendors like Google push for faster JavaScript execution, they introduce increasingly complex optimization layers. The Maglev compiler, designed to bridge the gap between the fast but unoptimized Sparkplug and the highly optimized but slower TurboFan, is the latest battleground. While Maglev aims for efficiency, its unique approach to graph building and register allocation introduces novel attack surfaces that bypass traditional mitigations.

The Mechanics of Maglev Vulnerabilities

Maglev operates on a Static Single Assignment (SSA) form, similar to TurboFan, but its implementation of graph building and register allocation is distinct. The core issue lies in how Maglev handles type information during its optimization phases. Unlike older compilers that have undergone years of hardening, Maglev’s rapid development cycle has left gaps in its type-checking logic.

During the graph building phase, Maglev converts bytecode into an intermediate representation (IR). If the compiler makes incorrect assumptions about the types of objects being processed, it can generate machine code that performs operations on memory regions it should not access. For example, if the compiler assumes a variable is always an integer when it could be a floating-point number, it may omit necessary bounds checks. This leads to classic memory corruption primitives, such as out-of-bounds reads and writes, which are the bread and butter of modern browser exploitation.

Finding Bugs with Differential Fuzzing

Discovering these bugs manually is a fool's errand. The researchers behind this work utilized differential fuzzing, a technique where the same input is fed into multiple compiler versions or configurations to identify discrepancies in output. By comparing the machine code generated by Maglev against the output of more stable compilers, they could isolate code paths where Maglev’s logic diverged, often indicating a vulnerability.

Tools like fuzz-it and custom implementations of JIT-picker were instrumental in this process. These tools allow researchers to automate the generation of test cases that specifically target the JIT compiler's complex state machine. When a crash occurs, Turbolizer provides the necessary visualization to trace the graph transformation, allowing the researcher to pinpoint exactly which optimization pass introduced the flaw.

Technical Deep Dive: Type Confusion and OOB Access

One of the most interesting findings involved a type confusion bug in the register allocation phase. Maglev uses a "scratch register" to save and restore object states during deoptimization. If the compiler incorrectly tracks the type of the object stored in this register, it can lead to a situation where the register contains a pointer to an object, but the compiler treats it as a raw integer.

Consider a scenario where the compiler generates code to access an array index. If the bounds check is optimized away due to a faulty type assumption, an attacker can provide a negative index or an index far beyond the array's allocated memory. This results in an out-of-bounds read or write, allowing for the corruption of adjacent heap objects. By carefully crafting the heap layout, an attacker can turn this primitive into a full read/write capability, eventually leading to arbitrary code execution.

Real-World Applicability for Pentesters

For those performing browser-based penetration tests or participating in bug bounty programs, the focus should shift toward these newer JIT components. While the OWASP Top 10 covers general web vulnerabilities, browser exploitation requires a deep understanding of the underlying engine. When testing applications that rely on complex JavaScript or WebAssembly, look for patterns that trigger JIT compilation, such as long-running loops or frequent function calls with varying object types.

If you are hunting for bugs, do not just rely on generic fuzzers. Develop custom scripts that force the browser to switch between different optimization tiers. By triggering deoptimization—where the engine reverts from optimized machine code back to interpreted bytecode—you can often uncover edge cases that the compiler developers missed.

Defensive Considerations

Defending against these attacks is notoriously difficult because the vulnerabilities exist within the core logic of the browser engine itself. The most effective defense is to keep browsers updated to the latest version, as vendors are constantly patching these JIT-related flaws. For enterprise environments, implementing strict Content Security Policy (CSP) headers and disabling unnecessary browser features can limit the impact of a successful exploit. However, the ultimate responsibility lies with the engine developers to implement more rigorous formal verification of their optimization passes.

The complexity of modern JIT compilers is a double-edged sword. While it provides the performance necessary for modern web applications, it also creates a massive, opaque attack surface. As we continue to push the boundaries of what browsers can do, we must ensure that our security research keeps pace with the architectural changes happening under the hood. The next time you see a browser update, remember that it likely contains fixes for the very types of compiler bugs that are currently defining the state of the art in exploit development.

Talk Type
research presentation
Difficulty
expert
Category
exploit dev
Has Demo Has Code Tool Released


Black Hat Europe 2023

47 talks · 2023
Browse conference →
Premium Security Audit

We break your app before they do.

Professional penetration testing and vulnerability assessments by the Kuboid Secure Layer team. Securing your infrastructure at every layer.

Get in Touch
Official Security Partner
kuboid.in