First step to linux kernel pwn
First step to linux kernel pwn
将根据笔者学习进度逐步更新,作为记录和总结。
update:这段时间在搞其他的,可能会鸽一阵
前置知识
linux kernel binary
linux内核也是一个程序,其二进制文件通常以两种形式呈现:vmlinux和bzImage。使用file命令可以快捷地区分:
|
|
|
|
vmlinux
普通ELF文件,也是内核源码编译出来的原始文件。在分析时可以直接载入gdb获取符号信息、函数地址信息等等。
bzImage
vmlinux原始文件经过objcopy和再次链接等步骤,最终进行压缩得到的文件。这里b
是big的首字母,所以其实也有zImage,适用于小内核,bzImage即大内核。
在分析时无法直接使用,需要先解压提取出vmlinux,这里可以使用shell脚本extract-linux,缺点是丢失了符号信息。更好的替代品是vmlinux-to-elf,大多数情况下都可以完美还原符号信息。
另外在/boot
目录下,还能找到一个vmlinuz-*
文件,实际是bzImage格式,例如在笔者机器上如下:
|
|
qemu基本使用
qemu是一个通用开源模拟器和虚拟机软件,通俗来说,和通常运行虚拟机所用的vmware、virtualbox是差不多的东西。qemu的突出特点就是在不需要图形界面场景中的方便易用,且可以纯软件模拟执行不同CPU架构的指令代码。在kernel pwn题目中,基本都会使用qemu加载特定内核部署题目环境。
user mod & system mod
qemu的使用通常有两种模式,即user mode和system mode。
kernel pwn题目中一般使用system mode,会模拟整个操作系统,使用的命令一般是qemu-system-x86_64
,即x64架构的qemu虚拟机。其他架构的还有qemu-system-mips、qemu-system-riscv64等等。
user mode不会模拟整个操作系统,只会进行相应指令集的指令模拟,所以一般被用来执行二进制程序。比如下面是一个在x86机器上运行riscv程序的例子:
|
|
qemu启动脚本
一个典型的启动脚本如下:
|
|
-initrd:指定init ram disk,一般就是文件系统
-kernel:指定内核
-append:指定内核命令行参数,需要关注启用的内核保护,比如这里只启用了kaslr保护
-enable-kvm:使用kvm虚拟化
-monitor /dev/null:把monitor重定向到/dev/null
-m 128M:分配128M内存
–nographic:不使用图形界面
-smp cores=1, threads=1:设置CPU为单核单线程
-cpu kvm64, +smep:使用kvm64虚拟化cpu,开启smep保护
-s:开启gdb server,默认监听1234端口,方便调试
文件系统
qemu虚拟机加载了内核,还需要文件系统才能实现完整的操作系统功能。linux通常有两类使用文件系统的方式。
initrd & hda
一种是init ram disk(initrd),实际本身是用于在真正的硬盘文件系统挂载前临时使用的,作为内核引导过程的一部分,所有文件内容都会被放入内存,在qemu中可以使用-initrd参数指定。另一种方式则是挂载一个硬盘/软盘等存储设备,使用qemu模拟时使用-hda参数指定模拟一个硬盘设备作为文件系统。
cpio archive
cpio是一种压缩文件格式,常用来作文件备份,也可以拿来打包文件系统,使用cpio相关命令即可方便地打包/解包。cpio文件一般是-initrd的参数
常用打包解包命令:
|
|
ext4 image
ext4是一种文件系统格式,目前已被linux系统广泛应用。使用ext4更像是使用一个磁盘,可以使用mount/umount命令方便地挂载/取消挂载,向其中添加/更改文件。ext4 image一般是-hda的参数。
可装载内核模块
可装载内核模块即Loadable Kernel Module(LKM),一般简称为内核模块,能够运行在内核态为用户态提供服务。模块是可插拔的,使用insmod、rmmod、lsmod可以增、删、查询模块。在未开启kptr_restrict保护时,lsmod可以查看到模块的内存加载地址,方便调试时手动指定基址。
在kernel pwn题目中,通常都会提供一个有漏洞的LKM,用户程序通过i/o设备或者procfs与LKM通信,攻击者构造恶意数据触发漏洞,从而达到提权或者其他恶意目的。
分析LKM时,应首先定位init_module
和cleanup_module
函数,二者是LKM的入口函数和退出函数,相当于类的构造和析构函数。
I/O设备
linux哲学强调一切皆文件,i/o设备也不例外。使用i/o设备的通信,简单说只是对i/o设备文件使用open、read、write三个系统调用完成的, 这些i/o设备文件都放在/dev下。
开发者可以创建自己的io设备,同时对于新创建的设备,实现对应的打开、读、写、关闭等回调函数,预定义的这些函数不够用时,linux还设计了ioctl系统调用,可以针对自定义的行为定义回调函数,以ioctl的调用号做区分,实现高定制性的功能。
要实现回调的能力,势必需要有个结构体存储各种回调函数指针,这里就使用了file_operations
这个或类似的结构体。这个逻辑就跟用户态FILE结构体中的指针、__free_hook、__malloc_hook这些东西别无二致了,这也就带来了潜在的攻击面。
procfs
进程文件系统(process file system),即/proc文件夹下的内容,描述了一个进程的运行状态,包括命令行参数、环境变量、进程内存布局等很多信息。这些内容实际存储在内存中,并不占用硬盘空间,故为伪文件系统。
开发者也可以使用API自行创建proc文件,定义相应回调函数,这就和i/o设备相差无几。
攻击面与利用手法
kernel pwn与用户态攻击面基本一致,主要还是针对栈、堆的攻击,比较有特色的是条件竞争(race condition)的攻击可能性增加,其余像整数溢出、数组越界依旧容易被利用。
攻击思路
针对kernel的攻击大多还是本地提权,各种提权手段,大致可以归结为以下三种思路:
修改cred结构体直接提权
|
|
只要将前28位置0,即覆盖前7项即可完成提权。具体做法一般是UAF释放和cred结构体相同大小的堆块,然后使用fork函数分配一个cred结构体刚好到这个堆块上,然后改值即可。
但是这种利用手法在kernel v4.5及以上版本中,由于引入了SLAB_ACCOUNT,cred结构体不可能分配到我们释放的堆块,故这条路基本堵死,关于这一点会在下面说内核保护时详细介绍。
构造ROP链提权并着陆用户态
由于linux kernel足够大足够复杂,比较容易找到各种gadget,这就有了构造ROP链的基础,下一步就是如何控制程序执行流的问题。
如果有栈溢出漏洞,自然直接覆盖返回地址即可。如果有堆上漏洞,就要考虑伪造特殊结构体,覆盖其中的函数指针,然后布置gadget进行栈迁移即可执行ROP链,这根用户态堆漏洞很多house of利用手法思路一致。常利用的结构体可以看【PWN.0x02】Linux Kernel Pwn II:常用结构体集合 - arttnba3‘s blog
ROP链中一般使用commit_creds(prepare_kernel_cred(NULL))
进行提权,本质上也是使用gadget修改了本进程的cred结构体。如果没有SMEP、SMAP保护或者可以修改cr4寄存器绕过,也可以直接ret2usr,即通过ROP执行用户代码更方便地提权,但在开启KPTI保护之后,ret2usr基本失效。
提权之后为了更好地利用,一般会返回用户态执行system("/bin/sh")
。从内核返回到用户态一般需要先使用swapgs恢复GS寄存器,然后使用iret或sysret返回用户态,使用这两个指令同时需要布置栈帧,比如iret返回的同时还会从栈上pop出4个值恢复用户态寄存器cs、eflags、sp、ss。为了防止非法寄存器值导致程序崩溃,最好在用户态执行一开始保存一份寄存器值方便后面使用。参考代码如下:
|
|
以上代码截取自Kernel ROP - CTF Wiki
任意地址写劫持全局静态指针
如果能直接获得任意地址读写的能力,那么可以直接泄漏内核基址、劫持全局静态指针一套带走了,当然如果有额外的内核保护无法修改那就没办法了。
常用的可劫持的是modprobe_path
,正常来说这个变量存着字符串/sbin/modprobe
,
动态内存分配 — slub系统
动态内存分配 — buddy系统
保护措施与绕过
地址随机化
kaslr
fgkaslr
隔离
SMEP
SMAP
KPTI
信息限制
dmesg_restrict
kptr_restrict
栈保护
cookie(canary)
堆保护
堆块分配隔离
GFP_KERNEL & GFP_KERNEL_ACCOUNT
SLAB_ACCOUNT
[PATCH v2 4/6] slab: add SLAB_ACCOUNT flag - Vladimir Davydov
Hardened Usercopy
Hardened freelist
Random freelist
CONFIG_INIT_ON_ALLOC_DEFAULT_ON
Reference
【OS.0x01】Linux Kernel II:内核简易食用指北 - arttnba3’s blog
【OS.0x00】Linux Kernel I:Basic Knowledge - arttnba3‘s blog
【PWN.0x00】Linux Kernel Pwn I:Basic Exploit to Kernel Pwn in CTF - arttnba3‘s blog