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!

From object transition to RCE in the Chrome renderer- 1861

Man Yue Mo - GitHubPosted 2 Months Ago
  • In JavaScript interpretters, there's a map (known as a hidden class) that represents the memory layout of a object. A map holds an array of property descriptors that contain information about each property, as well as the elements and their types. These maps are shared between objects that have the same layout. If a map doesn't exist, then a new one is created. When this happens, the old and new map are related by a transition that occurs to go from one map to another.
  • When doing this transition, the old map and new map have coinciding pointers to each other. A map can have multiple transitions. For instance, if property b is added and then property c is added, this creates two transition objects. If a field type is changed, such as going from an integer to a double, the map of the object is changed to reflect this via a transition.
  • In the example above, with o1 and o2 having a as an integer, if o1 gets a set to a double then the map in o2 is set to deprecated. This is because SMI (internal small integer) can be represented by a more generalized value. Eventually, the o2 object will be updated to the map of o1 once a property is accessed.
  • In v8, object properties can be stored in an array or a dictionary. Objects with properties stored in an array are fast objects while objects within properties in dictionaries are dictionary objects. Map transitions and deprecations specific to fast objects. Normally, when a map deprecation occurs, another fast map is created, but it's possible to make this not happen. In particular, if there are too many transitions in an object, then a new dictionary map is created instead.
  • Most uses PrepareForDataProperty are safe, there are two locations where the type can be updated to a dictionary map instead of the original object map. In CreateDataProperty, it may result in a dictionary map after an update. There are multiple routes to this but the usage of the spread syntax ended up being the most interesting.
  • When using the spread syntax (...obj1) and the usage of a property accessor, the function CreateDataProperty will be called while it's being cloned. While this cloning is happening, it's possible to deprecate the map while it's being used for the clone. This allows for the updated map to be a fast map instead of a dictionary map! A type confusion in the JavaScript engine leads to memory corruption now.
  • To exploit this, they used the type confusion to overwrite the elements to a large value within the underlying data structure for NameDictionary. By doing this, we get an OOB read for property values that leads to improper object access. Creating a "fake object primitive" is one of the best primitives in JavaScript engine exploitation. So, just arrange the heap in a nice way to create a fake object.
  • Once there, an arbitrary read/write is easy to gain. First, place an object into an array and use the OOB read to read the addresses of the objects stored within the array. For a write, do the same thing as a read but write to these objects instead.
  • Chrome recently implemented the V8 heap sandbox to isolate the V8 heap from other process memory, such as code, to prevent corruption within the V8 heap and access to other memory. So, to get code execution, it's a requirement to escape this requirement. To work around this, they modified DOM objects implemented in Blink. These are objects allocated outside of the v8 heap but are represented as API objects in v8. By causing type confusion in the API calls, it's possible to obtain a read/write primitive over the entire memory space.
  • Overall, a good post on exploitation and how to bypass a new defense-in-depth measure. Great stuff! If I had to guess how this bug was found, the author found a side effect that was not accounted for in some paths.