Linux Kernel Pwn 初探
字数 1811 2025-08-24 16:48:16

Linux Kernel Pwn 初探 - 详细教学文档

基础知识

内核主要功能

  1. 控制并与硬件进行交互
  2. 提供应用程序能运行的环境

CPU特权级别

Intel CPU将CPU特权级别分为4个级别:Ring 0、Ring 1、Ring 2、Ring 3。

  • Ring 0:只给操作系统使用
  • Ring 3:所有程序都可以使用
  • 内层Ring可以访问外层Ring的资源

进入内核态的方式

  1. 系统调用:int 0x80、syscall、ioctl
  2. 产生异常
  3. 外设产生中断

内核态与用户态切换

  • 进入内核态前:保存用户态各个寄存器及执行位置
  • 返回用户态
    • 64位系统:执行swapgs和iret指令
    • 32位系统:直接iret返回
    • 需要栈上布置好恢复的寄存器值

攻击思路

  1. 寻找内核程序漏洞
  2. 调用该程序进入内核态
  3. 利用漏洞进行提权
  4. 返回用户态

CTF中的Linux Kernel Pwn

常见形式

漏洞通常存在于动态装载模块(LKMs, Loadable Kernel Modules)中,包括:

  • 驱动程序(Device drivers)
  • 内核扩展模块(modules)

题目文件组成

通常提供四个文件:

  1. baby.ko:有漏洞的驱动程序
  2. bzImage:打包的内核,用于启动虚拟机与寻找gadget
  3. Initramfs.cpio:文件系统
  4. startvm.sh:启动脚本

有时还包含:

  • vmlinux:未打包的内核,含有符号信息

文件系统提取方法

  1. ext4文件系统

    mkdir ./rootfs
    sudo mount rootfs.img ./rootfs
    # 查看启动脚本init或etc/init.d/rcS
    sudo umount rootfs
    
  2. cpio文件系统

    mkdir extracted; cd extracted
    cpio -i --no-absolute-filenames -F ../rootfs.cpio
    find . | cpio -o --format=newc > ../rootfs.cpio
    

内核保护机制

  1. KPTI:内核页表隔离
  2. KASLR:内核地址空间布局随机化
  3. SMEP:管理模式执行保护
  4. SMAP:管理模式访问保护
  5. Stack Protector:栈保护(canary)
  6. kptr_restrict:限制查看内核函数地址
  7. dmesg_restrict:限制查看printk输出
  8. MMAP_MIN_ADDR:不允许申请NULL地址

查看保护机制

cat /proc/cpuinfo

关闭保护方法

  • KASLR、SMEP、SMAP:修改startvm.sh
  • dmesg_restrict、kptr_restrict:修改rcS文件
  • MMAP_MIN_ADDR:重新编译内核(.config文件)

准备工作

  1. 以root权限启动虚拟机
  2. 修改rcS文件中的权限设置
  3. 获取关键信息:
    lsmod  # 查看驱动加载基址
    cat /proc/kallsyms | grep "prepare_kernel_cred"  # 获取函数地址
    cat /proc/kallsyms | grep "commit_creds"  # 获取函数地址
    

漏洞类型与利用

1. 栈溢出漏洞(level1)

  • 特征:直接ret2usr
  • 利用步骤:
    1. 构造ROP链
    2. 覆盖返回地址
    3. 执行提权代码

2. 信息泄露+ROP(level2)

  • 特征:需要绕过SMEP/SMAP
  • 利用步骤:
    1. 泄露内核地址和canary
    2. 构造ROP链修改cr4寄存器
    3. 关闭SMEP/SMAP保护
    4. 执行提权代码

3. 竞争条件漏洞(level3)

  • 特征:double fetch漏洞
  • 利用步骤:
    1. 创建两个线程
    2. 主线程传入用户空间地址
    3. 子线程在验证间隙修改为内核地址
    4. 完成验证实现flag打印

4. UAF漏洞(level4)

  • 特征:未清理释放的指针
  • 利用步骤:
    1. 第一次UAF泄露内核地址
    2. 堆喷伪造数据
    3. 第二次UAF修改cr4
    4. 第三次UAF执行提权

实战案例

CVE-2019-9213

漏洞描述:Linux内核在mm/mmap.c中的expand_downwards函数缺少对mmap最小地址的检查,导致可以映射NULL地址。

利用方法

  1. 使用MAP_GROWSDOWN标志映射内存
  2. 向低地址扩展
  3. 映射到NULL地址实现提权

CVE-2019-8956

漏洞描述:sctp_sendmsg()函数处理SCTP_SENDALL标志时存在use-after-free错误。

利用方法

  1. 触发空指针解引用
  2. 结合0虚拟地址映射漏洞
  3. 伪造指针实现任意代码执行

关键代码片段

保存/恢复状态

void save_stat() {
    asm(
        "movq %%cs, %0;"
        "movq %%ss, %1;"
        "movq %%rsp, %2;"
        "pushfq;"
        "popq %3;"
        : "=r" (user_cs), "=r" (user_ss), "=r" (user_sp), "=r" (user_rflags) : : "memory");
}

void templine() {
    commit_creds(prepare_kernel_cred(0));
    asm(
        "pushq   %0;"
        "pushq   %1;"
        "pushq   %2;"
        "pushq   %3;"
        "pushq   $shell;"
        "pushq   $0;"
        "swapgs;"
        "popq    %%rbp;"
        "iretq;"
        ::"m"(user_ss), "m"(user_sp), "m"(user_rflags), "m"(user_cs));
}

绕过SMEP/SMAP

