由于个人需求,需要入门Kernel Pwn。所以又继续开始更新这个系列啦!
我的入门参考了Keith Makan的Kernel Pwn系列文章:
– [Linux Kernel Exploitation 0x0] Debugging the Kernel with QEMU
– [Linux Kernel Exploitation 0x1] Smashing Stack Overflows in the Kernel
– [Linux Kernel Exploitation 0x2] Controlling RIP and Escalating privileges via Stack Overflow
但我只能说,这系列文章的写作质量非常感人。第一篇文章写的还是比较优秀的,后面作者就开始放飞自我了。在一些关键的地方会出现不能看的错误。因此我自己再写一篇踩坑教程。
环境配置
我们首先选择一个工作目录:
export WORKSPACE=~/Documents/Kernel
cd $WORKSPACE
构建内核
根据教程文章,选择Ubuntu 18.04 LTS上进行内核编译。本文作者写作时,Ubuntu 18.04的最新版本应该是18.04.6。
首先安装依赖:
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install -y git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison qemu-system-x86
根据教程,用的是5.9.7的内核:
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.9.7.tar.xz
tar -xf linux-5.9.7.tar.xz
cd linux-5.9.7
将宿主机的内核.config
直接复制过来,作为当前的内核编译.config
。作者写作时,Ubuntu 18.04 LTS的最新内核版本是5.4.0-150-generic
,后续随系统更新可能有变动。
cp /boot/config-5.4.0-150-generic .config
然后再开启kernel对kvm虚拟化的支持,以便于在qemu中运行时更好的调试:
make kvmconfig
然后手动更改.config
,开启调试信息、特定文件系统,关闭保护机制和启用网卡支持,更改如下:
CONFIG_DEBUG_INFO=y
CONFIG_BINFMT_MISC=y
# CONFIG_RANDOMIZE_BASE is not set
# CONFIG_STACKPROTECTOR is not set
# CONFIG_FORTIFY_SOURCE is not set
CONFIG_SYSTEM_TRUSTED_KEYS=""
CONFIG_E100=y
CONFIG_E1000=y
CONFIG_E1000E=y
其中,保护机制关闭了地址随机化,Stack Canary和FORTIFY_SOURCE。
最后,保存.config
并编译:
make savedefconfig
make -j16
构建启动镜像(rootfs)
我们使用syzkaller中的镜像构建脚本:
cd $WORKSPACE
mkdir image
cd image
wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh
chmod +x create-image.sh
./create-image.sh
如果创建成功,当前目录下应该会有一个bullseye.img
。
启动虚拟机并附加调试器
接下来,我们创建run.sh
,内容如下:
#!/bin/bash
sudo qemu-system-x86_64 \
-kernel ../linux-5.9.7/arch/x86_64/boot/bzImage \
-append "console=ttyS0 root=/dev/sda earlyprintk=serial nokaslr net.ifnames=0"\
-hda ./bullseye.img \
-net user,host=10.0.2.10,hostfwd=tcp::10022-:22 -net nic,model=e1000 \
-enable-kvm \
-nographic \
-m 2G \
-s \
-S \
-smp 2 \
-pidfile vm.pid \
2>&1 | tee vm.log
其中,参数-s
代表-gdb tcp::1234
。表示gdb可以从1234端口对内核进行附加调试。-net user
一行创建了网卡,并使得guest的22端口转发到了host的10022端口。这使得我们可以使用SSH。
在image目录下启动qemu,运行guest:
./run.sh
新开一个窗口,启动gdb并连接调试端口:
cd $WORKSPACE/linux-5.9.7
gdb vmlinux
> (gdb) target remote :1234
然后在gdb cli中使用continue
语句,即可使得qemu继续运行。
镜像的默认用户名为root,无密码。
编译有漏洞的内核驱动与使用驱动的App
在host执行下列操作:
cd $WORKSPACE/linux-5.9.7
mkdir debug_driver
cd debug_driver
wget https://raw.githubusercontent.com/bjrjk/pwn-learning/main/ROP/KROP_LPE/Makefile
wget https://raw.githubusercontent.com/bjrjk/pwn-learning/main/ROP/KROP_LPE/stacksmash_driver.c
cd ..
make -C . M=drivers/debug_driver/
将stacksmash_driver.ko
通过ssh上传到guest,然后再使用如下命令,在guest中下载stacksmash_app.c
并进行编译。这是使用存在漏洞的内核驱动的用户态程序。
wget https://raw.githubusercontent.com/bjrjk/pwn-learning/main/ROP/KROP_LPE/stacksmash_app.c
gcc stacksmash_app.c -o stacksmash_app
启用内核驱动并在gdb中加载符号
在guest中启用内核模块,并获得内核模块的基地址,以在调试器中加载符号:
insmod stacksmash_driver.ko
cat /proc/modules
> stacksmash_driver 16384 0 - Live 0xffffffffa006b000 (OE)
从中可以看出,0xffffffffa006b000
是内核驱动的基地址。在调试器中,执行如下命令,以加载stacksmash_driver.ko
的符号:
add-symbol-file drivers/debug_driver/stacksmash_driver.ko 0xffffffffa006b000
然后就可以下断点了。例如,给stacksmash_dev_write
函数下断点:
b stacksmash_dev_write
此时,一旦在guest中执行调用stacksmash_driver
内核驱动的stacksmash_app
用户态程序,进程控制流就会到达内核态的stacksmash_dev_write
函数。此时,qemu将会在断点断下,并通知gdb,等待用户的进一步操作。