[Kernel] BlackhatMEA Quals 2024 - CPL0 challenge - Interrupt Descriptor Table (IDT) Hooking


  ( بِسْمِ اللَّـهِ الرَّحْمَـٰنِ الرَّحِيمِ )

(إن أحسنت فمن الله، وإن أسأت فمن نفسي والشيطان)


Hey guys,

let's try to solve a kernel exploit, and challenge CPL0 from BlackhatMEA Quals 2024 thanks to Saif (@wr3nchsr) and Sameh (@s4muii) for help understanding this challenge. This challenge has a new idea for me so I learned a lot so let's talk about it.

now we will do the following:

  • do recon on the challenge files
  • get root on the local version for debugging (by modifying the file system)
  • use bash scripts to compress and decompress the file system to pass it to qemu
  • list what we will have and do in the challenge
  • EXPLOIT


Recon

we have 

  • bzImage: Kernel
  • rootfs.cpio: the file system
  • run.sh: bash script to run qemu
  • qemu-system-x86_64: a modified qemu binary
  • qemu.diff: a diff file to the modified qemu binary

if we check the diff file

we can note that the check_cpl0 function is modified and will return "true" so we have access to CPL0, but what is CPL0???

Current Privilege Level (CPL) has different levels (0, 1, 2, 3) -chatGPT answer xD-

  • CPL 0 (Ring 0) – Kernel mode (highest privilege, can execute all instructions).
  • CPL 1 (Ring 1) – Usually unused or for device drivers (not common).
  • CPL 2 (Ring 2) – Usually unused or for specific OS services (not common).
  • CPL 3 (Ring 3) – User mode (lowest privilege, restricted access).

we have access to Ring 0 (CPL0) the highest privilege on the OS, what do we do with CPL0?

  • Execute more CPU instructions 
  • Access system memory
  • Hardware Devices

the interesting thing here is the CPU instructions that we can execute and i asked ChatGPT to check the answer (but from Saif, i knew what is the target which is IDT)

  • Interrupt Descriptor Table (IDT) (we can load and store addresses in this table)
  • Model-Specific Register (MSR) (extracts privileged info) (instruction: rdmsr)

"decompress.sh" will give us the file system so we can do our modification for debugging.

We know from "run.sh" there is KASLR in the kernel so we will disable it from "run.sh" and the loaded bash in the boot (etc/init.d/S99ctf) we will set the value in "kptr_restrict" to 1 not 2 and will modify the id in line 4 to be 0 (root), not 1000 (user) -this for debug-


The compress.sh and decompress.sh as the following will be used locally to debug



we will try to do IDT Hooking (manipulate IDT) with instructions (sidt, lidt) and the exploit will use "commit_creds(prepare_kernel_cred(&init_task))" If we call this we will be in root level (maybe like setuid(0)) and then we will execute "get_shell" function, but how?


EXPLOIT

our exploit will

  1. store the idt table into our script
  2. creates memory location "fake_idt" to store the manipulated IDT on it
  3. store the "exploit" function address into "fake_idt" but in the IDT format
  4. push the "fake_idt" to the memory
  5. save the state of the registered we will use it in PrivEsc
  6. write our exploit (and defeat the KASLR)
  7. trigger the IDT


setup the IDT table struct and other variables



creating the fake chunk filling the "exploit" function address to it in the right format, storing the "fake_idt" to the "new_idt" and loading it to the memory again

  • 0x8E (Interrupt Gate Type & Attributes)
  • 0x10 (Kernel Code Segment Selector)
this is the format for the IDT addresses

the main function will do the magic

now we will save the current state of registers because we will use it again in the exploit

finally the exploit function, first we will store the "old_idt" in memory again and start with the exploit.

We have to defeat the KASLR somehow, I learned this from Saif before knowing it from chatGPT is the method using MSR register we will read the MSR (rdmsr) and it will be stored in (rdx=low 32 bits and rax=high 32 bits) will combine the two parts and subtract the offset of MSR register from it and now we will have the base kernel address.

Example:

rdx=0x040506
rax=0x010203
r15=0x010203040506
r12=r15-(offset of register) -> will be the base kernel address

now we will complete the exploit in assembly to call "commit_creds(prepare_kernel_cred(&init_task))" and return the state of registers.


let's return the local file to its normal state and try the exploit


we got the shell ;-).

Thank you for reading, the solver.

عن ابن عباس قال قال رسول الله صلى الله عليه وسلم
 (نعمتان مغبون فيهما كثير من الناس الصحة والفراغ) 
أخرجه البخاري وابن ماجه

Comments

Popular posts from this blog

[BlackHatMEA-CTF 2024] cockatoo PWN challenge

[WEB] ASC Wargame CTF 2024 - Challenge Hot Proxy

Lets Analysis STM32F103 Chip Firmware from Attify