2018 0CTF Finals Baby Kernel
Environment
- ubuntu 20.04
- qemu 7.0.0
- kernel version 4.15.0-22-generic SMP mod_unload
sjy@ubuntu:~/ctf/2018-0ctf-final-baby$ strings baby.ko | grep vermagic= vermagic=4.15.0-22-generic SMP mod_unload
- qemu stratup script
qemu-system-x86_64 \ -m 256M -smp 2,cores=2,threads=1 \ -kernel ./vmlinuz-4.15.0-22-generic \ -initrd ./core.cpio \ -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet" \ -cpu qemu64 \ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ -nographic -enable-kvm
-kernel bzImage
Use bzImage as kernel image. The kernel can be either a Linux kernel or in multiboot format.-initrd file
Use file as initial ram disk.
Problem Analysis
- Open baby.ko(ELF) with IDA64.
- Flag
The flag is hard-coded by the driver in its data segment and the length of the flag is 33.
baby_iotctl()
There are two command numbers.- 0x6666
Print the flag’s address in the kernel address space. - 0x1337
Perform four checks on our input and print the flag if the input passes all the checks. Four checks respectively lie in line 16, line 17, line 21 and line 25.
__readgsqword()
reads memory from a location specified by an offset relative to the beginning of the GS segment.
- 0x6666
_chk_range_not_ok()
While__CFADD__()
represens the carry flag of addition(x+y),_chk_range_not_ok()
returns 1 only if a1+a2>a3 or a1+a2 generates an arithmetic carry.
Through dynamic debugging, it is found that the third parameter is always0x00007ffffffff000
, which is the boundary of user space in the 64-bit system.- All the checks
[v2,v2+16]
is in user space.[v5,v5+(signed int*)(v5+8)]
is in user space. (v5=v2
)(dword*)(v5+8)
equals tostrlen(flag)
。(byte*)((qword*)(v5)+i)
equals toflag[i]
while0<=i<strlen(flag)
.
Therefore, it is supposed that
v2
points to a struct(defined asData
), with two 8-bytes members.typedef struct { char* addr; size_t len; } Data;
In conclusion,
Data v2
is a user space variable,(char*)(v2->addr)
equals to(char*)flag
and(v2->len)
equals tostrlen(flag)
.
Double Fetch vulnerability
- The double fetch vulnerability is a specific type of time-of-check to time-of-use (TOCTOU) bug, generally occurring in shared memory interfaces. For example, when a kernel function reads a user data twice, the first read is to perform some security checks on the data or establish a connection and the second is for the real use. Between this two kernel read operations, a malicious thread in user mode can tamper with that data, thus causing inconsistencies in the data used by the kernel.
- For this problem, if we do not know the content of the real flag, by no means can we create the variable
Data v2
to pass all the checks. However, while the kernel function reads the user space address twice to check and use respectively, we could create a malicious thread that tampers withv2->addr
to the real kernel address of the flag afterv2
passes the first three checks. Now,v2
could pass the fourth check andiotcl()
will print the content of flag. Since our attack requires multiprocessing, the qemu startup script contains something like-smp 2
.
Exploit
- Source code: https://ctf-wiki.org/pwn/linux/kernel-mode/exploitation/double-fetch/#2018-0ctf-finals-baby-kernel
I changeTRYTIME
from0x1000
to0x10000
to increase the success rate of an exploit.