Kernel Pwn第四弹(路径劫持)
基础知识
modprobe_path
modprobe_path
指向了一个内核在运行未知文件类型时运行的二进制文件;当内核运行一个错误格式的文件的时候,会调用这个modprobe_path
所指向的二进制文件去,如果我们将这个字符串指向我们的自己的恶意二进制文件,那么在发生错误的时候就可以执行我们的恶意文件了。
1 | cat /proc/kallsyms | grep modprobe_path |
可以查看modprobe_path
在内核中的地址
mod_tree
mod_tree是一块包含了模块指针的内存地址的内核地址,通过查看这个位置我们可以获取到驱动ko文件的地址,在我们需要泄露模块基地址,但是在堆或栈中没有找到的时候可以查看这块内存区域
1 | grep mod_tree /proc/kallsyms |
题目附件
- initramfs.cpio打包的文件系统
- bzImage内核镜像
- hackme.ko漏洞驱动
- start.sh启动脚本
提取出文件系统
1 | cpio -idmv < initramfs.cpio |
提取出vmlinux便于调试
1 | vmlinux-to-elf bzImage vmlinux |
start.sh
1 |
|
开启了kaslr,smep以及smap。
/etc/initd/rcS
这道题的init为空,启动项写在/etc/initd/rcS
中
1 |
|
注意调试分析时关闭start.sh中的kaslr以及设置启动项为root,方便查看内核地址
代码分析
1 | __int64 __fastcall hackme_ioctl(__int64 a1, unsigned int a2, args *a3) |
实现了类似堆块的add,delete,show以及edit功能。分析代码提取出数据结构
1 | struct heap |
alloc
申请一块内存,地址和大小放在pool
数组中
delete
根据idx释放内存
edit_user
从内核拷贝length长度的内存到栈中,注意这里检测offset+length要小于length,那么offset就不能大于0,但是对offset没作限制,可以为负数,那么就能绕过这个判断而且此时还会导致内存向前任意读取。
edit_kernel
从栈中拷贝数据到内核中,这里也是对offset没作限制,所以可以利用offset为负数来实现向前任意内核地址写。
利用思路
因为slub
分配器的分配原理和fastbin
的原理类似,所以我们可以通过越界读写修改chunk的fd指针(用户态中的说法)。
如果我们将本来chunk1——>chunk3的fd指针修改为指向modprobe_path,然后再修改modprobe_path为恶意程序路径,再触发执行错误即可完成劫持。
但是想要修改fd为modprobe_path,需要泄露内核地址才能得到modprobe_path真正的地址。
泄露地址
从申请的堆地址往上找一下,发现有一处sysctl_table_root可以用来泄露内核地址
1 | edit_user(0,mem,0x100,-0xd8); |
泄露模块地址就利用修改fd指针申请到mod_tree,然后再泄露mod_tree内存处保存的模块加载地址
1 | *((size_t *)mem) = mod_tree + 0x50; |
劫持指针
本来想直接通过像申请mod_tree那样直接申请modprobe_path,然后直接修改modprobe_path为恶意程序地址进行利用的。
因为这样就可以不用泄露ko的地址,也不用再申请到pool数组
最后虽然修改成功了,但是执行system会发生崩溃,原因不得而知。
所以就还是先老实的把pool数组也申请出来,通过修改pool数组为modprobe_path,再修改modprobe_path为恶意程序地址,最后控制执行即可
EXP
1 |
|
成功利用!
参考文献
1 | https://bbs.kanxue.com/thread-276403.htm#msg_header_h3_5 |