ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ctf-wiki Linux 内核提权专题:劫持特权进程执行轨迹(modprobe_path、poweroff_cmd、core_pattern 与 vDSO 攻击)

ctf-wiki Linux 内核提权专题:劫持特权进程执行轨迹(modprobe_path、poweroff_cmd、core_pattern 与 vDSO 攻击) 文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载本篇技术指南以 ctf-wiki 中 change-others.md 为核心骨架系统讲解 Linux 内核提权中改变他人一类攻击通过修改特权进程所依赖的数据改数据或修改特权进程将要执行的代码改代码间接获得 root 权限。读完本文你将掌握modprobe_path、poweroff_cmd、core_pattern三类经典内核全局数据劫持的完整利用流程与触发方式理解call_usermodehelper与CONFIG_STATIC_USERMODEHELPER的防御原理并学会在 vmlinux 与内存中定位 vDSO 进而实施代码改写攻击。文章同时结合仓库内 freelist.md 的真实 exploit 代码进行源码级佐证。从改变自己到改变别人内核提权的两种视角内核提权Privilege Escalation指普通用户获取 root 权限、访问原先受限资源的过程。ctf-wiki 在 readme.md 中将其划分为两种攻击视角改变自身Change Self通过修改自身进程的cred结构体或task_struct中的cred指针直接让当前进程获得 root 权限相关内容参见 change-self.md改变别人Change Others不直接提升自身权限而是改变特权进程的执行轨迹诱使内核或高权限进程替攻击者完成原本受限的操作。本文聚焦第二种视角。其核心思想是内核中存在大量以 root 权限运行的执行路径只要攻击者能劫持其中某一环——无论是它读取的数据还是它执行的代码——就能实现提权。整体可以划分为两大方向改数据改写特权进程使用的数据全局变量、指针指向的目标等改代码改写特权进程将要执行的代码如 vDSO 中的函数。改数据篡改特权进程依赖的数据符号链接最简单直接的数据劫持如果一个以 root 权限运行的进程会执行某个程序而该程序路径本身或其所经过的符号链接symlink可以由攻击者控制那么攻击者就可以在该路径上放置自己的程序让 root 进程代为执行从而实现提权。这是改数据中最直观的场景——被改动的数据就是那个将要被执行的文件路径。call_usermodehelper内核态执行用户态程序的入口call_usermodehelper是内核以线程方式在用户态启动应用的机制其典型特征是启动出来的进程具有 root 权限。因此只要攻击者能够控制具体要执行的应用就能借助内核之手完成提权。在内核中call_usermodehelper具体执行的应用往往由某个全局变量指定因此攻击思路非常清晰找到并改写这个变量。以modprobe_path为例内核源码kernel/kmod.c中的call_modprobe()展示了其完整调用关系static int call_modprobe(char *module_name, int wait) { //... 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); //... }该代码片段同时出现在仓库 freelist.md 的实战分析中call_usermodehelper_exec()将modprobe_path作为可执行文件路径以 root 权限执行该变量默认值为/sbin/modprobe。这一机制正是多种内核数据流攻击的公共底座。需要特别关注的防御开关在部分近期 CTF 题目以及少数现实应用如 AOSP 内核中开启了CONFIG_STATIC_USERMODEHELPER选项。该选项会使call_usermodehelper只能执行一个由编译期确定的只读字符串指定的特定应用从而直接封死改写全局变量指向恶意程序的利用方式。不难看出这是一类典型的数据流攻击实践中主要有以下几种手法。修改 modprobe_pathmodprobe_path是内核中用于存放自动加载内核模块辅助程序路径的全局变量默认值为/sbin/modprobe。利用它提权的基本流程如下获取modprobe_path的地址将modprobe_path修改为攻击者指定的程序如恶意脚本/home/pwn/catflag.sh触发call_modprobe执行从而让内核以 root 权限运行该程序。关于第 3 步的触发方式目前主要有两类已过时执行一个非法的可执行文件当execve一个文件格式无法识别的文件file magic not found时内核会尝试自动加载对应 binfmt 模块。然而2024 年 11 月引入的 Linux 新补丁移除了加载非法可执行文件对 binfmt 的自动加载路径导致该方法在Linux 6.12 及之后版本不再适用引诱内核加载新内核模块例如使用未知协议创建 socket触发内核尝试加载对应协议模块从而进入call_modprobe。从仓库的 freelist.md 中可以看到这一触发过程的完整内核调用链entry_SYSCALL_64() sys_execve() do_execve() do_execveat_common() bprm_execve() exec_binprm() search_binary_handler() __request_module() // wrapped as request_module call_modprobe()ctf-wiki 给出了使用modprobe_path的通用模板可直接套用于具有任意地址写能力的场景// step 1. modify modprobe_path to the target value // step 2. create related file system(echo -ne #!/bin/sh\n/bin/cp /flag /home/pwn/flag\n/bin/chmod 777 /home/pwn/flag\ncat flag /home/pwn/catflag.sh); system(chmod x /home/pwn/catflag.sh); // step 3. trigger modprobe using unknown executable (may be obsolete) system(echo -ne \\xff\\xff\\xff\\xff /home/pwn/dummy); system(chmod x /home/pwn/dummy); system(/home/pwn/dummy); // step 3. trigger modprobe using unknown protocol socket(AF_INET,SOCK_STREAM,132);注意模板中 step 3 的第一种触发方式执行dummy非法文件对应已过时的利用路径仅适用于 Linux 6.12 之前的旧内核第二种触发方式socket(AF_INET, SOCK_STREAM, 132)协议号 132 为 SCTP通过未知协议请求触发内核加载模块是当前更可靠的触发手段。定位 modprobe_path 的两种思路这也是整个改数据环节的核心难点直接定位modprobe_path的取值/sbin/modprobe是确定不变的字符串因此可以直接扫描内核内存寻找该字符串——前提是攻击者具备扫描内存的能力通常来自任意地址读间接定位modprobe_path相对于内核基地址的偏移是固定的只随内核版本变化因此可以先获取内核基地址如通过泄露符号地址并减去固定偏移再根据相对偏移计算modprobe_path的地址。仓库 freelist.md 的完整 exploit 正是采用了这种方式先猜测page_offset_base泄露内核基址再计算MODPROBE_PATH的运行时地址#define MODPROBE_PATH 0xffffffff82444700 ... kernel_base data.ptr[2] - 0x30; kernel_offset kernel_base - 0xffffffff81000000; ... data.ptr[0] kernel_offset MODPROBE_PATH - 0x10; data.offset 0; data.length 0x8; editBuf(dev_fd[2], data); allocBuf(dev_fd[2], data); allocBuf(dev_fd[2], data); strcpy((char *) data.ptr[2], ROOT_SCRIPT_PATH); data.length 0x30; editBuf(dev_fd[2], data); /* trigger the fake modprobe_path */ system(echo -e \\xff\\xff\\xff\\xff /home/fake); system(chmod x /home/fake); system(/home/fake);该 exploit 中ROOT_SCRIPT_PATH为/home/getshell脚本内容为#!/bin/sh\nchmod 777 /flag触发后即获得/flag的读取权限。这是一个堆 UAF 构造任意地址写 → 覆写modprobe_path→ 执行非法文件触发 → 以 root 执行恶意脚本的完整案例。修改 poweroff_cmdpoweroff_cmd是内核中用于存放关机poweroff时辅助命令路径的全局变量利用思路与modprobe_path完全同构修改poweroff_cmd为攻击者指定的程序劫持控制流执行__orderly_poweroff触发内核以 root 权限运行该程序。__orderly_poweroff是内核执行有序关机的内部函数它会经由call_usermodehelper类机制执行poweroff_cmd指定的程序。定位poweroff_cmd的方法与定位modprobe_path相同——既可以扫描其特征字符串也可以基于相对内核基址偏移固定这一性质间接计算。修改 core_patterncore_pattern是内核的 core dump崩溃转储配置项位于fs/coredump.c的实现逻辑中。当core_pattern以管道符|开头时内核会把 core dump 内容通过管道交给后面的程序处理且该程序以 root 权限运行。利用思路为将core_pattern修改为|/path/to/your/program启用崩溃转储触发内核执行core_pattern管道符后的程序即|之后的路径该路径由fs/coredump.c中的调用点解析执行。core_pattern有一个独特优势格式串中大写%P表示崩溃进程在宿主机中的 PID。由于容器与宿主机 PID namespace 的差异%P可以指回宿主机的真实进程因此core_pattern常被用来取代 modprobe_path 用于容器Container环境下的提权——容器逃逸场景中modprobe_path往往位于容器自己的 namespace 而不可用core_pattern却能与宿主机交互。定位core_pattern同样可以采用类似定位modprobe_path的方法字符串特征扫描 / 内核基址相对偏移。关于使用core_pattern提权的完整实现细节可参考仓库文档中提到的近期 kctf 相关 exploitCVE-2025-21836 等方向中的%P与core_pattern管道执行利用代码。改代码篡改特权进程执行的代码如果攻击者在程序运行时能够修改 root 权限进程正在执行或即将执行的代码同样可以实现提权。与改数据相比改代码直接控制指令流攻击效果更彻底。修改 vDSO 代码vDSOvirtual dynamic shared object是内核映射进所有用户态进程地址空间的一小段特殊共享库其作用是将某些系统调用如gettimeofday、clock_gettime等以用户态函数的形式暴露给进程从而避免陷入内核的开销。由于 vDSO 会被映射到所有用户态进程包括高特权进程中且其中的函数会被周期性调用因此它是一个极具吸引力的代码改写目标如果有一个高特权进程会周期性地调用 vDSO 中的函数攻击者把 vDSO 中相应函数修改为特定的 shellcode当高权限进程下次执行该函数时shellcode 即以该进程的权限运行从而实现提权。历史背景与防御演进早期 Linux 的 vDSO 是可写的存在被篡改的风险。为此Kees Cook 提出引入post-init read-only即__ro_after_init机制将初始化后不再被写的数据标记为只读从硬件页保护层面封死这类改写。从内核构建脚本生成的vdso相关 C 代码中可以看到这一演变的直接证据。引入之前vDSO 对应的raw_data只是简单的页对齐数据fprintf(outfile, /* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n); fprintf(outfile, #include linux/linkage.h\n); fprintf(outfile, #include asm/page_types.h\n); fprintf(outfile, #include asm/vdso.h\n); fprintf(outfile, \n); fprintf(outfile, static unsigned char raw_data[%lu] __page_aligned_data {, mapping_size);引入之后raw_data被标记为__ro_after_init且以PAGE_SIZE对齐fprintf(outfile, /* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n); fprintf(outfile, #include linux/linkage.h\n); fprintf(outfile, #include asm/page_types.h\n); fprintf(outfile, #include asm/vdso.h\n); fprintf(outfile, \n); fprintf(outfile, static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) {, mapping_size);通过修改 vDSO 进行提权的基本步骤定位 vDSO将 vDSO 中特定函数修改为指定的 shellcode等待高权限进程调用该函数以触发 shellcode 执行。其中定位 vDSO是最关键的一步下面分在 vmlinux 中定位与在内存中定位两类展开。在 IDA 中定位 vmlinux 中的 vDSO在静态分析 vmlinux含符号的内核镜像时可以沿着符号引用逐级找到 vDSO 的存放位置Step 1在 IDA 中定位init_vdso函数的地址。init_vdso是内核初始化 vDSO 的入口函数反编译后大致如下__int64 init_vdso() { init_vdso_image(vdso_image_64 0x20000000); init_vdso_image(vdso_image_x32 0x20000000); cpu_maps_update_begin(); on_each_cpu((char *)startup_64 0x100003EA0LL, 0LL, 1LL); _register_cpu_notifier(sdata 536882764); cpu_maps_update_done(); return 0LL; }Step 2跟随vdso_image_64找到其引用。vdso_image_64是描述 64 位 vDSO 镜像的结构体IDA 中点击该符号可看到其定义其中第一个成员即指向raw_data注意这里的地址为静态链接基址带.rodata段偏移.rodata:FFFFFFFF81A01300 public vdso_image_64 .rodata:FFFFFFFF81A01300 vdso_image_64 dq offset raw_data ; DATA XREF: arch_setup_additional_pages18↑o .rodata:FFFFFFFF81A01300 ; init_vdso1↓oStep 3点击raw_data得到 64 位 vDSO 在内核镜像中的地址。可以看到 vDSO 内容以0x7F E L F开头ELF 魔数且确实以页对齐.data:FFFFFFFF81E04000 raw_data db 7Fh ; ; DATA XREF: .rodata:vdso_image_64↑o .data:FFFFFFFF81E04001 db 45h ; E .data:FFFFFFFF81E04002 db 4Ch ; L .data:FFFFFFFF81E04003 db 46h ; F从最后的符号信息来看也可以直接使用raw_data符号在内核符号表如 System.map参考 System.map.md中搜索来定位 vDSO。在内存中定位运行时的 vDSO运行时内存中定位 vDSO 同样有直接与间接两条路径直接定位vDSO 本质上是一个 ELF 文件具有 ELF 文件头\x7fELF并且 vDSO 中特定位置存储着导出函数的字符串如__vdso_gettimeofday等符号名。因此可以依据这两个特征扫描内存先按\x7fELF魔数粗筛候选页再验证候选区域中是否包含 vDSO 导出函数字符串从而确认 vDSO 的准确位置。间接定位与modprobe_path类似vDSO 相对于内核基地址的偏移是固定的因此可以先获取内核基地址再根据相对偏移计算 vDSO 的地址。这种方式在开启了 KASLR内核地址空间布局随机化参见 kaslr.md的内核中尤为常用——KASLR 只随机化内核整体基址符号间相对偏移保持不变。实战要点与防御对照综合全文可以总结出改变他人执行轨迹这一提权家族的通用方法论与防御演进的对照关系攻击目标改动对象触发方式主要限制/防御modprobe_path全局字符串/sbin/modprobe执行非法文件6.12 前/ 未知协议触发模块加载CONFIG_STATIC_USERMODEHELPER6.12 移除 binfmt 自动加载poweroff_cmd全局字符串劫持控制流调用__orderly_poweroff需要额外的控制流劫持能力core_pattern全局字符串使目标进程崩溃触发 core dump 管道执行需使特权进程可崩溃%P可用于容器逃逸vDSO 代码raw_dataELF 镜像高特权进程周期调用 vDSO 函数__ro_after_init只读页保护所有攻击共享同一个定位 修改两步模型如同 change-self.md 中对cred的操作一样定位依赖字符串特征直接扫描或相对内核基址偏移固定这两条原则修改则依赖攻击者已获得的任意地址读写或控制流劫持能力。这也是 ctf-wiki 将其归入数据流/代码流攻击、并建议读者与 info-leak.md信息泄露结合学习的原因——泄露内核基址是间接定位所有全局符号的前提。在实际做题时建议优先按以下顺序评估可利用面先检查内核是否开启CONFIG_STATIC_USERMODEHELPER与目标内核版本判断modprobe_path路径是否可行再检查core_pattern是否可写且存在可崩溃的特权进程最后考虑需要更强前置条件控制流劫持 / 代码改写的poweroff_cmd与 vDSO 攻击。结合仓库中的 freelist.md 完整 exploit 与 writable-root.md 等 tricks即可组装出从任意地址写到root shell的完整利用链。赞分享文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载相关推荐ctf-wiki 内核 Pwn 实战Linux 内核 UAF 利用与 slub 堆提权——从垂悬指针到 tty_struct 劫持ctf wiki 内核 Pwn 实战Linux 内核 UAF 利用与 slub 堆提权——从垂悬指针到 tty_struct 劫持 本文以 CTF Wiki文档网络安全教程PaddleOCR.js 浏览器端部署完全指南在 Web 应用中运行 PP-OCR 检测与识别PaddleOCR.js 浏览器端部署完全指南在 Web 应用中运行 PP OCR 检测与识别 本篇技术指南以 PaddleOCR 的浏览器端 OCR SDK文档网络安全教程ctf-wiki Linux 内核提权Change Self详解通过修改 cred 实现进程提权ctf wiki Linux 内核提权Change Self详解通过修改 cred 实现进程提权 本文是 CTF Wiki 的 Linux 内核漏洞利用系文档网络安全教程上一篇深入解析SCUDA从协议层到应用层的完整实现指南下一篇如何免费将CAJ转PDF2025年超实用的中国知网文献转换工具推荐创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表