内核提权案例分析一
字数 3200 2025-08-30 06:50:35

内核提权技术深度解析

1. 修改cred结构体提权技术

1.1 基本原理

内核通过进程的task_struct结构体中的cred指针来索引cred结构体,根据cred内容判断进程权限。当cred结构体成员中的uid-fsgid都为0时,进程具有root权限。

1.2 两种实现方式

  1. 直接修改cred结构体内容:找到cred结构体并修改其权限字段
  2. 修改task_struct中的cred指针:使其指向一个伪造的具有root权限的cred结构体

1.3 定位技术

  1. 使用task_struct中的comm字段定位:
    • comm标记可执行文件名,位于cred正下方
    • 使用prctl设置进程comm为特殊字符串提高定位准确性
  2. 具体步骤:
    • 定位当前进程task_struct结构体地址
    • 根据cred指针相对于task_struct的偏移计算cred指针地址
    • 获取或修改cred地址

1.4 修改方法

  1. 修改为init_cred地址

    • cred指针指向内核镜像中已有的init_cred地址
    • 适用于能直接修改cred指针且知道init_cred地址的情况
  2. 伪造cred结构体

    • 创建伪造的cred结构体并修改指针指向它
    • 实现复杂,一般不常用

1.5 UAF利用方法(已过时)

  1. 典型利用流程:

    • 申请与cred结构体大小相同的堆块
    • 释放该堆块
    • fork新进程,恰好使用刚释放的堆块
    • 修改cred结构体特定内存实现提权
  2. 限制:

    • 较新内核(v4.5+)中cred_jar设置了SLAB_ACCOUNT标记
    • 默认开启CONFIG_MEMCG_KMEM时不再与kmalloc-192合并

2. commit_creds函数提权技术

2.1 commit_creds(&init_cred)

  1. 原理:

    • commit_creds()函数将新cred设为当前进程task_structreal_credcred字段
    • init_cred是init进程的静态定义cred结构体,具有root权限
  2. 使用方法:

    commit_creds(&init_cred);
    

2.2 commit_creds(prepare_kernel_cred(NULL))

  1. 原理:

    • prepare_kernel_cred()函数拷贝指定进程的cred结构体
    • 参数为NULL时拷贝init_cred并返回root权限的cred
  2. 使用方法:

    commit_creds(prepare_kernel_cred(NULL));
    
  3. 限制:

    • 内核v6.2+后prepare_kernel_cred(NULL)将返回NULL
    • 不再适用于v6.2+内核

3. 内核ROP技术

3.1 基本原理

  1. 与用户态ROP类似,但使用内核中的gadget
  2. 目标ropchain:
    • commit_creds(&init_cred)
    • commit_creds(prepare_kernel_cred(NULL))

3.2 状态保存

  1. 需要手动模拟用户态进入内核态的准备工作
  2. 保存各寄存器值到内核栈上,便于后续返回用户态
  3. 常用保存函数(使用内联汇编):
    unsigned long user_cs, user_ss, user_rflags, user_sp;
    
    void save_status()
    {
        __asm__("mov user_cs, cs;"
                "mov user_ss, ss;"
                "mov user_sp, rsp;"
                "pushf;"
                "pop user_rflags;");
    }
    

3.3 返回用户态

  1. 必要步骤:

    • swapgs指令恢复用户态GS寄存器
    • sysretqiretq返回到用户空间
  2. 区别:

    • sysretqiretq操作不相同
    • 通常使用iretq更可靠

4. 案例分析:CISCN2017_babydriver

4.1 漏洞分析

  1. 驱动初始化:

    • 注册字符设备/dev/babydev
    • 定义操作函数:babyreadbabywritebabyioctlbabyopenbabyrelease
  2. 漏洞点:

    • babyrelease释放全局变量指向空间但未清零
    • 打开两个/dev/babydev设备并关闭一个会造成UAF

4.2 利用思路

  1. 利用UAF劫持控制流:

    • 执行内核gadget修改SMEP属性
    • 使用ret2usr提权
  2. SMEP关闭方法:

    • 向CR4寄存器写入0x6f0
    • 将第21位覆盖为0

4.3 利用技术细节

  1. 关键结构体:

    • tty_structtty_operations
    • 包含多个函数指针,利于构造ROP
  2. 结构体创建:

    • open("/dev/ptmx", O_RDWR)创建tty_struct结构体
    • 大小为0x2e0
  3. 控制流劫持步骤:

    • 伪造tty_operations结构体
    • 修改tty_struct中的ops指针指向伪造结构体
    • 操作设备时调用伪造的函数指针

4.4 调试分析

  1. UAF实现:

    • 打开两个/dev/babydev设备
    • 关闭一个造成UAF
    • 打开/dev/ptmx使tty_struct使用释放的内存
  2. 控制流劫持:

    • 使用babyread/babywrite操作tty_struct
    • 覆盖ops指针指向用户态伪造结构体
    • 调用write触发控制流转移
  3. 栈迁移:

    • 利用mov rsp, rax等gadget将栈迁移到用户空间
    • 构造ROP链关闭SMEP并提权

