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 批处理消息的处理涉及两个线程:
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删除上一步创建的 ruleNFT_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
关键修改:
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
利用步骤:
- 设置
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 及引用的匿名 setNFT_MSG_DELSET删除包含大量 elem 的delay_setNFT_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 窗口
- 精心设计堆风水布局
- 多阶段漏洞转换提升利用可靠性