Befor diving in kernel hacking we must understand
It’s the module which is launched in the kernel, necessarly vulnerable if it’s a challenge
As a module it exposed differents endpoints with are trigerrable through IOCTL’s. It’s like syscalls, just specific to a driver.
Or hooked function that we can trigger through normal system working
ffffffffc03162c8 b ref_sys_getdents64 [ecsc]
ffffffffc03162d0 b ref_sys_getdents [ecsc]
ffffffffc03162c0 b ref_sys_lstat [ecsc]
ffffffffc03162e0 b my_sys_call_table [ecsc]
ffffffffc03162d8 b original_cr0 [ecsc]
ffffffffc031436e t ecsc_end [ecsc]
ffffffffc0316000 d __this_module [ecsc]
ffffffffc0314150 t ecsc_sys_getdents [ecsc]
ffffffffc031436e t cleanup_module [ecsc]
ffffffffc03142a0 t ecsc_sys_lstat [ecsc]
ffffffffc0314000 t ecsc_sys_getdents64 [ecsc]
c8821000 t tostring_close [basic1_ch1]
c8821020 t tostring_open [basic1_ch1]
c8821040 t tostring_read [basic1_ch1]
c8821080 t tostring_read_dec [basic1_ch1]
c88210e0 t tostring_read_hexa [basic1_ch1]
c8821140 t tostring_write [basic1_ch1]
c88211fc t tostring_exit [basic1_ch1]
c88211fc t cleanup_module [basic1_ch1]
This is the kernel that we will work with, generally compressed (and then extractable ;))
└─$ file bzImage
bzImage: Linux kernel x86 boot executable, bzImage, version 4.14.167 (root@vps783610) #11 Fri Feb 14 16:47:58 CET 2020, RO-rootFS, Normal VGA, setup size 512*30, syssize 0x2821d, jump 0x268 0x8cd88ec0fc8cd239 instruction, protocol 2.13, from protected-mode code at offset 0x276 0x2540eb bytes gzip compressed, relocatable, legacy 64-bit entry point, can be above 4G, max cmdline size 2047, init_size 0xd89000
The iniramfs is generaly a cpio archive, this is the base file system that will be used by the kernel
└─$ file initramfs.example.cpio
initramfs.example.cpio: ASCII cpio archive (SVR4 with no CRC)
The cpio archive (initramfs) can be extracted in order to retrieve the file system that will be used
cpio -idv < ./initramfs
rootf
├── bin
│ ├── [
│ ├── [[
│ ├── acpid
│ ├── add-shell
...
│ ├── zcat
│ └── zcip
├── etc
│ ├── group
│ └── passwd
├── home
│ └── ctf
├── init
└── lib
└── modules
└── 4.14.167
└── ecsc.ko
The most important file is the ‘init’ one, this is the first userland executable that will be run by the kernel. Some interesting information about how to interact with the VM that we will launch can be found in.
We finally launch the kernel via qemu.
qemu-system-x86_64 \
-m 128M \
-kernel bzImage \
-initrd initramfs.example.cpio \
-append "console=ttyS0 panic=1 oops=panic quiet" \
-nographic
As that we can’t really interract or debug the kernel and the vulnerable driver. We need to have the vmlinux files
There is four available kernel hacking challenge on the hackropole platform: https://hackropole.fr/fr/challenges/pwn
Let’s begin with the easiest one:
As we deal with code already runinng in kernel land, we won’t interract with it in the same way as in userland pwn.
In userland we generally want to abuse a program in order to get shell, and we can commmunication with this programme through userland entries en classics IPC.
Here the ‘program’ that we wan’t attack is the kernel so we would also have IPC but not classic stdin(out/err).
We will have to write C programs that can open drivers (/dev/xxx) or trigger syscalls.
Vulns seems to be the same as userland, memory errors that leads to code redirection (stack/heap/and maybe other things).
Depending on what the modules do the goal can be multiples,
Just a docker image is provided for this challenge:
└─$ cat docker-compose.yml
services:
pepin:
image: anssi/fcsc2020-pwn-pepin:latest
ports:
- "2222:22"
That we launch with
└─$ docker compose up
└─$ ssh -p 2222 ctf@localhost (password: ctf)
WE have all the files mentionned before :
ctf@bb5418c1b1fe:~$ ls -la
total 3656
dr-xr-xr-x 1 ctf-admin ctf-admin 4096 Nov 29 2023 .
drwxr-xr-x 1 ctf-admin ctf 4096 Nov 29 2023 ..
-rwxr-xr-x 1 ctf-admin ctf-admin 1312 Oct 25 2023 .start.sh
-rw------- 1 ctf-admin ctf-admin 2678688 Oct 25 2023 bzImage
-rw------- 1 ctf-admin ctf-admin 1026048 Oct 25 2023 initramfs.cpio
-rwsr-x--- 1 ctf-admin ctf 16608 Nov 29 2023 wrapper
-rw-r--r-- 1 ctf-admin ctf 126 Oct 25 2023 wrapper.c
We can’t extract the bzImage and initramfs on our host… but i hate working in docker
It’s possible to refind them in root in the docker overlay:
┌──(root㉿kali)-[/var/…/e425d2c556db5b9042f348ad2aff0396d2e5ce3edc3b6ec113058400ae2f2201/diff/home/ctf]
└─# ls
bzImage initramfs.cpio wrapper wrapper.c
┌──(root㉿kali)-[/var/…/e425d2c556db5b9042f348ad2aff0396d2e5ce3edc3b6ec113058400ae2f2201/diff/home/ctf]
└─# pwd
/var/lib/docker/overlay2/e425d2c556db5b9042f348ad2aff0396d2e5ce3edc3b6ec113058400ae2f2201/diff/home/ctf
We need to briefly modify the start.sh in order to correspond to our filesystem
Let’s also extract the initramfs.cpio in order to see the init file
└─$ cat init
#!/bin/sh
export PATH=/bin
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
[ -d /run ] || mkdir /run
[ -d /root ] || mkdir /root
[ -d /etc ] || mkdir /etc
[ -d /home ] || mkdir /home
[ -d /mnt ] || mkdir /mnt
chmod 400 init
chmod -r init
chown -R root:root /
chmod 700 -R /root
chown ctf:ctf /home/ctf
chmod 777 /home/ctf
chmod 755 /dev
mkdir -p /mnt/share
mount -t 9p -o trans=virtio ecsc /mnt/share/ -oversion=9p2000.L,posix,msize=104857600,sync
chmod 777 /mnt/share/
mkdir -p /var/lock
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc
ln -sf /proc/mounts /etc/mtab
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mkdir -p /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run
echo 0 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/perf_event_paranoid
echo 2 > /proc/sys/kernel/randomize_va_space
cat <<EOF
_______ _______ _______ _______ ___ _ _______ ______ __ _ _______ ___
| || || || | | | | || || _ | | | | || || |
| ___|| || _____|| | | |_| || ___|| | || | |_| || ___|| |
| |___ | || |_____ | | | _|| |___ | |_||_ | || |___ | |
| ___|| _||_____ || _| | |_ | ___|| __ || _ || ___|| |___
| | | |_ _____| || |_ | _ || |___ | | | || | | || |___ | |
|___| |_______||_______||_______| |___| |_||_______||___| |_||_| |__||_______||_______|
EOF
setsid cttyhack setuidgid 1000 /bin/sh
umount /proc
umount /sys
poweroff -d 0 -f 2>&1 >/dev/null
It doesn’t seems to have kernel module, let’s try to find a interesting syscall:
/ $ cat /proc/kallsyms | grep -iE 'ecsc|fcsc|flag'
...
ffffffffb8d11e92 T sys_ecsc_getflag
...
There is an interesting syscall but we can’t know what number trigger it Maybe we can brute force it but let’s read the challenge description:
Ok, easy win, just call the 333 syscall and watch in dmesg
.text
.globl _start
_start:
mov $333, %rax
syscall
mov $0, %rdi
mov $0x3c, %rax
syscall
└─$ as sc.S -o sc.o
└─$ ld sc.o -o sc
└─$ cp sc /tmp/tmp.gOuSa0ILmL
in the vm we can launch this program
/ $ /mnt/share/sc
/ $ echo $?
0
nothing append but if we watch the kernel logs, the flag appears
dmesg
...
FCSC{REDACTED}