4.5 提权执行流程

  1. 提权函数:

    void privilege_escalate() {
        commit_creds(prepare_kernel_cred(NULL));
    }
    
  2. 返回用户态:

    • swapgs切换GS寄存器
    • iretq恢复用户态寄存器
    • 执行system("/bin/sh")获取root shell

5. 案例分析:qwb_2018_core

5.1 环境配置

  1. 内核保护机制:

    • kptr_restrict控制内核符号地址可见性
    • dmesg_restrict控制内核日志访问权限
  2. 符号地址获取:

    • /tmp/kallsyms读取commit_credsprepare_kernel_cred地址
    • 计算内核加载偏移量

5.2 漏洞分析

  1. 内核模块功能:

    • 创建/proc/core条目
    • 定义操作:core_writecore_ioctlcore_release
  2. 漏洞点:

    • ioctl0x6677889C0x6677889B操作可泄漏canary
    • core_write配合0x6677889A操作造成内核栈溢出

5.3 利用技术

  1. Canary泄漏:

    • 设置大偏移量读取内核栈布局
    • 通过core_read将canary读到用户空间
  2. 栈溢出利用:

    • 使用core_write构造ROP链写入name
    • 通过core_ioctl0x6677889A操作触发溢出
    • 使用负数绕过core_copy_func中的条件检查
  3. ROP链构造:

    • 覆盖返回地址执行提权代码
    • 返回用户态启动shell

6. 关键防护机制与绕过

6.1 SMEP/SMAP防护

  1. SMEP(Supervisor Mode Execution Protection):

    • 阻止内核态执行用户空间代码
    • 通过CR4寄存器的第20位控制
  2. 绕过方法:

    • 修改CR4寄存器关闭SMEP
    • 使用内核ROP技术

6.2 KASLR防护

  1. 内核地址空间布局随机化
  2. 绕过方法:
    • 通过信息泄漏获取内核基址
    • 计算函数和gadget的实际地址

6.3 Stack Canary防护

  1. 内核栈保护机制
  2. 绕过方法:
    • 信息泄漏获取canary值
    • 在溢出时正确覆盖canary

7. 开发技巧与最佳实践

  1. 调试技巧:

    • 使用gdb配合vmlinux调试内核
    • 设置断点分析执行流程
  2. 利用开发:

    • 编写可靠的内核态/用户态切换代码
    • 处理不同内核版本差异
  3. 稳定性考虑:

    • 确保ROP链在不同环境下的可靠性
    • 处理竞争条件和时序问题

8. 总结与演进

  1. 技术演进:

    • 新版本内核增加了更多防护机制
    • 旧技术可能失效,需要持续研究新方法
  2. 防御建议:

    • 及时更新内核版本
    • 启用所有可用防护机制
    • 监控可疑的内核行为
  3. 研究方向:

    • 新型内核漏洞利用技术
    • 绕过最新防护机制的方法
    • 更稳定的提权技术
