Search This Blog

Saturday, February 3, 2024

Kernel pwn basics for CTF - GETTING MY HANDS DIRTY (Part 1) -- [hxpctf 2020 'kernel-rop']

For a while, i've been keen to try and learn about kernel pwn. Kernel pwn or Linux kernel exploitation is essentially vulnerability research and exploitation of the Linux kernel. This includes source code auditing to understand program behaviour, debugging & fuzzing for dynamic analysis, vulnerability discovery and exploit development. I'm writing this not having a 360 degree full-fledged understanding of Kernel pwn, but writing this so drill in the info in my head first, and then in yours (hopefully!). This used to look like such a strange and elite type of knowledge but since I've immersed myself a bit into it and just generally got my feet wet, It would'nt be so bad to share the one or two things I've learnt.
If you don't know what a Kernel is, then let this serve as a chilled way to start. The Kernel is hands down the most important part of a computer. It goes as far as handling all the communication between hardware and software in a system as well as manage all the syscalls (system calls) that the system needs to execute just for it to run well and smooth without any issues. Since this is particularly important, and you know this blog is all about hacking and learning more about computer security, then you already know what time it is!

I'm going to be diving in to kernel pwn and understanding all the exploit techniques used as well as how to bypass the relevant mitigations. Keep in mind that this series will be split up into different posts so get your mind ready for that too. As opposed to userland pwn, this series will be somewhat of an advanced topic, especially with regards to the background knowledge so just keep that in mind. Obviously, hacking into a system you have no permission to can result in serious repercussions that can potentially ruin your future in an instant, so I will preface this post with this:

***DO NOT HACK INTO A SYSTEM YOU HAVE NO EXPLICIT PERMISSION TO. IT IS ILLEGAL AND ENGAGING IN SUCH WILL LAND YOU IN JAIL. 

Got it? cool.

Let's start off.

In Linux, there are different types of kernel modules. There are 'char', 'block' and 'network' kernel modules and usually end with .ko extension (which stands for kernel object), while originally are written in C. 

In a typical kernel pwn CTF challenge, the task is essentially to achieve local privilege escalation by exploiting a vulnerable kernel module which is installed on boot. It is actually quite similar to userland pwn (normal pwnable binaries), except to what I know and seen, userland pwn exploit scripts are written in python but kernel pwn exploits are written in C. 

To go in depth, i'll go over the kernel-rop CTF challenge, that deals with how a typical linux kernel pwn challenge would look like and go over ways I've learnt to approach these challenges. Let's have a look at some key challenge files that we will be focusing on.


There are quite a bit of files here, but the following are the most important for qemu to use for emulation of the challenge:

