Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

CVE-2026-43456 深度分析:Linux 内核 bonding 驱动 19 年类型混淆漏洞与 99% 稳定提权链

引子:一行代码,19 年

2026 年 7 月,GMO Cybersecurity 的两位研究员 Yuki Koike 和 Kota Toda 公开披露了他们向 Linux 内核报告并修复的漏洞 CVE-2026-43456。这个漏洞的特殊之处在于:

  • 根因代码在 2007 年 就合入了 Linux 内核主线。
  • 影响范围从 Linux 2.6.24 到 6.12.77,几乎覆盖过去 19 年的所有主流发行版。
  • 利用成功率超过 99%,单次提权可在 1 秒内 完成。
  • 研究人员因此获得 Google kernelCTF 超过 8 万美元 的赏金。

而它的一切,都源自 net/bonding/bond_main.c 里的一行赋值:

c
bond_dev->header_ops = slave_dev->header_ops; // ★ 漏洞根因

这行代码看起来合理:bond 设备要透明地处理下层 slave 设备的报文头,所以复用 slave 的 header 操作函数表。但问题恰恰在于——这些回调函数期望的私有数据结构类型,与 bond 设备的私有数据结构并不相同


一、bonding 子系统:一个 19 岁的设计假设

1.1 bonding 是什么?

Linux bonding 驱动用于将多个物理网卡聚合成一个逻辑接口(bond 设备),实现负载均衡(balance-rr)或主备切换(active-backup)。常见于服务器、虚拟化平台和网络设备。

text
┌──────────────────────┐
│      bond0 (逻辑设备)  │  ← 对上呈现为单一网卡
│   struct net_device   │
│   netdev_priv = struct bonding
└──────────┬───────────┘

    ┌──────┴──────┐
    │             │
┌───▼───┐     ┌───▼───┐
│ eth0  │     │ eth1  │  ← 下层 slave 设备
│dummy  │     │  gre  │  ← 可以是 dummy、veth、gre、tun 等
└───────┘     └───────┘

1.2 header_ops 的委托模型

struct net_device 中的 header_ops 是一组函数指针,用于创建、解析、重建二层报文头。例如 header_ops->create() 负责填充目的 MAC 地址。

bond 设备为了让上层协议栈"无感知"地处理报文,直接将 slave 设备的 header_ops 复制到自己身上:

c
static void bond_setup_by_slave(struct net_device *bond_dev,
                                struct net_device *slave_dev)
{
    bool was_up = !!(bond_dev->flags & IFF_UP);
    dev_close(bond_dev);

    bond_dev->header_ops = slave_dev->header_ops;   // ★ 类型混淆
    bond_dev->type       = slave_dev->type;
    bond_dev->hard_header_len = slave_dev->hard_header_len;
    bond_dev->needed_headroom = slave_dev->needed_headroom;
    bond_dev->addr_len   = slave_dev->addr_len;

    // ...
}

这行代码的设计假设是:所有 header_ops 回调都操作 struct net_device 公共字段,不会触及设备私有数据。但这个假设是错的。


二、类型混淆根因:函数指针与私有数据结构不匹配

2.1 回调函数内部使用了 netdev_priv()

部分 header_ops 回调会通过 netdev_priv(dev) 获取设备私有数据。例如 GRE 隧道的 ipgre_header()

c
// drivers/net/gre.c(示意)
static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
                        unsigned short type, const void *daddr,
                        const void *saddr, unsigned len)
{
    struct ip_tunnel *tunnel = netdev_priv(dev); // 期望 dev 是 gre 设备
    // ... 读写 tunnel->...
}

netdev_priv() 返回的是 struct net_device 末尾的私有内存。对于 GRE 设备,这块内存是 struct ip_tunnel;对于 bond 设备,这块内存是 struct bonding

当 bond 设备把 GRE 的 header_ops 复制到自己身上,并触发 ipgre_header() 时,函数会把 struct bonding 当成 struct ip_tunnel 来读写——这就是经典的 类型混淆(Type Confusion)

