Skip to content

Commit be731f8

Browse files
authored
Update how-to-kernel-pwn.md
1 parent ac97dfd commit be731f8

1 file changed

Lines changed: 90 additions & 0 deletions

File tree

main/content/post/how-to-kernel-pwn.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,27 @@ This can be done with a custom `init` script. In order to pass a custom `init` s
4747

4848
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`.
4949

50+
### Compiling your kernel module
51+
52+
This is a more complex issue, but in short, you can use the Kernel's build system to build your challenge against the Kernel's specific headers. An example `Makefile` can be seen below:
53+
54+
```Makefile
55+
obj-m += challenge.o
56+
57+
# Disables protections for easy challenge
58+
ccflags-y += -fno-stack-protector
59+
ccflags-y += -U_FORTIFY_SOURCE
60+
ccflags-y += -fno-pie
61+
ccflags-y += -no-pie
62+
63+
all:
64+
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
65+
66+
clean:
67+
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
68+
```
69+
70+
5071
### Writing an init script
5172

5273
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.
@@ -90,6 +111,75 @@ ENV JAIL_MEM=384M
90111
```
91112
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).
92113
114+
## Running with docker (but unhinged)!
115+
116+
To take this a step further, we can completely automate the build process, so that the entire thing is done in Docker. An example of this can be seen below:
117+
118+
```Dockerfile
119+
FROM debian:unstable@sha256:cc1675ddb1073d19ba9ef6fe9b9c625eceb02fccb9c0f7afbb4e60f16325c91d AS fs_builder
120+
RUN apt-get update && apt-get install -y which sed make binutils \
121+
build-essential diffutils gcc g++ bash patch gzip bzip2 perl \
122+
tar cpio unzip rsync file bc findutils gawk wget git
123+
RUN git clone https://github.com/buildroot/buildroot.git /tmp/buildroot
124+
COPY .config /tmp/buildroot/.config
125+
RUN --mount=type=cache,target=/tmp/buildroot/dl/,id=buildroot_dl \
126+
cd /tmp/buildroot && make -j8 && \
127+
mkdir -p /tmp/artifacts && \
128+
cp -r /tmp/buildroot/output/build/linux-* /tmp/artifacts/ && \
129+
cp /tmp/buildroot/output/images/rootfs.ext2 /tmp/artifacts/ && \
130+
cp /tmp/buildroot/output/images/bzImage /tmp/artifacts/ && \
131+
cp /tmp/buildroot/output/images/start-qemu.sh /tmp/artifacts/
132+
133+
# Build the kernel module against the exact kernel buildroot produced
134+
FROM debian:unstable@sha256:cc1675ddb1073d19ba9ef6fe9b9c625eceb02fccb9c0f7afbb4e60f16325c91d AS mod_builder
135+
RUN apt-get update && apt-get install -y build-essential libelf-dev
136+
COPY --from=fs_builder /tmp/artifacts/linux-* /tmp/linux/
137+
COPY module/* /tmp/module/
138+
RUN cd /tmp/module && make KERNELDIR=/tmp/linux
139+
140+
FROM debian:unstable@sha256:cc1675ddb1073d19ba9ef6fe9b9c625eceb02fccb9c0f7afbb4e60f16325c91d AS fs_patcher
141+
RUN apt-get update && apt-get install -y e2tools
142+
COPY --from=fs_builder /tmp/artifacts/rootfs.ext2 /tmp/rootfs.ext2
143+
COPY init /tmp/init
144+
COPY --from=mod_builder /tmp/module/*.ko /tmp/module.ko
145+
RUN e2rm /tmp/rootfs.ext2:/sbin/init
146+
RUN e2cp -p /tmp/init /tmp/rootfs.ext2:/sbin/
147+
RUN e2cp -p /tmp/module.ko /tmp/rootfs.ext2:/root/
148+
RUN e2cp /tmp/rootfs.ext2:/etc/passwd /tmp/passwd
149+
RUN echo 'ctf:x:1000:1000::/home/ctf:/bin/sh' >> /tmp/passwd
150+
RUN e2cp -p /tmp/passwd /tmp/rootfs.ext2:/etc/passwd
151+
RUN e2cp -p /tmp/rootfs.ext2:/etc/group /tmp/group
152+
RUN echo 'ctf:x:1000:' >> /tmp/group
153+
RUN e2cp -p /tmp/group /tmp/rootfs.ext2:/etc/group
154+
RUN e2mkdir /tmp/rootfs.ext2:/home/ctf
155+
156+
FROM debian:unstable@sha256:cc1675ddb1073d19ba9ef6fe9b9c625eceb02fccb9c0f7afbb4e60f16325c91d
157+
RUN apt-get update && apt-get install -y --no-install-recommends qemu-system-x86 socat e2tools && rm -rf /var/lib/apt/lists/*
158+
WORKDIR /app
159+
COPY --from=fs_builder /tmp/artifacts/bzImage ./
160+
COPY --from=fs_builder /tmp/artifacts/start-qemu.sh ./
161+
COPY --from=fs_patcher /tmp/rootfs.ext2 ./
162+
RUN sed -i "s/console=tty1/rw nokaslr/" start-qemu.sh
163+
RUN sed -i "s/-drive file=rootfs.ext2,if=virtio,format=raw/-drive file=rootfs.ext2,if=virtio,format=raw -snapshot/" start-qemu.sh
164+
COPY entrypoint.sh ./
165+
ENV FLAG=PWNED{FAKE_FLAG}
166+
RUN echo "PWNED{FAKE_FLAG}" > /tmp/flag.txt
167+
EXPOSE 5000
168+
CMD ["/app/entrypoint.sh"]
169+
```
170+
171+
You'll also need to populate `entrypoint.sh`, `init`, `.config`, `module/challenge.c`, and `module/Makefile`. In this case, my entrypoint looks something like the following:
172+
173+
```sh
174+
#!/bin/sh
175+
# Inject flag (mounted by scenario) into rootfs
176+
e2cp /tmp/flag.txt /app/rootfs.ext2:/root/flag.txt
177+
rm /tmp/flag.txt
178+
179+
# Each connection gets its own QEMU with -snapshot (reads base image, writes to tmpfile)
180+
exec socat TCP-LISTEN:5000,reuseaddr,fork EXEC:"/app/start-qemu.sh --serial-only",stderr
181+
```
182+
93183
## More guides and resources
94184

95185
- Much of my learning was with [smallkirby's guide](https://github.com/smallkirby/kernelpwn).

0 commit comments

Comments
 (0)