Kernel PWN 环境搭建

Posted on Jun 17, 2021

一直对 kernel pwn 挺感兴趣的,再加上 libc 那里也没什么新东西可学了,简单题没什么做的意义,难题也不会做,所以准备开始学习 kernel pwn。不过可能最近也没办法太深入的学习,总之先把环境搭起来再说。

首先说一下这里的环境到底指的是什么,我们希望能够有一个沙盒环境,可以做到

  • 启动速度快。kernel pwn 的过程中很容易造成 kernel panic,如果每次启动要几十秒,体验肯定是很差的
  • 可以用 gdb 之类的调试器调试。脑调是很痛苦的
  • 可复用。每次启动环境的时候初始状态都应该相同,即便上一次把内核搞炸了下一次重启应该也可以解决全部问题

满足上述要求的解决方案就是 qemu + gdb 的调试方法。

以一台全新的 Ubuntu 20.04 LTS Desktop 虚拟机为例,虚拟机的安装使用 VMware 快速安装模式安装。

首先运行一个 kernel 的环境需要有一个 kernel 的镜像,镜像的获取手段有

  • 手动编译一个内核出来
  • 直接从软件源里面下
  • 直接用本机的镜像

从软件源下载

首先搜索镜像,下面这句话会把源里有的所有镜像都显示出来,可以配合 grep 来查找特定版本的镜像

sudo apt search linux-image

找出镜像后就可以下载,这里以 linux-image-5.8.0-23-generic 为例

sudo apt download linux-image-5.8.0-23-generic

下载下来后解压,在文件夹 data/boot/ 中的以 vmlinux 开头的即为镜像文件

使用本机镜像

/boot 文件夹中即可找到

内核镜像编译

使用前两种方法比较快速,但是往往没有符号,为了调试方便,还是需要手动编译。

首先安装一些依赖

sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc libelf-dev flex bison

然后下载 kernel 源码,在官网可以下载,我下载的是 5.8 版本(版本应该可以随便选,我选择和系统版本一致)。

解压后先进行编译设置

make menuconfig

进入内核编译设置,进入 kernel hacking 子目录,检查一下 debug 相关的选项有没有打开(其实默认都是打开的,进去看看确定一下就可以了)。

然后编译安装

make bzImage -j$(nproc)

-j$(nproc) 是使用所有的核心编译,可以加快编译速度。

编译的过程中可能会碰到报错 *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'. Stop.,对于该错误只要注释掉编译目录下的 .config 中的CONFIG_SYSTEM_TRUSTED_KEYS="debian/certs/benh@debian.org.cert.pem" 一行即可。

我两次编译安装过程中都只碰到了这一个问题,如果出现别的问题通过搜索引擎应该也都可以解决。

编译出来的 bzImage 是不带符号的压缩映像,如果想要源码级调试,需要编译 vmlinux,也就是

make vmlinux -j$(nproc)

vmlinux 就是带符号的。

qemu 安装

qemu 可以模拟各个架构,基本是唯一的选择了。图省事的话可以直接用包管理器安装

sudo apt install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev
sudo apt install qemu-user

安装好后敲下 qemu 之后按 tab 有大量回显就算安装好了(可能需要重启终端)。

但是就我在调 ARM 过程中使用包管理安装的 qemu 时不怎么美好的经历,也建议自己手动编译安装一个最新版,可以参考这篇文章

busybox 安装

busybox 提供了一个 Linux 虚拟环境需要的大量程序,如 ls,cat 等。

首先到 官网 中下载,下下来之后解压,进行配置

make menuconfig

注意勾选 Settings —> Build static binary file (no shared lib),避免之后还要配置 lib。

然后

make -j$(nproc)
make install

既可,默认是安装在当前目录下的 _install 文件夹中的,把这个文件夹移到你想移的位置即可,为了方便,我直接放在了准备运行 qemu 的目录下。

然后进入 _install 文件夹中,先建立一些必要的文件夹

mkdir sys
mkdir proc

然后写一下 init 脚本

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 1000 /bin/sh #normal user
umount /proc
umount /sys
poweroff -d 0  -f

给予可执行权限

chmod +x ./init

然后把这里的文件都打包

find . | cpio -o --format=newc > ../rootfs.cpio

到上层文件夹中就会有 rootfs.cpio 文件了,然后把 bzImage 也放到这里,就可以启动 qemu 了。由于参数较多,所以写成一个 shell 脚本来启动

qemu-system-x86_64 \
    -m 128M \
    -kernel bzImage \
    -initrd rootfs.cpio \
    -append 'root=/dev/ram console=ttyS0 oops=panic panic=1' \
    -monitor /dev/null \
    -cpu kvm64,+smep \
    -smp cores=1,threads=1 \
    -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
    -nographic

执行脚本后就会调用 qemu 来启动虚拟机

这里会报错,我也不知道是什么原因,但是好像没什么影响,所以暂且不管了。

可见,有 boot.sh bzImage rootfs.cpio 即可启动一个虚拟内核环境,所以大多数的题目都会给出这几个文件。

对于 rootfs.cpio 的解压可以通过

cpio -idmv < rootfs.cpio

这会把 rootfs.cpio 中的文件都解压到当前目录中。