Download Horsepower - picoCTF 2021
Download Horsepower is a browser exploitation challenge from picoCTF 2021 featuring the V8 JavaScript engine.
Summary
First, let’s inspect the diff of the challenge’s patch file.
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;
}