PPPoEject
Linux 内核 PPPoE 发送路径中的一处内存破坏漏洞,
由设备头部回调重新分配 skb 头部后遗留的 sk_buff 头部陈旧指针所导致。
本仓库仅用于经授权的安全研究、内核漏洞分析、CTF 环境以及防御性测试。
请勿在未经明确授权的系统上执行概念验证代码。
| 字段 | 详情 |
|---|---|
| CVE | CVE-2026-68121 |
| 代号 | PPPoEject |
| 组件 | Linux 内核 |
| 子系统 | PPPoE / 网络 |
| 受影响文件 | drivers/net/ppp/pppoe.c |
| 主要函数 | pppoe_sendmsg() |
| 缺陷类别 | 释放后使用 |
| 影响 | 内核内存破坏 |
| 潜在影响 | 本地权限提升 |
| CVSS v3.1 | 7.8 — 高危 |
| 攻击向量 | 本地 |
| 攻击复杂度 | 低 |
| 所需权限 | 低 |
| 用户交互 | 无 |
| 状态 | 已修复 |
CVE 记录将该漏洞认定为陈旧的 PPPoE 头部指针,
当 dev_hard_header() 重新分配 skb 头部时,该指针可能失效。
该漏洞存在于:
drivers/net/ppp/pppoe.c
within:
pppoe_sendmsg()
易受攻击的序列在概念上是:
pppoe_sendmsg()
│
▼
Save PPPoE header pointer
│
▼
dev_hard_header()
│
▼
skb head may be reallocated
│
▼
Old pointer becomes stale
│
▼
PPPoE writes through stale pointer
│
▼
Use-After-Free
│
▼
Kernel memory corruption
Linux 网络代码允许设备头回调重新分配 sk_buff 头部。因此,在 dev_hard_header() 返回后,指向旧头部的指针无法安全地再次使用。
核心问题是生命周期违规。
pppoe_sendmsg() 在调用以下函数之前获取指向 PPPoE 头部的指针:
dev_hard_header()
然而,该回调可能导致 skb 头部移动。
概念上:
Before callback:
skb
┌──────────────────────────────┐
│ Ethernet │ PPPoE │ Payload │
└──────────┴───────┴───────────┘
▲
│
stale pointer
After skb expansion:
old skb head ──X──► freed
new skb head
┌────────────────────────────────────┐
│ Ethernet │ PPPoE │ Payload │
└──────────┴───────┴─────────────────┘
▲
│
valid location
旧指针仍然引用已释放的分配。
当 PPPoE 随后通过该指针写入头部时,内核会执行一次 释放后使用写入(use-after-free write)。
文档记录的触发条件涉及围绕 copy_from_user() 的竞态,以及对 team 设备头部操作的更改。
其中描述的一种序列是:
PPPoE sendmsg()
│
▼
copy_from_user() blocks
│
│
├───────────────┐
│ │
▼ ▼
Team device changes First non-Ethernet
header operations port is added
│
▼
Delegated GRE callback
│
▼
skb head expansion
│ │
└───────────────┘
│
▼
stale PPPoE pointer
│
▼
use-after-free write
CVE 记录指出,当第一个非以太网端口被添加到空的 team 设备,且委托的 GRE 头部回调扩展 skb 头部时,就可能发生此问题。
该漏洞可能导致:
该 CVE 公布的 CVSS 向量为:
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
基础评分为 7.8 高。
相关组件如下:
PF_PPPOX / PPPoE socket
│
▼
pppoe_sendmsg()
│
▼
sk_buff (skb)
│
▼
dev_hard_header()
│
▼
Network device header callback
│
▼
Potential skb head expansion
│
▼
PPPoE stale header pointer
│
▼
UAF write
因此,存在漏洞的代码并非简单的通用 PPPoE 数据包解析器; 关键条件在于 PPPoE socket 传输 与 动态 skb head 重新分配 之间的交互。
上游修复的标题为:
pppoe: reload header pointer after dev_hard_header()
修复方案在创建设备头之后,通过 skb 的网络头偏移量重新加载 PPPoE 头。
其重要特性是:
Before:
header pointer
│
▼
dev_hard_header()
│
▼
skb moves
│
▼
pointer = stale ❌
After:
dev_hard_header()
│
▼
skb may move
│
▼
reload header using skb offset
│
▼
pointer = valid ✅
pskb_expand_head() 在 skb 头部被重新分配时更新相关的 skb 偏移量,使得基于偏移量的查找在重新分配后仍然安全。
| 安全属性 | 易受攻击版本 | 已修补版本 |
|---|---|---|
| 回调前保存头部指针 | ✅ | — |
| skb 头部可移动 | ✅ | ✅ |
| 回调后刷新指针 | ❌ | ✅ |
| 悬空指针解引用 | 可能发生 | 已阻止 |
| 释放后使用写入 | 可能发生 | 已缓解 |
| 内核内存损坏 | 可能发生 | 已缓解 |
一个受控实验室可以按如下方式构建:
┌───────────────────────────────────────────────┐
│ Linux VM │
│ │
│ ┌───────────────┐ │
│ │ PPPoE Socket │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ pppoe_sendmsg │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Team │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ GRE │ │
│ └───────────────┘ │
│ │
└───────────────────────────────────────────────┘
有用的内核调试工具:
gdb
pwndbg
gef
crash
objdump
readelf
pahole
监控内核消息:
sudo dmesg -w
检查 PPPoE 支持:
lsmod | grep pppoe
检查可用的 PPP 模块:
find /lib/modules/$(uname -r) -iname '*pppoe*'
检查当前正在运行的内核:
uname -r
检查与 PPPoE 相关的配置:
grep -E 'CONFIG_PPP|CONFIG_PPPOE' \
/boot/config-$(uname -r)
典型的相关选项包括:
CONFIG_PPP
CONFIG_PPP_ASYNC
CONFIG_PPP_SYNC_TTY
CONFIG_PPPOE
用于受控的内核研究:
01. Build vulnerable kernel
↓
02. Boot isolated VM
↓
03. Enable PPPoE support
↓
04. Create controlled networking topology
↓
05. Configure team/GRE interaction
↓
06. Exercise PPPoE sendmsg()
↓
07. Monitor skb lifetime
↓
08. Capture kernel diagnostics
↓
09. Apply upstream patch
↓
10. Repeat and compare
目标是安全地复现内存生命周期违规,并比较存在漏洞与已修补的行为。