2.2 为什么 19 年都没被发现?

这个漏洞的触发条件非常苛刻:

  1. 必须创建一个 bond 设备。
  2. 必须把一个类型不兼容的 slave(如 GRE 隧道)加入 bond。
  3. 必须触发 header_ops 回调。
  4. 必须让 skb 的内存布局满足特定对齐条件,才能将越界写转化为可利用的内存损坏。

在普通场景下,越界写只是写到未使用内存,不会触发崩溃。因此它长期作为"不会崩溃的良性越界"存在,直到研究人员用 syzkaller 捕捉到一次异常崩溃,并深入分析后才发现其可利用性。


三、利用链:从类型混淆到 root

3.1 攻击条件

  • 本地访问:攻击者需要能够执行 ip link 等网络管理命令。
  • CAP_NET_ADMIN:创建 bond 和 GRE 设备需要该 capability。在开启了非特权用户命名空间(unprivileged user namespaces)的系统上,普通用户默认即可获得该 capability。
  • 目标内核:Linux 2.6.24 至 6.12.77(2026 年 3 月前未修复)。

3.2 最小触发命令(基于 kernel.org 公告)

bash
# 1. 创建并激活一个 dummy 设备
ip link add dummy0 type dummy
ip addr add 10.0.0.1/24 dev dummy0
ip link set dummy0 up

# 2. 创建一个 GRE 隧道设备
ip link add gre1 type gre local 10.0.0.1

# 3. 创建一个 bond 设备(active-backup 模式)
ip link add bond1 type bond mode active-backup

# 4. 把 GRE 设备挂到 bond 下,触发 header_ops 继承
ip link set gre1 master bond1
ip link set gre1 up
ip link set bond1 up

# 5. 给 bond 配置 IPv6 地址,触发 header_ops 回调
ip addr add fe80::1/64 dev bond1

3.3 利用流程

text
攻击者

  ├─ 创建 dummy0 + 配置 IPv4
  ├─ 创建 gre1(基于 dummy0 的本地地址)
  ├─ 创建 bond1(active-backup)
  ├─ 将 gre1 作为 slave 加入 bond1
  │      ↓
  │  bond_setup_by_slave() 执行
  │  bond_dev->header_ops = slave_dev->header_ops  ← 类型混淆发生
  │      ↓
  ├─ 触发 bond1 的 IPv6 报文发送
  │      ↓
  │  ipgre_header(bond_dev) 被调用
  │  netdev_priv(bond_dev) 返回 struct bonding*,但被当作 struct ip_tunnel* 使用
  │      ↓
  │  越界写修改 struct bonding 内部字段 / 邻近内存
  │      ↓
  ├─ 精心构造 skb 内存布局,将越界写转化为对关键内核对象的覆盖
  │      ↓
  └─ 覆盖 cred / task_struct 相关字段,实现本地权限提升至 root

3.4 为什么成功率高达 99%?

研究人员在利用中发现:

  • 通过控制 skb->data 的对齐位置,可以把越界写精确瞄准到 skb_shared_info 头部。
  • 利用 SLAB_TYPESAFE_BY_RCU 和对象重用机制,可以稳定地让目标内存区域被攻击者控制的结构覆盖。
  • 整个过程不需要用户交互,不需要 race condition,属于确定性利用路径

四、检测与防御

4.1 自查脚本:检查 bond 设备和不寻常 slave 组合

bash
#!/bin/bash
# 检查系统中是否存在 bond 设备挂接了 tunnel/veth/dummy 等非以太网 slave

