Overview of WebAssembly Type Confusion in JavaScript Engines Exploitation
Introduction
WebAssembly is a relatively low-level language and virtual machine, much closer to a real CPU than a higher-level language like JavaScript. Initially, WASM supports basic types:
Type | Description |
---|---|
i32 | 32-bit integer |
i64 | 64-bit integer |
f32 | 32-bit floating point |
f64 | 64-bit floating point |
However, with WebAssembly Garbage Collection (WASMGC) extension, WebAssembly can now support more complex types.
In simplified terms, the idea of garbage collection is the attempt to reclaim memory which was allocated by the program, but that is no longer referenced.
WasmGC now adds struct and array heap types, which means support for non-linear memory allocation. Each WasmGC object has a fixed type and structure, which makes it easy for VMs to generate efficient code to access their fields without the risk of deoptimizations that dynamic languages like JavaScript have.
Reference Types
Consider the type s1
the type (ref null $s1)
is a reference type of s1
or null
.
Struct Types
Array Types
Recursive Types
Recursive types of Wasm can define mutually recursive types.
External Types
External types can refer to types defined in host environments, for example, JavaScript.
Case Study
CVE-2024-2887
https://github.com/KpwnZ/browser-pwn-collection/tree/main/v8/CVE-2024-2887
CVE-2024-6100
Iso-recursive Types
The type constructor that solves recursive type equation can be represented as:
And
We call the left-to-right substitution unfolding, and the right-to-left is folding. The value of iso-recursive type must be introduced and aliminated using term-level operators. That is, we need type annotations to determine the type of the value when folding to ensure the uniqueness.
WasmGC supports type comparison between types from their recursive groups in different modules.
When it decodes type section
In AddRecursiveGroup
method
which means for a type i
, the canonical type index is module->isorecursive_canonical_type_ids[i]
and canonical_supertypes_
maintains the relationship of subtyping. Recall the writeup of CVE-2024-2887, there is a maximum limitation for type index, which is kV8MaxWasmTypes = 1000000
. But the code above does not check the boundary of canonical_supertypes_
and isorecursive_canonical_type_ids
.
Consider the definition of ValueType
we only have 20bits for kHeapTypeBits
and yes that’s enought for heap types but not for canonical type index since there is no boundary check for it. Thus we can have some type confusion strategies:
- If we have a type
i
with a canonical type index(n << 20) + t
it will be confused with type with canonical type indext
. - We can create type confusion between internal reserved heap types and canonical type index.
In JSToWasmObject()
And heap_representation_non_shared()
will call heap_representation()
which is defined as
The heap type conversion simply take the bit field of the heap type. That means we can mess up the canonical type index and internal reserved heap type.
Proof of Concept
Here is the proof of concept:
CVE-2024-8194
Google fixed CVE-2024-6100 by adding the following patch:
It will check the canonical type index when adding recursive group. But
kMaxCanonicalTypes
is too big to fit in 20 bits. Thus we can still overflow the canonical type index to create a type confusion.
When it adds new recursive group
Method CanonicalizeTypeDef
will convert type index in structure to canonical type index.
In
When type.ref_index() >= recursive_group_start
it will canonicalize with relative index.
We can control this relative index by
when n >= recursive_group_start
. This makes us able to craft a type confusion between types with index 0x1000000 | n
and n
.
Proof of Concept
References
- Seunghyun Lee (@0x10n): WebAssembly Is All You Need: Exploiting Chrome and the V8 Sandbox 10+ times with WASM
- Yaoda Zhou, Bruno C. d. S. Oliveira, Jinxu Zhao: Revisiting Iso-Recursive Subtyping
- ANDREAS ROSSBERG: Mutually Iso-recursive Subtyping (Expanded)