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

Linux Kernel Exploitation 从入门到精通(三):堆利用与结构体攻击


一、内核内存分配基础

  1. 核心分配函数

    void *kmalloc(size_t size, gfp_t gfp);
    
    • 关键参数
      • size:分配大小,决定落入的SLUB缓存(如kmalloc-64)。
      • gfp:内存分配标志,控制分配行为:
        • GFP_KERNEL:允许阻塞,可触发内存回收(最常见)。
        • GFP_KERNEL_ACCOUNT:计入内存控制组账单(隔离kmalloc-cg-xx缓存)。
        • GFP_HARDWALL:限制为当前NUMA节点分配。
        • GFP_NOWARN:禁止输出分配失败警告。
  2. SLUB分配器机制

    • 三级缓存结构kmem_cache_cpu(每CPU快速通道)→ kmem_cache_node(Partial链表)→ Buddy System(底层页分配)。
    • 分配流程
      1. 优先从当前CPU的kmem_cache_cpu->freelist获取对象。
      2. 若为空,从kmem_cache_node->partial链表补充Slab页。
      3. 仍不足则向Buddy System申请新页。
    • 释放流程:对象按优先级插入CPU本地链表、Partial链表或释放回Buddy。
  3. 多核绑定优化
    通过sched_setaffinity绑定进程到单一CPU核心,避免跨核竞争:

    void bind_cpu(int core) {
        cpu_set_t cpu_set;
        CPU_ZERO(&cpu_set);
        CPU_SET(core, &cpu_set);
        sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
    }
    

二、关键结构体利用:tty终端

  1. tty结构体概述

    • 分配路径open("/dev/ptmx")alloc_tty_struct()(大小固定为0x2e0,位于kmalloc-1kkmalloc-cg-1k)。
    • 关键字段
      struct tty_struct {
          int magic;                      // 0x5401(标识符)
          const struct tty_operations *ops; // 函数指针表(攻击目标)
          // ...其他字段省略...
      };
      
  2. tty_operations劫持

    • 函数表结构
      struct tty_operations {
          int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
          int (*write)(struct tty_struct *tty, const unsigned char *buf, int count);
          // ...其他函数指针...
      };
      
    • 利用步骤
      1. UAF漏洞:释放后未清零的tty对象被重新分配。
      2. 伪造ops表:覆盖tty->ops指向用户可控内存,填充恶意函数指针(如ioctl)。
      3. 触发调用:通过ioctl(tty_fd, cmd, arg)劫持控制流。
  3. 调试技巧

    • 使用pahole工具分析结构体布局:
      pahole -C tty_struct /path/to/vmlinux
      
    • 若劫持失败,可通过内核报错日志定位调用的函数指针偏移。

三、高级利用技术:userfaultfd

  1. 机制原理

    • 流程
      1. 注册缺页处理:用户空间监控指定内存区域的页面故障。
      2. 触发缺页:如copy_from_user访问未映射的地址。
      3. 异步处理:内核暂停当前线程,转由用户态处理缺页事件。
  2. 利用场景

    • 条件竞争:在缺页处理期间操作内核对象(如释放堆块并重新占用)。
    • 典型流程
      // 1. 注册userfaultfd监控匿名映射区
      Register_Userfalutfd(anon_mem, size, handler);
      
      // 2. 触发缺页(如copy_from_user写入anon_mem)
      // 3. 在handler中释放原堆块,并分配目标结构体(如tty)
      // 4. 恢复缺页处理,覆写目标结构体
      
  3. 代码模板

    void Userfault_Handler(int uffd) {
        struct uffdio_copy ufcopy;
        read(uffd, &msg, sizeof(msg)); // 接收缺页事件
        sem_post(&sem[0]);             // 同步信号量
    
        // 在缺页期间执行堆操作
        ufcopy.src = fake_data;        // 伪造数据
        ufcopy.dst = fault_addr;       // 缺页地址
        ioctl(uffd, UFFDIO_COPY, &ufcopy); // 恢复执行并覆写
    }
    

四、实战案例解析

  1. CISCN2017-babydriver

    • 漏洞:全局变量UAF,无锁的读写操作。
    • 利用链
      1. 通过双fd触发UAF,控制释放的kmalloc-1k对象。
      2. 分配tty结构体占用空洞,伪造ops->ioctl
      3. 执行ROP链提权(commit_creds(prepare_kernel_cred(0)))。
  2. SCTF2024-kno_puts_revenge

    • 关键点
      • 通过CVE泄露内核基址。
      • 结合userfaultfd实现精确堆占位。
      • 利用push rdx; pop rsp等gadget构造栈迁移。
  3. 2024-akernel

    • 地址泄露:通过cpu_entry_area固定地址绕过KASLR。
    • 多阶段利用
      1. 泄露驱动基址 → 获取堆地址。
      2. userfaultfd暂停写入,置换为tty对象。
      3. 劫持控制流至ROP。