for bond in /sys/class/net/bonding_masters; do
    [ -f "$bond" ] || continue
    for dev in /sys/class/net/*/bonding_slave; do
        [ -f "$dev" ] || continue
        slave=$(basename "$(dirname "$dev")")
        master=$(cat "$dev" 2>/dev/null)
        slave_type=$(cat "/sys/class/net/$slave/uevent" 2>/dev/null | grep DEVTYPE | cut -d= -f2)
        echo "slave=$slave master=$master type=${slave_type:-unknown}"
    done
done

4.2 临时缓解措施

如果无法立即升级内核,可采取以下措施:

  1. 禁用非特权用户命名空间

    bash
    echo 0 > /proc/sys/kernel/unprivileged_userns_clone

    这会阻止普通用户获得 CAP_NET_ADMIN,但也会影响 Rootless Docker、Podman 等特性。

  2. 卸载 bonding 模块

    bash
    echo "install bonding /bin/false" > /etc/modprobe.d/disable-bonding.conf
    rmmod bonding 2>/dev/null

    适用于不需要链路聚合的服务器。

  3. 限制网络命名空间创建:通过 seccomp、AppArmor、SELinux 限制用户创建 bond/GRE/tunnel 设备。

4.3 修复方案

Linux 内核在 2026 年 3 月修复了该漏洞,核心改动是在 bond_setup_by_slave() 中不再盲目继承 slave 的 header_ops,而是确保 slave 的 header 回调在 bond 设备上执行时,netdev_priv() 获得正确的私有数据结构。

修复提交(部分):

  • 950803f7254721c1c15858fbbfae3deaaeeecb11
  • 6ac890f1d60ac3707ee8dae15a67d9a833e49956
  • 95597d11dc8bddb2b9a051c9232000bfbb5e43ba
  • 9baf26a91565b7bb2b1d9f99aaf884a2b28c2f6d

修复的关键思路:对于依赖特定 netdev_priv() 布局的 header_ops,不允许 bond 设备直接复用;要么提供 bond 自己的兼容包装函数,要么在调用时传入正确的 slave 设备指针。


五、复盘:这个漏洞给安全研究什么启示?

5.1 单行道假设是漏洞温床

bond_dev->header_ops = slave_dev->header_ops 这行代码背后的假设是"所有 header_ops 回调都足够通用"。当这个假设在 19 年前成立时,系统很简单;随着新设备类型(GRE、VXLAN、IPIP 等)不断加入,旧代码的通用假设逐渐变成危险的单行道。

5.2 模糊测试 + 手动分析仍是发现老漏洞的王牌

研究人员最初是通过 syzkaller 捕捉到一次崩溃,然后通过手动分析定位到根因。这说明:

  • 自动化 fuzzer 能发现异常行为;
  • 但复杂类型混淆的根因分析仍依赖人类对内核数据结构和调用约定的理解。

5.3 最小权限与命名空间隔离仍是关键

CVE-2026-43456 的触发需要 CAP_NET_ADMIN。在大多数发行版上,普通用户本不该拥有这个 capability;但非特权用户命名空间的默认开启让攻击者绕过了这一限制。这个漏洞再次证明:

网络 capability 的边界,必须和命名空间边界一起评估。


六、总结

CVE-2026-43456 是 2026 年最具影响力的 Linux 内核本地提权漏洞之一。它提醒我们:

  • 老代码不等于安全代码:2007 年的代码在 2026 年的设备类型组合下,可能突然变成可利用漏洞。
  • 类型混淆是内核高危漏洞的常见模式:函数指针表 + 私有数据结构不匹配,是 C 语言内核中反复出现的攻击面。
  • 防御优先级:升级内核 > 禁用非特权用户命名空间 > 卸载 bonding 模块 > 限制网络设备创建。

对于运行 Linux 服务器的团队,最务实的动作是:

  1. 检查当前内核版本是否在 6.12.77 之前。
  2. 检查是否存在 bond 设备挂接了 tunnel/dummy 等类型 slave。
  3. 如果业务不需要链路聚合,直接禁用 bonding 模块。
  4. 如果必须保留,优先升级到 2026 年 3 月之后修复的内核版本。

参考与延伸阅读

上次更新于: