本仓库是对发表在 MISC Magazine #96 上的文章的补充。
我们成功地在启用了 SMEP/SMAP 和 KASLR 的虚拟机上,以可靠的方式提升了权限。但应注意,系统会处于不稳定状态,极有可能发生 oops。
在 configs/ 文件夹中,你可以找到 Linux 内核和 Busybox 的配置文件,每个文件与默认配置略有不同。
binaries/ 文件夹包含所有预构建的二进制文件,你需要它们来复现我们的测试环境,例如一个完整的 rootfs,可直接用于 QEMU。
linux-stable 是一个指向存在漏洞的 Linux 内核版本的 Git 子模块。使用 git submodule update --recursive 来获取它(前提是你的系统有至少 1GB 空闲空间)。busybox 也是如此。
我们的环境基于一台 QEMU x86 虚拟机,通过 9P 协议与宿主机共享一个文件夹。一个存在漏洞的内核版本(ff33952e4d23)与静态链接构建的 Busybox 一起编译。
我们的配置非常简单,因为我们不需要支持任何异类架构或硬件。它通过运行 make defconfig 生成,并启用了一些功能来支持 QEMU 的网络和文件夹共享:
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_MEMORY_BALLOON=y
CONFIG_BALLOON_COMPACTION=y
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_NET_9P_DEBUG=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_BLK_SCSI=y
CONFIG_VIRTIO_NET=y
CONFIG_HVC_DRIVER=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
CONFIG_9P_FS=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
还添加了 GDB 脚本和调试符号,以便于开发最初的 PoC:
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
请注意,在此版本中,KASLR 在 x86 上默认启用。由于每次重启后内核映射到不同的地址,GDB 将无法匹配符号文件。因此你有两个选择:在不开启 KASLR 的情况下进行测试,或者在 GDB 加载符号时通过 symbol-file 传递内核的基地址。
编译后,你可以在 linux-stable/arch/x86/boot/bzImage 中找到 bzImage。
该文件存在于 binaries/bzImage,我们的配置文件在 confifs/kernel.config。使用它只需将其复制为 linux-stable 中的 .config。
我们使用了 Busybox 的最新稳定版本 1.28。唯一需要更改的设置是 CONFIG_STATIC,将其设为 y。编译应该不会出现问题。
我们的配置文件位于 configs/busybox.config,静态编译的二进制文件在 binaries/busybox。和 Linux 一样,只需将其复制为 Busybox 源码文件夹中的 .config。
Busybox 已经实现了一个 init 进程,它会尝试执行 /etc/init.d/rcS。通常,这个脚本由你的发行版提供(可能名称不同),但这里我们必须自己创建!这个脚本将创建并挂载几个必要的系统文件夹(proc、sys、dev)、我们的宿主共享文件夹,并将我们放入一个非特权 shell:
for i in $(seq 1 9); do mknod /dev/tty$i c 4 1; done
mknod -m 0666 /dev/null c 1 3
mknod -m 0660 /dev/ttyS0 c 4 64
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
mkdir -p /mnt/share
mount -t 9p -o trans=virtio share /mnt/share/ -oversion=9p2000.L,posixacl,sync
chmod 777 /mnt/share/
export ENV=/etc/profile
setsid cttyhack setuidgid 1000 sh
umount /proc
umount /sys
umount /dev
poweroff -f
/etc/profile 文件不是必须的,但在测试中很有用,尤其是当漏洞利用不可靠且需要多次尝试才能获得 root 权限时。
漏洞利用编写的过程在 MISC 96 中有详细记录:我们使用 unsafe_put_user 来探测内存,直到找到堆的基地址。然后,通过数千次调用 clone 来在内存中喷洒大量的 cred 结构体。在我们的测试中,它们的内存位置比使用 fork 时更加“恒定”,因为 fork 会为新任务分配更多结构体。
退出子进程会因页面请求错误导致内核 oops,这需要正确处理。
如果你想提高该漏洞利用的可靠性或添加文档,欢迎贡献!我们可能在某些概念上犯了错误或表述不精确,如果你认为有不对的地方,请毫不犹豫地提交 issue。
Linux 内核
漏洞利用