|
| 1 | ++++ |
| 2 | +author = "Emily Miller" |
| 3 | +title = "How to kernel pwn challenges" |
| 4 | +date = "2025-10-03" |
| 5 | +description = "A crash course in CTF infrastructure for Kernel pwn" |
| 6 | +categories = [ "misc" ] |
| 7 | ++++ |
| 8 | + |
| 9 | + |
| 10 | +## Overview |
| 11 | + |
| 12 | +This guide will |
| 13 | +- Walk through using the [Buildroot](https://buildroot.org/) tool to build a base system image |
| 14 | +- Adding custom components to the stock image (namely custom kernel modules and init scripts) |
| 15 | +- Discuss enabling / disabling specific kernel protections |
| 16 | +- Demonstrate the use of containerization to deploy as a CTF challenge |
| 17 | + |
| 18 | +## Building your image and running it with qemu |
| 19 | + |
| 20 | +To start, go to the the [buildroot download's page](https://buildroot.org/download.html) to download an LTS version of buildroot. From here, you can unzip it with `tar -xvf /path/to/my/file.tar.gz`. |
| 21 | + |
| 22 | +Next, navigate to the local directory with your copy of buildroot. |
| 23 | + |
| 24 | +Buildroot is a tool that allows you to easily build images for a variety of linux compatable devices. It was originally designed to make embedded linux easy to get started with, but also serves as a great tool for kernel pwn challenge creation. |
| 25 | + |
| 26 | +The first step to use the tool is to build out a config file. When getting started, you can use one of the default config files provided. To see a list, you can run `make list-defconfigs`. In our case, we'll run the following to get the default config for `x86-64`: |
| 27 | +```bash |
| 28 | +make qemu_x86_64_defconfig |
| 29 | +``` |
| 30 | +Alternatively, you can run the `make menuconfig` to get a menu for configuration, or `make allnoconfig` to use a completely custom config file. |
| 31 | + |
| 32 | +From here, we can build a copy of linux with: |
| 33 | +```bash |
| 34 | +make -j<max logical core number>` |
| 35 | +``` |
| 36 | +The output will be in `output/images/`. |
| 37 | + |
| 38 | +This output image will, by default, contain a `bzImage` file, which holds the kernel itself, an `rootfs.ext4` file, which holds the root filesystem, and a `start-qemu.sh` script which will boot qemu for you. |
| 39 | + |
| 40 | +Now, when you launch you'll see a login screen. From here, I'd recommend setting up a custom user for the challenge. In this case, you can change the shell for the `root` user to `/bin/false`, and add a new user to `/etc/passwd` and `/etc/shadow`. |
| 41 | + |
| 42 | +## Adding custom components on startup |
| 43 | + |
| 44 | +This can be done with a custom `init` script. In order to pass a custom `init` script, we need a custom `initramfs`. You can either build this into the kernel, or you can build this and run qemu with flags for a custom initramfs. The latter is the approach I'd recommend taking for CTFs, because you'll often want an easy and quick way of updating init scripts, and waiting for a full recompile takes a lot of time. |
| 45 | + |
| 46 | +### Adding your custom kernel module |
| 47 | + |
| 48 | +I'd recommend placing most of your custom code in the same place. So, you'll want to copy your compiled `chall.ko` into something like `/challenge/chall.ko`. |
| 49 | + |
| 50 | +### Writing an init script |
| 51 | + |
| 52 | +Assuming you've kept the default settings, and you have a `rootfs.ext4` file, you can simply edit the file at `/sbin/init` to be your desired script. A template like the following is fairly standard to work with, and note that the script must switch to a long running process or the kernel will kill itself. |
| 53 | +```bash |
| 54 | +#!/bin/sh |
| 55 | +
|
| 56 | +# Insert custom kernel module |
| 57 | +insmod /challenge/chall.ko |
| 58 | +
|
| 59 | +# Drop into busybox init |
| 60 | +exec busybox init |
| 61 | +``` |
| 62 | +
|
| 63 | +### Adding a custom userspace binary |
| 64 | +
|
| 65 | +Notice in the above script that the actual script switches to `/bin/sh` at the final (`setsid`) step. If you wanted, you could replace this with another custom compiled binary in `/challenge`. This would allow you to give a userspace pwn into kernel pwn if you wanted to (for instance, shellcoding into kernel pwn). |
| 66 | +
|
| 67 | +Alternatively, if you wanted to build a jail breakout challenge, this same process could be used to provide kernel isolation. |
| 68 | +
|
| 69 | +## How do I disable \[X\] protection? |
| 70 | +
|
| 71 | +Some protections can be enabled or disabled at runtime (assuming a build with support for it). These include KASLR, SMAP, SMEP, NX, and PTI. These can be disabled by adding flags in qemu. For instance, to disable KASLR, you can use `-append "nokaslr"` (multiple options are delimited with a space), which will disable KASLR at runtime. |
| 72 | +
|
| 73 | +Some protections can only be changed at compile time, namely canaries and SLUB/SLAB hardening. These can be changed by editing the `CONFIG_STACKPROTECTOR`, `CONFIG_SLAB_FREELIST_HARDENED`, etc. options in your `kconfig`. The easiest way to use a custom `kconfig` with buildroot is to use the menu build option (`make menuconfig`). |
| 74 | +
|
| 75 | +## Running with docker! |
| 76 | +
|
| 77 | +Next, we can create a Dockerfile to actually host the challenge, which can be done fairly simply with one like so, |
| 78 | +```Dockerfile |
| 79 | +FROM ubuntu:latest AS bootstrap |
| 80 | +WORKDIR /app/ |
| 81 | +RUN apt-get update && apt-get install -y qemu-system |
| 82 | +COPY ./start-qemu.sh ./run |
| 83 | +COPY rootfs.ext2 ./rootfs.ext2 |
| 84 | +COPY bzImage ./bzImage |
| 85 | +
|
| 86 | +FROM pwn.red/jail:0.4.1@sha256:ee52ad5fd6cfed7fd8ea30b09792a6656045dd015f9bef4edbbfa2c6e672c28c |
| 87 | +COPY --from=bootstrap / /srv |
| 88 | +ENV JAIL_TIME=600 |
| 89 | +ENV JAIL_MEM=384M |
| 90 | +``` |
| 91 | +At this point, depending on your build configurations, it may be necessary to tweak `start-qemu.sh` (for instance, to adjust filesystem mounting options or enabling / disabling runtime kernel protections). |
| 92 | +
|
| 93 | +## More guides and resources |
| 94 | +
|
| 95 | +- Much of my learning was with [smallkirby's guide](https://github.com/smallkirby/kernelpwn). |
| 96 | +- While it can be a lot, [QEMU's official docs are fairly complete for specific questions](https://www.qemu.org/docs/master/) |
| 97 | +
|
0 commit comments