内核提权技术深度解析 1. 修改cred结构体提权技术 1.1 基本原理 内核通过进程的 task_struct 结构体中的 cred 指针来索引 cred 结构体,根据 cred 内容判断进程权限。当 cred 结构体成员中的 uid-fsgid 都为0时,进程具有root权限。 1.2 两种实现方式 直接修改cred结构体内容 :找到cred结构体并修改其权限字段 修改task_ struct中的cred指针 :使其指向一个伪造的具有root权限的cred结构体 1.3 定位技术 使用 task_struct 中的 comm 字段定位: comm 标记可执行文件名,位于 cred 正下方 使用 prctl 设置进程 comm 为特殊字符串提高定位准确性 具体步骤: 定位当前进程 task_struct 结构体地址 根据 cred 指针相对于 task_struct 的偏移计算 cred 指针地址 获取或修改 cred 地址 1.4 修改方法 修改为init_ cred地址 : 将 cred 指针指向内核镜像中已有的 init_cred 地址 适用于能直接修改 cred 指针且知道 init_cred 地址的情况 伪造cred结构体 : 创建伪造的cred结构体并修改指针指向它 实现复杂,一般不常用 1.5 UAF利用方法(已过时) 典型利用流程: 申请与cred结构体大小相同的堆块 释放该堆块 fork新进程,恰好使用刚释放的堆块 修改cred结构体特定内存实现提权 限制: 较新内核(v4.5+)中 cred_jar 设置了 SLAB_ACCOUNT 标记 默认开启 CONFIG_MEMCG_KMEM 时不再与 kmalloc-192 合并 2. commit_ creds函数提权技术 2.1 commit_ creds(&init_ cred) 原理: commit_creds() 函数将新cred设为当前进程 task_struct 的 real_cred 与 cred 字段 init_cred 是init进程的静态定义cred结构体,具有root权限 使用方法: 2.2 commit_ creds(prepare_ kernel_ cred(NULL)) 原理: prepare_kernel_cred() 函数拷贝指定进程的cred结构体 参数为NULL时拷贝 init_cred 并返回root权限的cred 使用方法: 限制: 内核v6.2+后 prepare_kernel_cred(NULL) 将返回NULL 不再适用于v6.2+内核 3. 内核ROP技术 3.1 基本原理 与用户态ROP类似,但使用内核中的gadget 目标ropchain: commit_creds(&init_cred) 或 commit_creds(prepare_kernel_cred(NULL)) 3.2 状态保存 需要手动模拟用户态进入内核态的准备工作 保存各寄存器值到内核栈上,便于后续返回用户态 常用保存函数(使用内联汇编): 3.3 返回用户态 必要步骤: swapgs 指令恢复用户态GS寄存器 sysretq 或 iretq 返回到用户空间 区别: sysretq 和 iretq 操作不相同 通常使用 iretq 更可靠 4. 案例分析:CISCN2017_ babydriver 4.1 漏洞分析 驱动初始化: 注册字符设备 /dev/babydev 定义操作函数: babyread 、 babywrite 、 babyioctl 、 babyopen 、 babyrelease 漏洞点: babyrelease 释放全局变量指向空间但未清零 打开两个 /dev/babydev 设备并关闭一个会造成UAF 4.2 利用思路 利用UAF劫持控制流: 执行内核gadget修改SMEP属性 使用ret2usr提权 SMEP关闭方法: 向CR4寄存器写入 0x6f0 将第21位覆盖为0 4.3 利用技术细节 关键结构体: tty_struct 和 tty_operations 包含多个函数指针,利于构造ROP 结构体创建: open("/dev/ptmx", O_RDWR) 创建 tty_struct 结构体 大小为 0x2e0 控制流劫持步骤: 伪造 tty_operations 结构体 修改 tty_struct 中的 ops 指针指向伪造结构体 操作设备时调用伪造的函数指针 4.4 调试分析 UAF实现: 打开两个 /dev/babydev 设备 关闭一个造成UAF 打开 /dev/ptmx 使 tty_struct 使用释放的内存 控制流劫持: 使用 babyread / babywrite 操作 tty_struct 覆盖 ops 指针指向用户态伪造结构体 调用 write 触发控制流转移 栈迁移: 利用 mov rsp, rax 等gadget将栈迁移到用户空间 构造ROP链关闭SMEP并提权 4.5 提权执行流程 提权函数: 返回用户态: swapgs 切换GS寄存器 iretq 恢复用户态寄存器 执行 system("/bin/sh") 获取root shell 5. 案例分析:qwb_ 2018_ core 5.1 环境配置 内核保护机制: kptr_restrict 控制内核符号地址可见性 dmesg_restrict 控制内核日志访问权限 符号地址获取: 从 /tmp/kallsyms 读取 commit_creds 和 prepare_kernel_cred 地址 计算内核加载偏移量 5.2 漏洞分析 内核模块功能: 创建 /proc/core 条目 定义操作: core_write 、 core_ioctl 、 core_release 漏洞点: ioctl 的 0x6677889C 和 0x6677889B 操作可泄漏canary core_write 配合 0x6677889A 操作造成内核栈溢出 5.3 利用技术 Canary泄漏: 设置大偏移量读取内核栈布局 通过 core_read 将canary读到用户空间 栈溢出利用: 使用 core_write 构造ROP链写入 name 通过 core_ioctl 的 0x6677889A 操作触发溢出 使用负数绕过 core_copy_func 中的条件检查 ROP链构造: 覆盖返回地址执行提权代码 返回用户态启动shell 6. 关键防护机制与绕过 6.1 SMEP/SMAP防护 SMEP(Supervisor Mode Execution Protection): 阻止内核态执行用户空间代码 通过CR4寄存器的第20位控制 绕过方法: 修改CR4寄存器关闭SMEP 使用内核ROP技术 6.2 KASLR防护 内核地址空间布局随机化 绕过方法: 通过信息泄漏获取内核基址 计算函数和gadget的实际地址 6.3 Stack Canary防护 内核栈保护机制 绕过方法: 信息泄漏获取canary值 在溢出时正确覆盖canary 7. 开发技巧与最佳实践 调试技巧: 使用 gdb 配合 vmlinux 调试内核 设置断点分析执行流程 利用开发: 编写可靠的内核态/用户态切换代码 处理不同内核版本差异 稳定性考虑: 确保ROP链在不同环境下的可靠性 处理竞争条件和时序问题 8. 总结与演进 技术演进: 新版本内核增加了更多防护机制 旧技术可能失效,需要持续研究新方法 防御建议: 及时更新内核版本 启用所有可用防护机制 监控可疑的内核行为 研究方向: 新型内核漏洞利用技术 绕过最新防护机制的方法 更稳定的提权技术