CVE-2023-32233 Linux 内核 UAF 漏洞分析与利用
字数 2232 2025-08-18 11:36:00

Linux 内核 nftable 模块 UAF 漏洞分析与利用教学文档

漏洞概述

CVE-2023-32233 是 Linux 内核 nftable 模块中的一个 use-after-free (UAF) 漏洞,存在于处理匿名 set 时。该漏洞允许本地攻击者提升权限,影响 Linux 内核版本至 6.1。

漏洞成因

漏洞的根本原因是 nf_tables_deactivate_set 函数在释放匿名 set 时没有将 set 标记为 inactive,导致它仍能被同一 netlink 批处理中的其他任务访问,从而造成 UAF。

前置知识

Netlink 批处理机制

用户态进程可以一次提交多个 netlink 请求给内核,这些请求在内存中按顺序存储,请求的存储结构为 struct nlmsghdr

用户态填充和发送请求的基本代码结构:

struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
table_seq = seq;
mnl_nlmsg_batch_next(batch);

struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
    mnl_nlmsg_batch_current(batch),
    NFT_MSG_NEWSETELEM,
    NFPROTO_INET,
    NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
    seq++);
nftnl_set_elems_nlmsg_build_payload(nlh, set);
mnl_nlmsg_batch_next(batch);

if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) {
    err(1, "Cannot into mnl_socket_sendto()");
}
mnl_nlmsg_batch_stop(batch);

内核处理流程

netlink 批处理消息的处理涉及两个线程:

  1. nfnetlink_rcv_batch - 在进程的系统调用上下文中执行对请求的初步处理
  2. nf_tables_trans_destroy_work - 内核线程,完成最终的销毁工作

关键处理流程:

  1. nfnetlink_rcv_batch 遍历批处理中的每个请求
  2. 根据请求类型查找对应的处理函数(nf_tables_cb 回调函数表)
  3. 处理函数分配 trans 对象并将其放到 commit_list
  4. 处理完所有请求后调用 nf_tables_commit 进行第二次处理
  5. 最后通过 nf_tables_commit_releasecommit_list 转移到 nf_tables_destroy_list
  6. 调度 nf_tables_trans_destroy_work 线程完成最终处理

漏洞触发步骤

  1. 创建一个匿名 set (pwn_lookup_set)并插入一个 elem
  2. 创建一个 rule,其中包含一个 lookup expr,引用 pwn_lookup_set
  3. 创建一个批处理包含两个请求:
    • NFT_MSG_DELRULE 删除上一步创建的 rule
    • NFT_MSG_DELSETELEM 删除 pwn_lookup_set 的 elem

内存状态变化:

  1. 处理 NFT_MSG_DELRULE 时会释放 rule 及其 expr
  2. nft_lookup_destroy 中释放匿名 set
  3. 处理 NFT_MSG_DELSETELEM 时访问已释放的 set

补丁分析

补丁地址:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c1592a89942e9678f7d9c8030efa777c0d57edab

关键修改:

