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 = &regs->data[priv->sreg];
    u32 *dst = &regs->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. 在[1]处定义了指向匿名联合的两个指针sd,联合的大小是其最大元素的大小(4字节)
  2. 在处理2字节元素时([2]处),数组访问会在4字节上对齐,不考虑实际元素大小
  3. 这导致在处理16位元素时可能越界访问内存

漏洞利用限制

  1. 所有访问都是2字节宽
  2. 所有访问都需要在4字节上对齐
  3. priv->length限制了溢出范围(数组之后的几十个字节)

漏洞利用

利用策略

  1. 泄漏nf_tables.ko以计算表达式vtable地址
  2. 使用精心构造的表达式泄漏内核基址
  3. 使用精心构造的表达式编写ropchain并获得代码执行

利用步骤

1. 设置攻击模板

创建两条链:

  • 基链:跳转到另一条链以填充jumpstack并将泄漏指针存储在集合中
  • 泄漏链:包含泄漏和覆盖操作

利用过程:

  1. 用必需的表达式填充基链
  2. 用泄漏的有效payload填充泄漏链
  3. 向评估链发送数据包并恢复基链jumpstack指针
  4. 用覆盖payload替换泄漏链表达式
  5. 将数据包发送到评估链,损坏顶部jumpstack条目

2. 模块信息泄露

使用nftables执行跟踪机制泄漏信息:

  1. 设置基链包含:
    • nft_meta表达式启用跟踪
    • 跳转leak_chain + verdict_return表达式
  2. 覆盖rule指针指向verdict_return ops指针前两个字节
  3. 覆盖last_rule指向新的rule指针后的8个字节

3. kASLR泄漏

使用精心构造的nft_byteorder表达式和大的源寄存器索引从堆栈中读取返回地址:

  1. 填充基链包含:
    • 跳转到泄漏链
    • 将构造的规则嵌入表达式中
    • 将泄漏的指针写入集合的规则(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链实现代码执行:

  1. 精心制作假的nft_payload表达式:
struct nft_payload {
    enum nft_payload_bases base:8;
    u8 offset;
    u8 len;
    u8 dreg;
};
  1. 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挂起内核线程

防御措施

  1. 及时更新内核,应用补丁
  2. 限制非特权用户使用用户命名空间(kernel.unprivileged_userns_clone)
  3. 考虑禁用nftables或限制其使用

参考资源

  1. 攻击代码EXP
  2. 补丁Patch
Linux内核nftables子系统提权漏洞(CVE-2023-35001)分析与利用 漏洞概述 CVE-2023-35001是Linux内核nftables子系统中的一个权限提升漏洞,该漏洞允许本地用户通过精心构造的nftables规则实现内核代码执行,从而获得root权限。该漏洞在2023年Pwn2Own Vancouver比赛中被利用来攻击Ubuntu系统。 漏洞背景 nftables简介 nftables是Linux内核中用于包过滤的子系统,实现了一个基于寄存器的虚拟机,包含以下核心组件: 表(Table) : 链的容器,顶层对象 链(Chain) : 包含要执行的规则列表,类似于函数 规则(Rule) : 包含表达式列表,类似于语句 表达式(Expr) : 最低级别的对象,虚拟机的实际指令 相关数据结构 漏洞分析 漏洞位置 漏洞存在于 nft_byteorder 表达式的求值函数中,具体在 net/netfilter/nft_byteorder.c 文件的 nft_byteorder_eval 函数: 漏洞原理 在[ 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_return ops指针前两个字节 覆盖 last_rule 指向新的 rule 指针后的8个字节 3. kASLR泄漏 使用精心构造的 nft_byteorder 表达式和大的源寄存器索引从堆栈中读取返回地址: 填充基链包含: 跳转到泄漏链 将构造的规则嵌入表达式中 将泄漏的指针写入集合的规则( nft_dynset ) 使用 nft_range 表达式构造假规则: 4. 内核代码执行 使用ROP链实现代码执行: 精心制作假的 nft_payload 表达式: 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或限制其使用 参考资源 攻击代码EXP 补丁Patch