背景分析
下面分析 以下modeprobe_path的作用:
执行一个错误文件头的时候,调用链如下:
直接看exec_binprm函数,在一个depth的循环中迭代,调用到search_binary_handler:
进入到searchPbinary_handler函数,可以看到首先进行安全性检查,然后会通过一个叫做“list_for_head“的宏来遍历formats这个全局变量:
formats全局变量定义在这里,是一个双链表结构:
调试format如下,
具体的一个elf_format如下:
具体的汇编如下,就是直接和format的地址作比较,应该是预先通过文件名字找了一个format?:
总之如果找不到一个format,且need_retry == true的情况下,就会进入到如下代码中调用request_module:
最终调用到call_usermodehelper(从全局变量modeprobe_path中获取路径),这就是一个内核调用用户层程序的例子,这个程序是以特权运行的。
源码分析
回到subprocess_info:
其触发分配的代码如下:
#include <sys/socket.h>
int main(){
socket(22, AF_INET, 0);
}
函数调用栈帧如下:
可以看到是走了call_usermodehelper_set,这个函数的作用是创建一个subprocess_info:https://elixir.bootlin.com/linux/v5.19/source/kernel/umh.c#L358
调试一下看看在本环境下该结构体的大小:
然后到https://elixir.bootlin.com/linux/v5.19/source/kernel/kmod.c中看一下 __request_module函数:
其会通过call_modprobe函数来调用call_usermodehelper_setup,但是我们的栈帧中没有,且这个函数也不是内联的:
setup之后就会exec:
如果此时info的path是NULL,就会free info,就会调用导subprocess_info的cleanup函数指针:
利用方法
因此,当我们有能力篡改一个subprocess_info结构体的时候,就可以将其path设置为0,将其cleanup指针设置为目标函数,即可实现控制流的劫持;
参考
https://www.anquanke.com/post/id/236126#h2-0
https://x3h1n.github.io/2019/10/10/ret2dir%E5%88%A9%E7%94%A8%E5%AD%A6%E4%B9%A0/