vmlinuz - This is the actual linux kernel, except it has been compressed to make the size of the file smaller (sometimes, it is also named bzImage.

initramfs.cpio.gz - This is the linux file system (compressed with gzip and cpio) where the /bin, /etc and many other important linux file directories are found. The vulnerable kernel module is also located somewhere in the file system as well.

run.sh - This shell script actually contains the qemu boot configuration. (qemu is simply just a system emulation tool)

Now that we're aware of the important files needed to take on the challenge, the next thing we have to do is to use the extract-image.sh script, to extract the kernel ELF File (yes, the linux kernel is an ELF executable file). We do this because, as the name of the challenge assumes, "kernel-rop", we will be dealing with ROP chains, just as we would when exploiting userland pwn challenges. we can use the following command to extract the kernel ELF file like so:

Now that we have the kernel ELF file extracted into the name 'vmlinux', the next thing we have to do is to extract all the ROP Gadgets from kernel ELF file. If you're not caught up to speed with what ROP gadgets are then i'll do you a favour and give an overview. ROP gadgets are gadgets located within an ELF file, assembly instructions specifically that end with a RET instruction. ROP gadgets can be used within an exploit payload to setup the stack in such a way to achieve code execution, especially if the non-executable bit is enabled. 

since the Kernel ELF file is quite big, ill execute this command to extract all the ROP Gadgets:



p.s make sure you have ROPGadget installed or even ropper as well. Like i said, the kernel ELF file is a big boy so i'll leave this to run in the background. Could take a good while. 

Now we deal with the compressed initramfs file which contains the linux file system. To have the entire file system exposed to us we have to make use of the decompress.sh script. Let's go into it deeper.




Thes script basically creates a new directory called initramfs, navigates to it, copy the initramfs.cpio.gz in the previous directory into the current working directory, uses gunzip & cpio to extract with gunzip and cpio. Let's quickly run the script.


The result of decompress.sh file. It has extracted the file system and here we see an interesting file called 'hackme.ko'. That is because that is the vulnerable module we are to exploit. 

When we run the decompress.sh script, we do it, not only to have a hold of the vulnerable kernel module to analyze, but also to do a file modification that will be very important. firstly, we will have a look at what exactly this file is.


within the /etc directory in the file system is the inittab file. This file basically tells us that when we finally run the run.sh qemu boot script, it sets the user ID and group ID to 1000 instead of 0. we cannot proceed with debugging the kernel. So we have to change the 1000 to 0 to achieve the capability to boot the kernel in qemu as root to enable debugging and overall supply ease to the exploitation process. Like so:

setuidgid from '1000' to '0'

Once this is done, we just have to use the compress.sh to recompress it to save the configurations before booting it with qemu.


Compressed. 

the run.sh script (qemu)

Now that we have that done, before I round up, let's look at the run.sh script.



some of these flags get all the info we need to get started with the challenge.

-m -> This is showing the memory size
-kernel -> This specifies the compressed kernel image
-initrd -> specifies the local file system.
- the script also contains protective cases like kaslr, smep and smap, which are exploitation mitigations enabled when the image boots up when we finally run the script.

So we've gone through a good number of things to setup the environment to finally get pwning the kernel. Remember, I am no pro at this. i just have a fundamental understanding of userspace pwn challenges that's helped me along the time I've been actively engaging in pwn ctf challenges enough to have got me started. In the next blog post, we'll go into actually running the run.sh qemu shell script, attaching the challenge to gdb as well as doing some reversing and establishing the attack strategy.










Wednesday, January 24, 2024

"write4" x64 exploitation - ROP EMPORIUM [PWNED!!!!]


In this challenge, we will go over quite a more advanced strategy to exploit the binary and leak the flag. 

In the previous ROP Emporium challenges, the flag.txt string was always included within the challenge. We simply had to find where the flag.txt pointer is located in memory and use that for the exploit. In this challenge, the string is not located in the binary, which makes exploitation more harder. The challenge has similar protections as the previous challenges. NX is enabled which equals no shellcoding injections. So we will abuse an arbitrary-write primitive to ultimately leak the flag.

The buffer overflow vulnerability is confirmed by a segmentation fault. additionally, we also confirm the offset from junk byte input to RIP is 40 bytes.

P.S: There are 2 ways of confirming the vulnerability. You can either dynamically analyze the program's behaviour to input like I've done for this write-up or you can go down the route of plugging the binary into a disassembler like IDA or ghidra, analyze the code and establish the presence of the vulnerability that way.

 
 
program crashes at RIP from trying to access an invalid memory address. 

Now that we know the offset, onto the next step. We want to ultimately call print_file@plt with the function parameter as flag.txt so we can leak the flag. But remember the flag.txt string does not exist in the binary, so we need to find a gadget within the binary that we can utilise to write flag.txt to a read-write memory section.

we have to look for a gadget that looks similar to this.

mov [reg], reg
 
A basic understanding of x84_64 intel syntax is necessary to understand this. I'll use ROPGadget tool to scan through the binary and find where a gadget of that syntax is located as it will be useful for crafting the overall ROP chain for the payload.
 


Good. We've now located a gadget with the applied syntax above. The mov qword ptr just shows that is a quadword pointer, which is the address of the readable-writeable memory section, and r15 is the value we want to write to that said section, which is 'flag.txt'. cool.

We do this because at a later stage, the print_file function will have to use the flag.txt as a parameter in order to leak the flag. So for that to happen, the flag,txt has to be written into memory first since we know from the challenge authors that the string is not present in the binary at all. 

Now, we need to actually find a memory section within the binary that has read-write permissions. 
 

 
The .bss section of the binary along with .data section have read-write permissions along with any others. I'll go with the .bss section, .data section or any other with read-write permissions would work. 

Now, we need to find another gadget that is going to pop values from the stack into the r14 and r15 registers respectively. Understanding push and pop stack operations in memory will also be useful to understanding this.

 

Gadget found! Now lastly, we need to fin. the last gadget we will use to have the print_file() function in the binary use the flag.txt written in the .bss section as a parameter. We will have to specifically look for a pop rdi, ret gadget as this gadget will ensure 'flag.txt' is stored in the rdi register, as x86_64 calling convention requires that function parameters are to be stored in the RDI, RSI, RDX and so on. But for this specific exploit, we just store the 'flag.txt' in RDI.


Let's go over the exploit strategy to see how we can exploit the challenge and get the flag.

1. we start by overwriting the buffer and establish the offset from input to RIP is 40 bytes.

2. After we change execution by changing RDI to POP R14; R15; RET

3. We then write the value of R14 in the stack for POP R14 (.bss section memory address)

4. We then write the value of R15, which is flag.txt string in the stack for POP R15.

5. After, execution is then redirected again to MOV QWORD PTR [R14], R15, RET to write the 'flag.txt' string into the .bss section which is readable-writable.

6. Next, POP RDI, to prepare the argument for the print_file() function.
 
7. After the location of flag.txt in bss is written (for RDI)
 
8. Finally, print_file() function with 'flag.txt' in RDI is called to ultimately leak the flag.
 
Final Exploit Script
 
 
 
the script just predefines all the gadgets found and stores them in the relevant variables and appends all the gadgets together to form the final payload. When we run the exploit script, the flag is leaked and challenge is complete.