kernel从小白到大神(二)
字数 2157 2025-08-23 18:31:08

Linux Kernel Pwn 从入门到精通(二):保护机制与绕过技术

一、内核保护机制详解

1. KASLR (内核地址空间布局随机化)

  • 基本概念:类似于用户态的ASLR,通过基地址+偏移的方式实现随机化
  • 未开启时的地址
    • 内核代码段基址:0xffffffff81000000
    • 直接映射区(direct mapping area)基址:0xffff888000000000

2. FGKASLR (函数粒度KASLR)

  • 特点:KASLR的增强版,以函数粒度重新排布内核代码
  • 实现方式
    • 将不同函数放到不同的节(section)上
    • 使用kernel_symbol结构体记录函数偏移:
      struct kernel_symbol {
          int value_offset;    // 函数偏移量
          int name_offset;     // 符号名称偏移量
          int namespace_offset; // 符号命名空间偏移量
      };
      
  • 查找函数地址
    • 通过__ksymtab段查找符号表
    • 使用__start___ksymtab__stop___ksymtab定义符号表范围
    • each_symbol_section函数遍历符号表段

3. STACK PROTECTOR

  • 功能:类似于用户态的canary,防止栈溢出攻击

4. SMAP/SMEP

  • SMAP(Supervisor Mode Access Prevention):防止内核态访问用户态数据
  • SMEP(Supervisor Mode Execution Prevention):防止内核态执行用户态代码
  • 绕过方式
    1. 篡改CR4寄存器(第20位控制SMEP开关)
    2. ret2dir攻击(利用内核线性映射区)

5. KPTI (内核页表隔离)

  • 原理:内核空间与用户空间使用两组不同的页表集

  • 未开启时的布局:内核和用户空间共用一个页全局目录表(PGD)

  • 开启后的变化

    • 内核页表和用户页表连续放置在8KB内存空间中
    • 用户页表只有少量内核代码映射(系统调用入口点等)
    • 用户地址空间在内核页表中不再有执行权限
  • 系统调用处理流程(以x86_64为例):

    • 通过entry_SYSCALL_64进入内核态
    • 使用SWITCH_TO_KERNEL_CR3切换到内核页表
    • 系统调用处理完成后使用SWITCH_TO_USER_CR3_STACK切换回用户页表
  • 绕过方式

    1. 使用gadget修改CR3寄存器,然后通过iretq/sysret返回
    2. 直接使用swapgs_restore_regs_and_return_to_usermode函数返回
    3. 使用signal(SIGSEGV, shell)捕获异常信号

二、关键攻击技术详解

1. ret2dir攻击

  • 原理:利用内核线性映射区对物理空间的完整映射

  • 特点:可以绕过SMEP/SMAP/PXN等隔离防护

  • 实现步骤

    1. 在用户空间喷射大量相同payload(mmap或堆喷)
    2. 随机挑选direct mapping area上的地址(高概率命中payload)
    3. 通过内核地址访问用户空间数据
  • 限制:高版本内核中direct mapping area没有可执行权限,需配合ROP

2. pt_regs结构利用

  • 背景:系统调用时会将所有寄存器压入内核栈,形成pt_regs结构
  • 结构定义(x86_64):
    struct pt_regs {
        unsigned long r15;
        unsigned long r14;
        unsigned long r13;
        unsigned long r12;
        unsigned long rbp;
        unsigned long rbx;
        unsigned long r11;
        unsigned long r10;
        unsigned long r9;
        unsigned long r8;
        unsigned long rax;
        unsigned long rcx;
        unsigned long rdx;
        unsigned long rsi;
        unsigned long rdi;
        // 其他寄存器...
    };
    
  • 利用方式
    • 当控制rip后,通过add rsp,0xn;ret调整栈指针
    • 利用pt_regs中残留的寄存器值构造ROP链

三、实战案例分析

案例1:MINI-LCTF2022 - kgadget

  • 保护机制:开启SMEP/SMAP,未开启KASLR

  • 漏洞点call [rdx](可控rbx)

  • 利用技术

    1. 利用pt_regs残留的r8、r9控制执行流
    2. physmap spray喷射提权payload
    3. 通过direct mapping area访问payload
  • 关键代码

    // physmap spray
    for(size_t i=0; i<25000; i++) {
        physmap[i] = mmap(0, page_size, PROT_READ|PROT_WRITE, 0x2|0x20, -1, 0);
        memcpy(physmap[i], payload, page_size);
    }
    
    // 控制执行流
    __asm__(
        "mov r9, rsp_ret;"
        "mov r8, try_hit;"
        // 其他寄存器设置...
        "syscall"
    );
    

案例2:长城杯京津冀2024初赛Kylin_driver

  • 漏洞点:栈溢出+bypass KPTI

  • 利用技术

    1. 泄露栈数据获取基地址和canary
    2. 使用swapgs_restore_regs_and_return_to_usermode返回用户态
    3. 信号处理获取root shell
  • ROP构造

    size_t swapgs_restore_regs_and_return_to_usermode = vmlinux_base + 0xc01026;
    qdata[idx++] = rdi_ret;
    qdata[idx++] = 0;
    qdata[idx++] = prepare_kernel_cred;
    qdata[idx++] = movRdiRax_ret;
    qdata[idx++] = commit_creds;
    qdata[idx++] = swapgs_restore_regs_and_return_to_usermode;
    // 其他寄存器设置...
    

案例3:hxpCTF 2020-kernel-rop

  • 挑战:绕过FGKASLR
  • 解决方案
    1. 在.text节范围内查找gadget
    2. 通过__ksymtab获取函数实际地址
    3. 分阶段构造ROP链