void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
                             struct nft_set_binding *binding, enum nft_trans_phase phase)
{
    switch (phase) {
    case NFT_TRANS_PREPARE:
+       if (nft_set_is_anonymous(set))
+           nft_deactivate_next(ctx->net, set);
+       set->use--;
        return;
    case NFT_TRANS_ABORT:

补丁在 NFT_TRANS_PREPARE 阶段将匿名 set 标记为 inactive,防止后续请求访问。

漏洞利用

越界销毁 expr

利用步骤:

  1. 设置 set->ops->elemsize = 8
  2. 触发 free 后使用 set->ops->elemsize = 0x10 的 set 占位
  3. 导致 nft_set_elem_ext 计算出的 ext 发生 8 字节错位
  4. 控制 ext->offset[NFT_SET_EXT_EXPRESSIONS] 越界释放 expr

稳定占位 UAF 对象

挑战:

  1. 内核线程调度不可控
  2. free 和 use 点之间的时间窗小

解决方案:

  1. 使用死循环线程占位 CPU(1,2,3),提高内核线程调度到 CPU 0 的概率
  2. 利用 nft_commit_release 中的循环删除特性增大时间窗

具体做法:

  1. 下发三个请求:
    • NFT_MSG_DELRULE 删除 rule 及引用的匿名 set
    • NFT_MSG_DELSET 删除包含大量 elem 的 delay_set
    • NFT_MSG_DELSETELEM 引用被释放的匿名 set
  2. 在释放 delay_set 时进行用户态堆喷占位

堆风水布局

  1. elem 分配使用 GFP_KERNEL_ACCOUNT,大小可控
  2. log expr 通过 newrule 请求分配,嵌入到 rule 内存中
  3. elem 前后喷几十个 rule 确保被包围

任意地址读写

转换步骤:

  1. 将漏洞转换为 nft_log->prefix 的 UAF
  2. 使用 nft_object->udata 占位
  3. 通过 nft_log 释放 prefix,转换为 udata 的 UAF
  4. 使用 nft_dynset_new 分配 nft_counternft_quota 占位 udata
  5. 读取 udata 泄露内核地址
  6. 修改 nft_quota->consumed 实现任意地址读写
  7. 修改 modprobe 提权

总结

漏洞挖掘启示

  1. 关注匿名 set 等特殊对象的处理逻辑
  2. 重点审计特判逻辑场景
  3. 熟悉对象管理和请求处理流程

漏洞利用技巧

  1. 类型混淆转换为更易利用的原语
  2. CPU 占位控制内核线程调度
  3. 利用程序循环增大 RACE 窗口
  4. 精心设计堆风水布局
  5. 多阶段漏洞转换提升利用可靠性
Linux 内核 nftable 模块 UAF 漏洞分析与利用教学文档 漏洞概述 CVE-2023-32233 是 Linux 内核 nftable 模块中的一个 use-after-free (UAF) 漏洞,存在于处理匿名 set 时。该漏洞允许本地攻击者提升权限,影响 Linux 内核版本至 6.1。 漏洞成因 漏洞的根本原因是 nf_tables_deactivate_set 函数在释放匿名 set 时没有将 set 标记为 inactive,导致它仍能被同一 netlink 批处理中的其他任务访问,从而造成 UAF。 前置知识 Netlink 批处理机制 用户态进程可以一次提交多个 netlink 请求给内核,这些请求在内存中按顺序存储,请求的存储结构为 struct nlmsghdr 。 用户态填充和发送请求的基本代码结构: 内核处理流程 netlink 批处理消息的处理涉及两个线程: nfnetlink_rcv_batch - 在进程的系统调用上下文中执行对请求的初步处理 nf_tables_trans_destroy_work - 内核线程,完成最终的销毁工作 关键处理流程: nfnetlink_rcv_batch 遍历批处理中的每个请求 根据请求类型查找对应的处理函数( nf_tables_cb 回调函数表) 处理函数分配 trans 对象并将其放到 commit_list 中 处理完所有请求后调用 nf_tables_commit 进行第二次处理 最后通过 nf_tables_commit_release 将 commit_list 转移到 nf_tables_destroy_list 调度 nf_tables_trans_destroy_work 线程完成最终处理 漏洞触发步骤 创建一个匿名 set ( pwn_lookup_set )并插入一个 elem 创建一个 rule,其中包含一个 lookup expr,引用 pwn_lookup_set 创建一个批处理包含两个请求: NFT_MSG_DELRULE 删除上一步创建的 rule NFT_MSG_DELSETELEM 删除 pwn_lookup_set 的 elem 内存状态变化: 处理 NFT_MSG_DELRULE 时会释放 rule 及其 expr 在 nft_lookup_destroy 中释放匿名 set 处理 NFT_MSG_DELSETELEM 时访问已释放的 set 补丁分析 补丁地址:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c1592a89942e9678f7d9c8030efa777c0d57edab 关键修改: 补丁在 NFT_TRANS_PREPARE 阶段将匿名 set 标记为 inactive,防止后续请求访问。 漏洞利用 越界销毁 expr 利用步骤: 设置 set->ops->elemsize = 8 触发 free 后使用 set->ops->elemsize = 0x10 的 set 占位 导致 nft_set_elem_ext 计算出的 ext 发生 8 字节错位 控制 ext->offset[NFT_SET_EXT_EXPRESSIONS] 越界释放 expr 稳定占位 UAF 对象 挑战: 内核线程调度不可控 free 和 use 点之间的时间窗小 解决方案: 使用死循环线程占位 CPU(1,2,3),提高内核线程调度到 CPU 0 的概率 利用 nft_commit_release 中的循环删除特性增大时间窗 具体做法: 下发三个请求: NFT_MSG_DELRULE 删除 rule 及引用的匿名 set NFT_MSG_DELSET 删除包含大量 elem 的 delay_set NFT_MSG_DELSETELEM 引用被释放的匿名 set 在释放 delay_set 时进行用户态堆喷占位 堆风水布局 elem 分配使用 GFP_KERNEL_ACCOUNT ,大小可控 log expr 通过 newrule 请求分配,嵌入到 rule 内存中 在 elem 前后喷几十个 rule 确保被包围 任意地址读写 转换步骤: 将漏洞转换为 nft_log->prefix 的 UAF 使用 nft_object->udata 占位 通过 nft_log 释放 prefix ,转换为 udata 的 UAF 使用 nft_dynset_new 分配 nft_counter 和 nft_quota 占位 udata 读取 udata 泄露内核地址 修改 nft_quota->consumed 实现任意地址读写 修改 modprobe 提权 总结 漏洞挖掘启示 关注匿名 set 等特殊对象的处理逻辑 重点审计特判逻辑场景 熟悉对象管理和请求处理流程 漏洞利用技巧 类型混淆转换为更易利用的原语 CPU 占位控制内核线程调度 利用程序循环增大 RACE 窗口 精心设计堆风水布局 多阶段漏洞转换提升利用可靠性