一、漏洞描述
Linux 内核流量控制子系统被爆出存在本地提权漏洞,编号为CVE-2026-68138,漏洞产生在 net/sched/sch_api.c 文件。漏洞根因是速率表全局链表 qdisc_rtab_list 和引用计数 refcnt 缺少并发保护的机制。从 Linux 5.1 开始 flower 分类器被标记为 TCF_PROTO_OPS_DOIT_UNLOCKED,police 动作处理可在没有 RTNL 互斥锁的情况下进入,并发请求竞争同一速率表,触发 struct qdisc_rate_table 速率表对象的 use-after-free 或 double-free。
| |
|---|
| 本地提权;竞态导致 use-after-free / double-free |
|---|
| Linux 内核 net/sched/sch_api.c |
|---|
| 5.1 – 7.1.5;7.2-rc1 – 7.2-rc4 |
|---|
| 7.1.6(fb29e1b41052);7.2-rc5(f43ee0c0730d) |
|---|
| |
|---|
| 本机代码执行权限;非特权用户/网络命名空间开启;flower、police 相关内核选项 |
|---|
| |
|---|
| Aldo Ariel Panzardo 报告;A. Ramos 实现 PoC(2026-08-12 公开) |
|---|
风险研判:CVE-2026-68138漏洞是一个本地提权漏洞,攻击者需要有被攻击端的普通权限,PoC 存在一定的内核配置局限,成功的几率也受竞态概率影响,不能达到通杀。
二、漏洞原理
漏洞的根本原因是无锁的全局速率表管理。 qdisc_rate_table 用于流量整形,内核按速率将报文换算为等待时间,同速率表由多个调用方共享,以全局链表缓存、引用计数管理生命周期。qdisc_get_rtab() 与 qdisc_put_rtab() 对链表执行查找、插入、计数增减与释放,全程无锁。历史调用都是在持有 RTNL 互斥锁的前提下调用,锁串行化了全部访问,缺陷未暴露。Ubuntu 5.15 官方内核的 net/sched/sch_api.c 原码如下:
static struct qdisc_rate_table *qdisc_rtab_list; // 全局链表,无锁struct qdisc_rate_table *qdisc_get_rtab(...){ ... for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) { if (表和参数能匹配上) { rtab->refcnt++; // 非原子 +1 return rtab; } } rtab = kmalloc(sizeof(*rtab), GFP_KERNEL); ... rtab->next = qdisc_rtab_list; qdisc_rtab_list = rtab; // 头插全局链表,无锁 return rtab;}void qdisc_put_rtab(struct qdisc_rate_table *tab){ if (!tab || --tab->refcnt) // 非原子 -1 return; for (遍历链表找到 tab) { *rtabp = rtab->next; kfree(rtab); // 释放 return; }}
触发路径是 flower 分类器解除了 RTNL 约束。 cls_flower 设置 TCF_PROTO_OPS_DOIT_UNLOCKED。
RTM_NEWTFILTER 请求创建 flower 规则时保持 rtnl_held=false,该状态经调用链传递,tcf_police_init() 在没有 RTNL 的情况下调用速率表辅助函数:
tc_new_tfilter() → fl_change() → tcf_exts_validate_ex() → tcf_action_init() → tcf_police_init() → qdisc_get_rtab() / qdisc_put_rtab()
不同的CPU上的两个 RTM_NEWTFILTER 请求携带相同速率表并发执行的时候,引用计数增减与链表头插、摘除交错,计数提前归零或链表指针错位,从而形成 use-after-free 或 double-free。官方 CVE 记录附带的 KASAN 报错如下:
BUG: KASAN: slab-use-after-free in qdisc_put_rtab+0x12f/0x160 qdisc_put_rtab+0x12f/0x160 tcf_police_init+0xda9/0x1590 ... fl_change+0xddd/0x7da0 tc_new_tfilter+0xaa7/0x2420
利用流程。 PoC 利用链分五个阶段:
- 竞态触发。4 个线程分别进入到网络命名空间,利用竞争攻击反复创建带 police 动作的 flower 规则,其中3 个线程走正常路径,1 个线程故意传非法参数,在取得速率表引用后走错误清理路径释放表,使并发操作交错。
- 内存回收。被释放的速率表仍留在内核堆缓存中,同一 CPU 随即挂载 BPF 过滤器回收这块内存。过滤器按速率表的结构整形,末条指令带自定义标记,读回标记即可判断两块内存是否重叠。
- 指针泄露。关闭一个 socket,待内存真正释放后以管道缓冲回收;另一 socket 再读内容,得到内核页指针与 /sbin/modprobe 所在页位置,无需猜测内核基址。
- 伪造管道缓冲。使活着的管道缓冲被释放,再以 xattr 内存回收,把泄露的管道结构摆回原位并置可合并标志。此后向管道写入的数据会落入 /sbin/modprobe 入口所在页,实现可控写入并回读确认。
- 触发执行。创建内核未实现的协议 socket,触发模块自动加载,内核以初始命名空间 root 身份执行被改写的 /sbin/modprobe,入口代码随即开启 root shell。
三、修复建议
修复版本为稳定版 7.1.6 与主线 7.2-rc5