五、防御与绕过

  • 常见防护
    • CONFIG_MEMCG_KMEM:隔离GFP_KERNELGFP_KERNEL_ACCOUNT缓存。
    • CONFIG_SLAB_FREELIST_HARDENED:保护SLUB freelist。
  • 绕过思路
    • 利用跨缓存类型混淆(如通过kmalloc-cg污染kmalloc)。
    • 结合其他漏洞(如地址泄露)绕过KASLR。

附录

  • 工具推荐crash分析内核转储,slabinfo查看SLUB状态。
  • 延伸阅读:Linux内核源码mm/slub.cdrivers/tty/tty_io.c

(完)

Linux Kernel Exploitation 从入门到精通(三):堆利用与结构体攻击 一、内核内存分配基础 核心分配函数 关键参数 : size :分配大小,决定落入的SLUB缓存(如 kmalloc-64 )。 gfp :内存分配标志,控制分配行为: GFP_KERNEL :允许阻塞,可触发内存回收(最常见)。 GFP_KERNEL_ACCOUNT :计入内存控制组账单(隔离 kmalloc-cg-xx 缓存)。 GFP_HARDWALL :限制为当前NUMA节点分配。 GFP_NOWARN :禁止输出分配失败警告。 SLUB分配器机制 三级缓存结构 : kmem_cache_cpu (每CPU快速通道)→ kmem_cache_node (Partial链表)→ Buddy System(底层页分配)。 分配流程 : 优先从当前CPU的 kmem_cache_cpu->freelist 获取对象。 若为空,从 kmem_cache_node->partial 链表补充Slab页。 仍不足则向Buddy System申请新页。 释放流程 :对象按优先级插入CPU本地链表、Partial链表或释放回Buddy。 多核绑定优化 通过 sched_setaffinity 绑定进程到单一CPU核心,避免跨核竞争: 二、关键结构体利用:tty终端 tty结构体概述 分配路径 : open("/dev/ptmx") → alloc_tty_struct() (大小固定为 0x2e0 ,位于 kmalloc-1k 或 kmalloc-cg-1k )。 关键字段 : tty_ operations劫持 函数表结构 : 利用步骤 : UAF漏洞 :释放后未清零的tty对象被重新分配。 伪造ops表 :覆盖 tty->ops 指向用户可控内存,填充恶意函数指针(如 ioctl )。 触发调用 :通过 ioctl(tty_fd, cmd, arg) 劫持控制流。 调试技巧 使用 pahole 工具分析结构体布局: 若劫持失败,可通过内核报错日志定位调用的函数指针偏移。 三、高级利用技术:userfaultfd 机制原理 流程 : 注册缺页处理 :用户空间监控指定内存区域的页面故障。 触发缺页 :如 copy_from_user 访问未映射的地址。 异步处理 :内核暂停当前线程,转由用户态处理缺页事件。 利用场景 条件竞争 :在缺页处理期间操作内核对象(如释放堆块并重新占用)。 典型流程 : 代码模板 四、实战案例解析 CISCN2017-babydriver 漏洞 :全局变量UAF,无锁的读写操作。 利用链 : 通过双fd触发UAF,控制释放的 kmalloc-1k 对象。 分配tty结构体占用空洞,伪造 ops->ioctl 。 执行ROP链提权( commit_creds(prepare_kernel_cred(0)) )。 SCTF2024-kno_ puts_ revenge 关键点 : 通过CVE泄露内核基址。 结合userfaultfd实现精确堆占位。 利用 push rdx; pop rsp 等gadget构造栈迁移。 2024-akernel 地址泄露 :通过 cpu_entry_area 固定地址绕过KASLR。 多阶段利用 : 泄露驱动基址 → 获取堆地址。 userfaultfd暂停写入,置换为tty对象。 劫持控制流至ROP。 五、防御与绕过 常见防护 : CONFIG_MEMCG_KMEM :隔离 GFP_KERNEL 与 GFP_KERNEL_ACCOUNT 缓存。 CONFIG_SLAB_FREELIST_HARDENED :保护SLUB freelist。 绕过思路 : 利用跨缓存类型混淆(如通过 kmalloc-cg 污染 kmalloc )。 结合其他漏洞(如地址泄露)绕过KASLR。 附录 : 工具推荐 : crash 分析内核转储, slabinfo 查看SLUB状态。 延伸阅读 :Linux内核源码 mm/slub.c 、 drivers/tty/tty_io.c 。 (完)