// 修改cr4为0x6f0
int i = 18;
buf[i++] = calc(0xffffffff815033ec);  // pop rdi; ret;
buf[i++] = 0x6f0;
buf[i++] = calc(0xffffffff81020300);  // mov cr4,rdi; pop rbp; ret;
buf[i++] = 0;
buf[i++] = &templine;

工具与技巧

  1. 提取vmlinux

    ~/linux-4.20/scripts/extract-vmlinux ./bzImage > vmlinux
    
  2. 提取gadget

    objdump -d ./vmlinux > gadget
    
  3. 调试技巧

    • 在startvm.sh中添加-gdb tcp::1234 -S等待调试器连接
    • 使用ubuntu 18.04避免玄学问题
  4. 编译上传exp

    from pwn import *
    context.update(log_level='debug')
    
    def compile():
        os.system("musl-gcc -w -s -static -o3 oob.c -o exp")
    
    def upload():
        with open("exp", "rb") as f:
            encoded = base64.b64encode(f.read())
        for i in range(0, len(encoded), 300):
            exec_cmd("echo \"%s\" >> benc" % (encoded[i:i+300]))
        exec_cmd("cat benc | base64 -d > bout")
        exec_cmd("chmod +x bout")
    

总结

Linux Kernel Pwn需要掌握:

  1. 内核基本工作原理
  2. 常见保护机制及绕过方法
  3. 漏洞类型识别与利用技巧
  4. 内核态与用户态切换机制
  5. 调试与开发工具使用

通过系统学习和实践,可以逐步掌握内核漏洞利用的技术要点。

Linux Kernel Pwn 初探 - 详细教学文档 基础知识 内核主要功能 控制并与硬件进行交互 提供应用程序能运行的环境 CPU特权级别 Intel CPU将CPU特权级别分为4个级别:Ring 0、Ring 1、Ring 2、Ring 3。 Ring 0:只给操作系统使用 Ring 3:所有程序都可以使用 内层Ring可以访问外层Ring的资源 进入内核态的方式 系统调用:int 0x80、syscall、ioctl 产生异常 外设产生中断 内核态与用户态切换 进入内核态前 :保存用户态各个寄存器及执行位置 返回用户态 : 64位系统:执行swapgs和iret指令 32位系统:直接iret返回 需要栈上布置好恢复的寄存器值 攻击思路 寻找内核程序漏洞 调用该程序进入内核态 利用漏洞进行提权 返回用户态 CTF中的Linux Kernel Pwn 常见形式 漏洞通常存在于动态装载模块(LKMs, Loadable Kernel Modules)中,包括: 驱动程序(Device drivers) 内核扩展模块(modules) 题目文件组成 通常提供四个文件: baby.ko :有漏洞的驱动程序 bzImage :打包的内核,用于启动虚拟机与寻找gadget Initramfs.cpio :文件系统 startvm.sh :启动脚本 有时还包含: vmlinux :未打包的内核,含有符号信息 文件系统提取方法 ext4文件系统 : cpio文件系统 : 内核保护机制 KPTI :内核页表隔离 KASLR :内核地址空间布局随机化 SMEP :管理模式执行保护 SMAP :管理模式访问保护 Stack Protector :栈保护(canary) kptr_ restrict :限制查看内核函数地址 dmesg_ restrict :限制查看printk输出 MMAP_ MIN_ ADDR :不允许申请NULL地址 查看保护机制 关闭保护方法 KASLR、SMEP、SMAP:修改startvm.sh dmesg_ restrict、kptr_ restrict:修改rcS文件 MMAP_ MIN_ ADDR:重新编译内核(.config文件) 准备工作 以root权限启动虚拟机 修改rcS文件中的权限设置 获取关键信息: 漏洞类型与利用 1. 栈溢出漏洞(level1) 特征:直接ret2usr 利用步骤: 构造ROP链 覆盖返回地址 执行提权代码 2. 信息泄露+ROP(level2) 特征:需要绕过SMEP/SMAP 利用步骤: 泄露内核地址和canary 构造ROP链修改cr4寄存器 关闭SMEP/SMAP保护 执行提权代码 3. 竞争条件漏洞(level3) 特征:double fetch漏洞 利用步骤: 创建两个线程 主线程传入用户空间地址 子线程在验证间隙修改为内核地址 完成验证实现flag打印 4. UAF漏洞(level4) 特征:未清理释放的指针 利用步骤: 第一次UAF泄露内核地址 堆喷伪造数据 第二次UAF修改cr4 第三次UAF执行提权 实战案例 CVE-2019-9213 漏洞描述 :Linux内核在mm/mmap.c中的expand_ downwards函数缺少对mmap最小地址的检查,导致可以映射NULL地址。 利用方法 : 使用MAP_ GROWSDOWN标志映射内存 向低地址扩展 映射到NULL地址实现提权 CVE-2019-8956 漏洞描述 :sctp_ sendmsg()函数处理SCTP_ SENDALL标志时存在use-after-free错误。 利用方法 : 触发空指针解引用 结合0虚拟地址映射漏洞 伪造指针实现任意代码执行 关键代码片段 保存/恢复状态 绕过SMEP/SMAP 工具与技巧 提取vmlinux : 提取gadget : 调试技巧 : 在startvm.sh中添加 -gdb tcp::1234 -S 等待调试器连接 使用ubuntu 18.04避免玄学问题 编译上传exp : 总结 Linux Kernel Pwn需要掌握: 内核基本工作原理 常见保护机制及绕过方法 漏洞类型识别与利用技巧 内核态与用户态切换机制 调试与开发工具使用 通过系统学习和实践,可以逐步掌握内核漏洞利用的技术要点。