Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

The Chromium super (inline cache) type confusion- 1820

Man Yue Mo - GitHubPosted 3 Months Ago
  • Inline cache is an optimization in the V8 browser engine that speeds up property access. When a function is invoked, Ignition compiles it into bytecode, collecting profiling and feedback each time it is invoked. Based upon this feedback, the JIT compiler can optimize this well at a later stage. An object type always has a map in the beginning of the object used for the type of the object and offsets to its other properties. Once the object data is known, this information no longer needs to be calculated, thus speeding it up.
  • The vulnerability resides in the handling of super properties of the inline cache. With the usage of both prototypes and classes, the hierarchical information about these types is complicated. For instance, parents' prototypes can even be different from those of their children.
  • The LoadSuperIC function is used to cache super property (parent) access. The property is NOT on the receiver this but on the parent prototype. Because of this, assumptions about the object types and maps should be examined in both the receiver object and the parent protocol type object to ensure correctness. Notably, the confusion between the receiver and home_object has led to vulnerabilities in the past, such as CVE-2021-30517. This bug led to type confusion due to differences between cached and real data access.
  • The final concept to understand the bug is around megamorphic inline cache. In the simplest case, each function has its own inline cache for each property access. Sometimes, different functions can share an inline cache, making it megamorphic; a system-wide stub for handling the lookup.
  • The function simple_api_call, used for interaction between the V8 engine and the Blink rendering engine, requires a particular byte format. When a handler is created for a property accessor, the wrong map type is used; it accidentally uses the lookup_start_object instead of the super type. Because there's a type check and the types can differ between parent and child, this creates a type confusion vulnerability.
  • Blink is responsible for the Web API that provides objects for rendering the web page, but those objects aren't normally accessible via JavaScript. When calling the Blink APIs, there is a very specific type check to ensure that the functions to Blink are the correct type and conform to the API call format. Because of the incorrect type usage check from above, it's possible to bypass the check for the type. In particular, a blink getter() can be called on an arbitrary type; this is their primitive. This type confusion is very powerful for developing further primitives.
  • The first new primitive is an arbitrary relative read of 8 bytes from the DeviceMotionEvent::interval function. By using this on the DOMMatrix type, the interval field, used to read data based on the interval data, we can specify the offset and read from there. The next primitive is getting an arbitrary objects address in order to corrupt it. The type Uint8ClampedArray in an ImageData type can be overlayed with DOMMatrix again to read the address of the code>Uint8ClampedArray array.
  • Finally, the goal is to create an arbitrary fake object. The getter of some objects sometimes returns JavaScript objects. By causing a type confusion on the value returned from the getter, an arbitrary write can be achieved. In particular, the signal property of Request returns a ScriptWrappable type. By overlaying the AudioData timestamp field with the request data, the timestamp is effectively the address of the object being returned. This is great for creating a OOB read/write primitive from an array. That's game over :)
  • Functionality in V8 is often implemented in multiple places, depending on the optimization level. This leads to the same bug occurring multiple times on tricky sections of code. They found the same bug in a different section of the JIT code, which is pretty interesting. So, if a codebase has made a mistake once before, check other places where this issue could also appear.
  • The vulnerability was complicated but a variant of several bugs. The exploitation requires very deep knowledge of the API interactions with Blink, but gave a very powerful primitive once it was understood. The more complex the target, the better primitives you likely have. In this case, it was worthwhile to take this type confusion to RCE in the sandbox by understanding this integration point. Great post!