论坛首页 逆向工程技术区 阅读主题

[原创]Linux内核modprobe_path覆盖利用技术

497 浏览 0 回复
#1 楼主 2026-06-01 21:09:19
今年的 ccb&ciscn 初赛有一道 Linux 内核的 Pwn 题,让我这个内核菜鸡被直接“硬控”了。现在初赛都上内核了。。。赛后搞到了 WP,于是正好趁着元旦假期有时间好好复现一下,分析了一下这道题的利用技术的原理,于是就有了这篇文章。modprobe_path覆盖利用技术是一种 Linux 内核漏洞利用方法,它可以让攻击者以 root 权限运行任意 shell 脚本。modprobe_path覆盖利用技术的关键是覆盖掉modprobe_path内核全局变量,它的默认值为/sbin/modprobe程序的路径,modprobe是一个用于向 Linux 内核添加可加载内核模块,或从内核中移除可加载内核模块的工具。本质上它是一个用户态程序,当我们在 Linux 内核中安装或卸载新模块时会被执行。我们可以通过运行以下命令检查:modprobe_path变量中的程序,会在我们执行一个系统无法识别文件类型的非法格式文件(无效魔数)时被调用。简单说一般我们执行 shell 文件时文件签名是#!/bin/bash,这是合法的文件格式,系统可以识别。而我们执行一个文件签名为\xff\xff\xff\xff的文件时内核会进行一系列操作,最终触发并以 root 权限执行modprobe_path变量中的程序。因此如果我们将modprobe_path变量覆盖修改为想要执行的程序,然后再执行一个非法格式文件就可以以 root 权限执行我们想要执行的目标程序。但是如果内核开启了CONFIG_STATIC_USERMODEHELPER内核配置选项,那么会将内核执行用户态 helper 的路径从运行时可变的全局变量改为编译期固定的常量,这样我们就无法对modprobe_path进行覆盖修改。总结一下modprobe_path覆盖利用技术需要满足以下条件:基本上,在大多数的 kernel pwn 题目中,条件 2 和条件 3 都是直接满足的,因此最关键的问题在于是否具备任意地址写的能力以及内核是否开启了CONFIG_STATIC_USERMODEHELPER选项。询问了一下 GPT 给了一个检查内核是否开启CONFIG_STATIC_USERMODEHELPER选项的方法:如果修改成功则CONFIG_STATIC_USERMODEHELPER选项未开启,不成功则开启。接下来我们通过分析 Linux 内核的源代码,详细了解覆盖modprobe_path利用技术的原理。当我们在 Linux Shell 中执行命令时(例如bash中执行ls),本质上会由 Shell 程序内部调用execve系统调用来加载并执行对应的程序。execve系统调用会设置可执行文件路径、命令行参数数组以及环境变量数组等信息,然后调用do_execve,它才是真正负责执行程序加载的核心函数。do_execve函数会对传入的可执行文件路径、参数数组和环境变量数组进行封装与处理,然后将执行流程统一转交给 do_execveat_common,由后者完成后续的程序加载与执行逻辑。do_execveat_common负责构建并填充 linux_binprm,完成命令行参数和环境变量的准备工作。linux_binprm是 Linux 内核中 描述一个即将被execve执行的可执行文件及其执行上下文的核心数据结构,是execve执行流程中的“载体”,保存了程序加载所需的全部信息。do_execveat_common最终会调用bprm_execve,进入真正的二进制加载流程。bprm_execve是execve执行路径中最关键的通用入口函数。bprm_execve函数会先对被加载程序的信息准备凭证与安全检查,最后调用exec_binprm函数加载可执行文件。exec_binprm函数负责在执行过程中选择并执行合适的二进制格式处理器,通过循环解析解释器链,最终完成可执行文件的确定并触发执行成功事件通知。其中最重要的逻辑就是解析选择合适的二进制格式处理器部分,因此我们接下来需要跟进search_binary_handler函数分析。search_binary_handler函数会在formats链表中寻找合适的文件加载器。formats是一个包含所有已注册的可执行格式文件解析器。常见的解析器:每种解析器包含一个关键字段load_binary,其中包含着对应格式的加载函数。检查文件魔数,匹配解析器:例如 ELF 格式:如果成功匹配到解析器,内核将调用该格式对应格式的加载函数完成可执行文件的加载(如 ELF 文件或 shell 文件)。如果没有匹配到解析器,且文件的前 4 字节不可打印(非 ASCII),内核会进入一个兜底处理流程。在在该流程中,内核会根据可执行文件头部的魔数值,动态请求加载对应的 binfmt 内核模块。具体做法是从文件头的第 3、4 字节读取一个 16 位数值,并将其格式化为模块名 binfmt-xxxx,随后调用request_module触发模块加载:request_module其实是一个宏,真正执行的是__request_module函数:__request_module函数最终会调用call_modprobe函数。call_modprobe是内核模块自动加载路径中真正执行用户态程序的函数:这部分代码的关键点:通过call_usermodehelper_setup封装好execve需要的所有信息,然后调用call_usermodehelper_exec函数以 root 权限执行modprobe_path的值。默认的modprobe_path值是:CONFIG_MODPROBE_PATH来自内核配置,默认为/sbin/modprobe。所以如果我们修改了modprobe_path的值,并执行一个非法格式文件后就会执行modprobe_path的值。前面已经说过启用CONFIG_STATIC_USERMODEHELPER选项就会导致modprobe_path覆盖利用技术失效,这里结合代码分析一下原因。在call_usermodehelper_setup中,如果CONFIG_STATIC_USERMODEHELPER配置选项启用,那么就会执行以下代码:此时内核会强制使用静态、编译时写死的路径,而忽略modprobe_path,从而使得modprobe_path覆盖技术失效。下载题目:8a4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4j5Y4V1J5f1V1q4Q4x3X3c8U0f1U0c8%4y4W2c8m8x3g2y4a6j5h3V1J5N6s2y4m8i4K6y4r3M7s2N6V1i4K6y4p5j5%4j5#2k6l9`.`.题目提供了三个文件:首先分析启动脚本:保护机制:只读数据保护关闭会导致.rodata段可写,这样的话即使modprobe_path是常量,也能被覆盖。这样就直接绕过了CONFIG_STATIC_USERMODEHELPER的防护。然后解压rootfs.c

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-289658.htm

暂无回复,快来抢沙发吧!

请登录后参与讨论

立即登录 注册账号