Skip to content

Commit 0c1293d

Browse files
author
emily747
committed
added kernel pwn
1 parent 4d2527e commit 0c1293d

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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

Comments
 (0)