最近看内核exp时候一些题目的另一种解法可以通过modprobe_path
方式读取flag,好像挺好玩的
定义
在内核运行一个未知类型的文件时,modprobe_path
就会指向这个未知文件,当内核运行一个错误格式的文件后,内核会自动调用这个modprobe_path
指向的文件,因为内核执行的权限是root权限,当我们将modprobe_path
指向一个我们用于查看具有root权限的flag文件,那么就能得到flag
//源码定义如下:kernel/kmod.c
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
实际调用如下:
//linux4.4.7 /kernel/kmod.c
static int call_modprobe(char *module_name, int wait)
{
struct subprocess_info *info;
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL
};
char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
if (!argv)
goto out;
module_name = kstrdup(module_name, GFP_KERNEL);
if (!module_name)
goto free_argv;
argv[0] = modprobe_path;
argv[1] = "-q";
argv[2] = "--";
argv[3] = module_name; /* check free_modprobe_argv() */
argv[4] = NULL;
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
NULL, free_modprobe_argv, NULL);
if (!info)
goto free_module_name;
return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
//启动用户模式应用程序 info结构体里面包含了子进程信息和modprobe_path
free_module_name:
kfree(module_name);
free_argv:
kfree(argv);
out:
return -ENOMEM;
}
使用
/ $ cat /proc/kallsyms | grep modprobe_path #不一定会有
ffffffff81e476e0 D modprobe_path
符号里面没有 modprobe_path
地址的时候可以通过一些函数的引用来找
比如__request_module
-
通过shell命令(开启保护后需要root身份):
grep __request_module /proc/kallsyms
查看汇编即可得到字符串地址/ $ grep __request_module /proc/kallsyms #user 0000000000000000 T __request_module 0000000000000000 t __request_module.cold.4 / # grep __request_module /proc/kallsyms #root ffffffff810833e0 T __request_module #addr ffffffff8108378b t __request_module.cold.4
-
通过gef插件:(不用root身份就可以查看)
gef➤ xinfo __request_module ────────────────────────────────── xinfo: 0xffffffff810833e0────────────────────────────────── Page: 0x00000000000000 → 0xffffffffffffffff (size=0xffffffffffffffff) Permissions: rwx Pathname: /home/hnhuangjingyu/kernel/SUCTF2019_sudrv/vmlinux Offset (from page): 0xffffffff810833e0 //得到的地址 Inode: 0 Segment: .text (0xffffffff81000000-0xffffffff81c01781) Offset (from segment): 0x833e0 Symbol: __request_module
这里直接使用gef进行观察
gef➤ x/30i __request_module 0xffffffff810833e0 <__request_module>: push rbp 0xffffffff810833e1 <__request_module+1>: mov rbp,rsp 0xffffffff810833e4 <__request_module+4>: push r15 0xffffffff810833e6 <__request_module+6>: push r14 //。。。。忽略。。。。。。。 0xffffffff81083425 <__request_module+69>: jne 0xffffffff810835a6 <__request_module+454> 0xffffffff8108342b <__request_module+75>: xor r15d,r15d 0xffffffff8108342e <__request_module+78>: cmp BYTE PTR [rip+0x11beeeb],0x0 # 0xffffffff82242320 //在这里!!! 0xffffffff81083435 <__request_module+85>: jne 0xffffffff8108345e -------------------- -------------------- -------------------- -------------------- gef➤ strings 0xffffffff82242320 0xffffffff82242320: "/sbin/modprobe" 0xffffffff8224232f: "" 0xffffffff82242330: ""
那么就得到了
modprobe_path
=0xffffffff82242320
mod_tree
mod_tree
这个内核指针里面包含了模块指针的内存地址。通过它可以获取到ko文件的地址
/sbin # cat /proc/kallsyms | grep mod_tree
ffffffff811043e0 t __mod_tree_remove
ffffffff81105b00 t __mod_tree_insert
ffffffff81e09200 d mod_tree
使用实例:
题目2019 suctf sudrv -> https://blog.csdn.net/csdn546229768/article/details/124367307