BackdoorCTF
Kuwu🥇

Description
A simple little kernel challenge
Note: No brute-force is required for this challenge
nc 34.42.147.172 4005
Author: p0ch1ta
Analysis
we're given the following files:
inside the filesystem, we can find the challenge kernel module, the module is really small and consist only of ioctl, roughly as follows
in essence, we can allocate a chunk once of page size and then free that chunk as we wish throughout the exploit since the pointer is not nullified.
with this we have an UAF with somewhat arbitrary free of a single chunk
below is the qemu run script
Exploitation
overview
first, lets initialize the UAF
then we can we can spray msg_msgto recycle the chunk
as shown below, our global_chunknow contains:

now at this point, there's some things to note:
the flag is not stored in the filesystem, our goal is not to gain LPE
since the flag is stored in memory, we need an arbitrary read primitive
to gain arbitrary read primitive, we can utilize UAF on msg_msgto corrupt its content to be the following
however with msg_msg we only have partial overwrite at offset +0x30 and as such we're not able to edit its headers.
to gain more partial overwrite, we can utilize msg_segment which able to control the data at offset +0x8.
to do this, we need to free it once more, and spray it with msg of size bigger than a page
and as we can see below, the chunk now filled with the segment section.

with this, we're able to corrupt the msg_msgheaders and craft our own msg_msg .
however this is not enough to immidiately read the flag, since kaslr is enabled we need some sort of leak, but without copy_to_user() how are we able to gain leaks?
Failed Leak Attempt
before moving on to the leak method that I use, I wanted to discuss some of the failed attempts to better understand the constraint that we have.
first, some diagram to understand what we currently have:

my first thought is that since we have arbitrary free on the red chunk, we can free it and allocate another object on it that potentially has kernel addresses.
if that's possible we can then peek at said msg_msg and we'll also read the msg_segment region thus leak the addresses.
however this comes with certain constraints:
the object has to be size of kmalloc-4k
the first 8 byte of the object has to be null, or valid address if the segment is more than 2 pages (essentially forming a valid linked list)
I tried msg_msg since it has heap addresses on it, however the first 8 byte is not null and the kernel panics when I tried to peek from it.
I also tried to construct the msg_segment to be more than 2 pages such that *next is linked suitable for msg_msg as its first 8 byte is an address, however this also results in kernel panic.
I then tried to look for other kernel objects that satisfy the constraints but found nothing.
next, I tried to free the segment before peeking to leak the freelist pointer, but hardened freelist is enabled (I didn't read the compile config lol)
Cross Cache Leak
the idea of this came from a blog that my teammate shares

I suggest you to read the whole blog, but the gist of it that is relevant to the current exploit is the following:
so with the poll syscall, we're able to allocate user controlled sized kernel chunk. with 30 of its entries stored in the stack and the remaining in the said kernel allocated chunk with structure similar to that of msg_segment with the first 8 byte is a pointer forming a singly linked list.
to allocate to its maximum size of 1 page, 510 entries is needed, any remaining entries will be allocated more and forming a singly linked list. for example to allocated a kmalloc-4k and kmalloc-0x20 chunk we can poll with 542 (30 + 510 + 2) entries.
another thing is that, the allocated chunk will be freed once it timeouts.
with this in mind, my idea is to construct a fake msg_segment with poll_list that links to a 0x20 cache, poll_list will then be freed upon timeout, we can then spray objects of size 0x20 that contains addresses to leak, since the pointer to said 0x20 chunk is still intact in the fake msg_segment we can peek at it, thus reading an active 0x20 chunk.
lets see this in action, first construct a two allocated msg_segment as follow:

free the first segment, and then spray poll_list to reused the chunk, we expected to have as below:

then we spray objects size 0x20, we have options such as seq_operations and shm_file_data, but seq_operations wont work because of the constraint I mentioned earlier, the first 8 byte must be null.
shm_file_data is also convenient as it has both kernel address and heap address, compared to seq_operations that only has kernel address.
which I also referenced from this writeup
so we spray shm_file_data and now our chunks will results in the following:

this can also be verified in gdb:

and thus as we peek from it, leaks are viable.
Final steps
with the address leaked, we can now perform arbitrary read using corrupted msg_msgdiscussed in overview, but its turns out that the flag is stored in the modules addresses which is not continous to kernel .text

so before reading the flag, we need to leak the kernel module's base address, I just thought the mapping must be stored somewhere in kernel .text and it surely is

one last thing, as we perform arbitrary read, the msg_msg will cast the address we're reading to msg_segment this means, we also need to satisfy the first 8 byte to be null constraint to avoid kernel panic (considering the msg segment size is only 1 page).
so with arbitrary read, we will be reading slightly behind the target address that contains a null

here's the exploit being ran againts the remote server

here's the full exploit script:
Flag: flag{4_m5g_4_d4y_w1ll_g1v3_y0u_th3_l34k5_r1ght_4w4y}
Last updated