四、实用工具与技巧

  1. ROPgadget查找

    ROPgadget --binary vmlinux --opcode "4881c490000000"
    
  2. 寄存器状态调试

    __asm__(
        "mov r15, 0xbeefdead;"
        "mov r14, 0x11111111;"
        // 其他寄存器设置...
        "syscall"
    );
    
  3. 地址泄露检查

    void binary_dump(char *desc, void *addr, int len) {
        // 以16进制和ASCII格式打印内存内容
    }
    
  4. 状态保存

    void save_status() {
        __asm__(
            "mov user_cs, cs;"
            "pushf;"
            "pop user_rflags;"
            "mov user_sp, rsp;"
            "mov user_ss, ss;"
        );
    }
    

五、防御与绕过总结

保护机制 绕过技术 适用场景
KASLR 地址泄露、暴力破解 信息泄露漏洞
FGKASLR ksymtab查找、节内gadget 有符号表访问权限
SMEP/SMAP CR4篡改、ret2dir 控制流劫持
KPTI CR3修改、专用返回函数 系统调用路径
Stack Canary 泄露、不覆盖 栈溢出漏洞

通过深入理解这些保护机制的工作原理和绕过技术,可以有效地进行内核漏洞利用和防御方案设计。

Linux Kernel Pwn 从入门到精通(二):保护机制与绕过技术 一、内核保护机制详解 1. KASLR (内核地址空间布局随机化) 基本概念 :类似于用户态的ASLR,通过基地址+偏移的方式实现随机化 未开启时的地址 : 内核代码段基址: 0xffffffff81000000 直接映射区(direct mapping area)基址: 0xffff888000000000 2. FGKASLR (函数粒度KASLR) 特点 :KASLR的增强版,以函数粒度重新排布内核代码 实现方式 : 将不同函数放到不同的节(section)上 使用 kernel_symbol 结构体记录函数偏移: 查找函数地址 : 通过 __ksymtab 段查找符号表 使用 __start___ksymtab 和 __stop___ksymtab 定义符号表范围 each_symbol_section 函数遍历符号表段 3. STACK PROTECTOR 功能 :类似于用户态的canary,防止栈溢出攻击 4. SMAP/SMEP SMAP(Supervisor Mode Access Prevention) :防止内核态访问用户态数据 SMEP(Supervisor Mode Execution Prevention) :防止内核态执行用户态代码 绕过方式 : 篡改CR4寄存器(第20位控制SMEP开关) ret2dir攻击(利用内核线性映射区) 5. KPTI (内核页表隔离) 原理 :内核空间与用户空间使用两组不同的页表集 未开启时的布局 :内核和用户空间共用一个页全局目录表(PGD) 开启后的变化 : 内核页表和用户页表连续放置在8KB内存空间中 用户页表只有少量内核代码映射(系统调用入口点等) 用户地址空间在内核页表中不再有执行权限 系统调用处理流程 (以x86_ 64为例): 通过 entry_SYSCALL_64 进入内核态 使用 SWITCH_TO_KERNEL_CR3 切换到内核页表 系统调用处理完成后使用 SWITCH_TO_USER_CR3_STACK 切换回用户页表 绕过方式 : 使用gadget修改CR3寄存器,然后通过 iretq/sysret 返回 直接使用 swapgs_restore_regs_and_return_to_usermode 函数返回 使用 signal(SIGSEGV, shell) 捕获异常信号 二、关键攻击技术详解 1. ret2dir攻击 原理 :利用内核线性映射区对物理空间的完整映射 特点 :可以绕过SMEP/SMAP/PXN等隔离防护 实现步骤 : 在用户空间喷射大量相同payload(mmap或堆喷) 随机挑选direct mapping area上的地址(高概率命中payload) 通过内核地址访问用户空间数据 限制 :高版本内核中direct mapping area没有可执行权限,需配合ROP 2. pt_ regs结构利用 背景 :系统调用时会将所有寄存器压入内核栈,形成pt_ regs结构 结构定义 (x86_ 64): 利用方式 : 当控制rip后,通过 add rsp,0xn;ret 调整栈指针 利用pt_ regs中残留的寄存器值构造ROP链 三、实战案例分析 案例1:MINI-LCTF2022 - kgadget 保护机制 :开启SMEP/SMAP,未开启KASLR 漏洞点 : call [rdx] (可控rbx) 利用技术 : 利用pt_ regs残留的r8、r9控制执行流 physmap spray喷射提权payload 通过direct mapping area访问payload 关键代码 : 案例2:长城杯京津冀2024初赛Kylin_ driver 漏洞点 :栈溢出+bypass KPTI 利用技术 : 泄露栈数据获取基地址和canary 使用 swapgs_restore_regs_and_return_to_usermode 返回用户态 信号处理获取root shell ROP构造 : 案例3:hxpCTF 2020-kernel-rop 挑战 :绕过FGKASLR 解决方案 : 在.text节范围内查找gadget 通过 __ksymtab 获取函数实际地址 分阶段构造ROP链 四、实用工具与技巧 ROPgadget查找 : 寄存器状态调试 : 地址泄露检查 : 状态保存 : 五、防御与绕过总结 | 保护机制 | 绕过技术 | 适用场景 | |---------|---------|---------| | KASLR | 地址泄露、暴力破解 | 信息泄露漏洞 | | FGKASLR | ksymtab查找、节内gadget | 有符号表访问权限 | | SMEP/SMAP | CR4篡改、ret2dir | 控制流劫持 | | KPTI | CR3修改、专用返回函数 | 系统调用路径 | | Stack Canary | 泄露、不覆盖 | 栈溢出漏洞 | 通过深入理解这些保护机制的工作原理和绕过技术,可以有效地进行内核漏洞利用和防御方案设计。