2023年Pwn2Own温哥华PWN比赛上的Ubuntu提权漏洞攻击
字数 1544 2025-08-24 10:10:13
Linux内核nftables子系统提权漏洞(CVE-2023-35001)分析与利用
漏洞概述
CVE-2023-35001是Linux内核nftables子系统中的一个权限提升漏洞,该漏洞允许本地用户通过精心构造的nftables规则实现内核代码执行,从而获得root权限。该漏洞在2023年Pwn2Own Vancouver比赛中被利用来攻击Ubuntu系统。
漏洞背景
nftables简介
nftables是Linux内核中用于包过滤的子系统,实现了一个基于寄存器的虚拟机,包含以下核心组件:
- 表(Table): 链的容器,顶层对象
- 链(Chain): 包含要执行的规则列表,类似于函数
- 规则(Rule): 包含表达式列表,类似于语句
- 表达式(Expr): 最低级别的对象,虚拟机的实际指令
相关数据结构
struct nft_expr {
const struct nft_expr_ops *ops;
unsigned char data[] __attribute__((aligned(__alignof__(u64))));
};
struct nft_rule_dp {
u64 is_last:1, dlen:12, handle:42; /* for tracing */
unsigned char data[] __attribute__((aligned(__alignof__(struct nft_expr))));
};
struct nft_rule_blob {
unsigned long size;
unsigned char data[] __attribute__((aligned(__alignof__(struct nft_rule_dp))));
};
漏洞分析
漏洞位置
漏洞存在于nft_byteorder表达式的求值函数中,具体在net/netfilter/nft_byteorder.c文件的nft_byteorder_eval函数:
void nft_byteorder_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_byteorder *priv = nft_expr_priv(expr);
u32 *src = ®s->data[priv->sreg];
u32 *dst = ®s->data[priv->dreg];
union { u32 u32; u16 u16; } *s, *d; // [1]
s = (void *)src;
d = (void *)dst;
switch (priv->size) {
case 2:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 2; i++)
d[i].u16 = ntohs((__force __be16)s[i].u16); // [2]
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 2; i++)
d[i].u16 = (__force __u16)htons(s[i].u16); // [2]
break;
}
break;
// ... 其他case处理
}
}
漏洞原理
- 在[1]处定义了指向匿名联合的两个指针
s和d,联合的大小是其最大元素的大小(4字节) - 在处理2字节元素时([2]处),数组访问会在4字节上对齐,不考虑实际元素大小
- 这导致在处理16位元素时可能越界访问内存
漏洞利用限制
- 所有访问都是2字节宽
- 所有访问都需要在4字节上对齐
priv->length限制了溢出范围(数组之后的几十个字节)
漏洞利用
利用策略
- 泄漏
nf_tables.ko以计算表达式vtable地址 - 使用精心构造的表达式泄漏内核基址
- 使用精心构造的表达式编写ropchain并获得代码执行
利用步骤
1. 设置攻击模板
创建两条链:
- 基链:跳转到另一条链以填充jumpstack并将泄漏指针存储在集合中
- 泄漏链:包含泄漏和覆盖操作
利用过程:
- 用必需的表达式填充基链
- 用泄漏的有效payload填充泄漏链
- 向评估链发送数据包并恢复基链jumpstack指针
- 用覆盖payload替换泄漏链表达式
- 将数据包发送到评估链,损坏顶部jumpstack条目
2. 模块信息泄露
使用nftables执行跟踪机制泄漏信息:
- 设置基链包含:
nft_meta表达式启用跟踪- 跳转
leak_chain+verdict_return表达式
- 覆盖
rule指针指向verdict_returnops指针前两个字节 - 覆盖
last_rule指向新的rule指针后的8个字节
3. kASLR泄漏
使用精心构造的nft_byteorder表达式和大的源寄存器索引从堆栈中读取返回地址:
- 填充基链包含:
- 跳转到泄漏链
- 将构造的规则嵌入表达式中
- 将泄漏的指针写入集合的规则(
nft_dynset)
使用nft_range表达式构造假规则:
struct nft_range_expr {
struct nft_data data_from;
struct nft_data data_to;
u8 sreg;
u8 len;
enum nft_range_ops op:8;
};
4. 内核代码执行
使用ROP链实现代码执行:
- 精心制作假的
nft_payload表达式:
struct nft_payload {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
u8 dreg;
};
- ROP链执行:
- 在
sys_modify_ldt上调用set_memory_rw - 调用
copy_from_user_priv,将shellcode从用户区读取到sys_modify_ldt - shellcode执行
commit_creds(prepare_kernel_creds(0)) - 调用
do_task_dead挂起内核线程
- 在
防御措施
- 及时更新内核,应用补丁
- 限制非特权用户使用用户命名空间(
kernel.unprivileged_userns_clone) - 考虑禁用nftables或限制其使用