
LID — Linux Integrity Drift: Bypassing AppArmor via eBPF pathname rewriting. Pre-LSM syscall argument manipulation with zero audit footprint. "Linux is Dying"
```
██╗ ██╗██████╗
██║ ██║██╔══██╗
██║ ██║██║ ██║
██║ ██║██║ ██║
███████╗██║██████╔╝
╚══════╝╚═╝╚═════╝
```
— "Linux 正在消亡" —
系统发现绕过 LSM 安全保证的内核代码路径
大门从未被攻破,只是被绕过了。
Linux 安全模块框架有一个核心保证,已经保持了 20 多年:
安全模块只能增加限制。它们永远不能移除限制。
这个保证是正确的。LID 并没有打破它。
LID 发现了完全绕过 LSM 钩子的内核代码路径——执行安全敏感操作而不咨询 LSM 框架的子系统。安全检查是正确的。问题是内核从未询问过。
每个发现有两个不同的维度,不应混为一谈:
架构上的盲点。问题不是“攻击者能否利用这个?”而是:
如果发生了一个安全敏感操作,而执行层甚至从未对其进行评估,那么你就存在一个可见性差距——无论攻击者今天是否能够实际利用它。这对合规性、取证和纵深防御假设很重要。
现实世界中的可利用性问题:
这两者是不同的。 一个发现可以是关键的可见性差距(你的监控是盲目的),而不一定是一个实际的权限提升(攻击者已经需要 root)。相反,一个发现可以是一个直接提权路径,具有最低的先决条件。
| 发现 | 可见性差距 | 实际提权 |
|---|---|---|
| LID-001 | 关键 — AppArmor 什么都看不到,审计日志为空,零取证痕迹 | 有限 — 需要 root 或 CAP_BPF+CAP_PERFMON(已有特权)。不是权限提升。影响:策略规避 + 审计盲区。 |
| LID-002 | 高 — security_file_receive() 从未触发,fd 传递对所有 LSM 不可见 | 高 — 通过 io_uring 从无特权用户空间工作。无需任何特权即可跨越 LSM 强制边界。 |
| LID-003 | 高 — security_sb_mount() 被绕过,AppArmor 挂载策略成为死代码 | 中 — 需要挂载命名空间访问(用户命名空间中的 CAP_SYS_ADMIN)。在许多容器配置中可用。 |
| LID-004 | 关键 — AppArmor 什么都看不到,零 BPF 钩子(0/9),任何 BPF 令牌操作均无审计痕迹 | 中 — 需要主机委托 bpffs + 用户命名空间中的 CAP_BPF。在具有 BPF 委托的容器运行时(LXD/Incus)中可用。 |
| LID-005 | 中 — 容器接口上的 tc 出站分类器永远不评估 AF_XDP 流量 | 有限 — 绕过容器 eth0 上的 tc 出站(已验证)。Cilium 未被绕过——在节点侧 veth 入站加强并验证源 IP(已测试)。影响限于仅使用 tc 出站过滤的普通 Docker 设置。 |
每个发现都有特定的内核/配置/权限要求。如果你的环境不匹配,该发现将无法复现。
| 条件 | 要求 | 注释 |
|---|---|---|
| 内核版本 | 5.x+ | 已在 5.15、6.1、6.6、6.8 上测试 |
CONFIG_BPF_SYSCALL | =y | 所有主要发行版默认开启 |
CONFIG_BPF_KPROBE_OVERRIDE | =y | Ubuntu/Debian 启用,RHEL 不启用 |
CONFIG_SECURITY_APPARMOR | =y | 目标 LSM 必须为 AppArmor |
| AppArmor 配置文件 | 强制模式,目标路径上有拒绝规则 | 适用于任何基于路径的拒绝规则 |
| 权限 | root 或 CAP_BPF + CAP_PERFMON | 不能以无特权方式运行 |
kernel.lockdown | none 或 integrity | confidentiality 模式阻止 kprobe 附加 |
kernel.unprivileged_bpf_disabled | 无关 | 无论如何都需要 CAP_BPF |
fs.protected_hardlinks | 跨用户链接需为 0 | 1(默认)仍允许同用户硬链接 |
| SELinux 代替 AppArmor | 不工作 | SELinux 基于 inode,而不是基于路径名 |
| 条件 | 要求 | 注释 |
|---|---|---|
| 内核版本 | 6.0+ | IORING_MSG_SEND_FD 在 6.0 中添加 |
CONFIG_IO_URING | =y | 所有主要发行版默认开启 |
| 权限 | 无 | 从无特权用户空间工作 |
io_uring_disabled sysctl | 0(默认) | 2 阻止无特权用户,1 阻止所有 |
| 目标 LSM | 任意(SELinux、AppArmor、Smack) | security_file_receive() 是一个通用 LSM 钩子 |
kernel.lockdown | 无关 | 不涉及 BPF |
| 条件 | 要求 | 注释 |
|---|---|---|
| 内核版本 | 6.9+ | BPF 令牌在 6.9 中引入 |
CONFIG_BPF_SYSCALL | =y | 所有主要发行版默认开启 |
CONFIG_SECURITY_APPARMOR | =y | Ubuntu/Debian 默认 |
| bpffs 启用了委托 | 是 | 主机必须使用 delegate_* 选项挂载 |
| 权限 | 用户命名空间中的 CAP_BPF | 用户命名空间 root 可轻易获得 |
| SELinux 代替 AppArmor | 不受影响 | SELinux 实现了所有 9 个 BPF 钩子 |
| 条件 | 要求 | 注释 |
|---|---|---|
| 内核版本 | 5.2+ | fsopen/fsmount 在 5.2 中引入 |
CONFIG_SECURITY_APPARMOR | =y | 仅影响 AppArmor |
| 权限 | 用户命名空间中的 CAP_SYS_ADMIN | 可通过 unshare -m 获取 |
| SELinux 代替 AppArmor | 不工作 | SELinux 实现了 security_sb_kern_mount() |
| 容器运行时 | 取决于 seccomp 过滤器 | Docker 默认 seccomp 阻止 fsopen — Podman/LXC 可能不阻止 |
| 条件 | 要求 | 注释 |
|---|---|---|
| 内核版本 | 4.18+ | AF_XDP 在 4.18 中引入 |
CONFIG_XDP_SOCKETS | =y | 所有主要发行版默认开启 |
| 权限 | 仅 CAP_NET_RAW | Docker、Kubernetes Pod 默认具备 |
| 容器运行时 | Docker、K8s、LXC | 默认权限集 |
| 基于 tc 的网络策略 | 是 | Cilium eBPF、Calico、tc u32/flower |
| 环境 | LID-001 | LID-002 | LID-003 | LID-004 | LID-005 |
|---|---|---|---|---|---|
| Ubuntu 22.04+(AppArmor,默认) | 有效 | 有效 | 有效 | 有效(6.9+) | 有效 |
| Debian 12+(AppArmor) | 有效 | 有效 | 有效 | 有效(6.9+) | 有效 |
| RHEL/Fedora(SELinux) | 否 | 有效 | 否 | 否 | 有效 |
lockdown=confidentiality | 否 | 有效 | 有效 | 部分 | 有效 |
| 无特权用户 | 否 | 有效 | 取决于用户命名空间 | 取决于 bpffs 委托 | 否 |
| 容器(无 CAP_BPF) | 否 | 取决于 io_uring | 取决于 seccomp | 否 | 有效 |
| 容器(已丢弃 CAP_NET_RAW) | 否 | 取决于 | 取决于 | 否 | 否 |
| ID | 向量 | 目标 | 发生了什么 |
|---|---|---|---|
| LID-001 | eBPF kprobe 路径名重写 | AppArmor | kprobe 在 copy_from_user 之前重写文件名 → AppArmor 检查错误的路径 |
| LID-002 | io_uring MSG_RING SEND_FD | SELinux、AppArmor、Smack | fd 传递跳过了 security_file_receive() — 而所有其他 fd 传递都会调用它 |
| LID-003 | 新挂载 API(fsopen/fsmount) | AppArmor | security_sb_mount() 从未被调用 — AppArmor 唯一的挂载钩子被绕过 |
| LID-004 | BPF 令牌委托 | AppArmor、Smack | 零 BPF 钩子 — 令牌创建、使用、能力委托完全不可见 |
| LID-005 | AF_XDP __dev_direct_xmit | tc 出站、Cilium、Calico | 从默认 Docker 的复制模式 TX 绕过 tc 分类器 — 伪造的数据包到达网桥 |
每个发现都遵循相同的模式:``` ┌─────────────────────────────────────────────────────────────┐ │ THE LID PATTERN │ │ │ │ Kernel Subsystem A LSM Framework │ │ (eBPF / io_uring / mount API) (AppArmor / SELinux) │ │ │ │ ┌───────────────────┐ │ │ │ Performs operation │──── should call ──── security_*() │ │ │ or manipulates │ but │ │ │ security input │ DOESN'T ██ │ │ └───────────────────┘ │ │ │ │ The LSM framework is correct. │ │ The kernel just doesn't always use it. │ └─────────────────────────────────────────────────────────────┘
两个内核子系统。不兼容的信任假设。一个漏洞。
<br>
---
## LID-001: eBPF 路径名重写
**重点发现。** 一个在 `do_sys_openat2` 上的 BPF kprobe 在用户内存中重写了文件名,然后再由内核复制。AppArmor 检查了重写后的路径,授予了访问权限。零审计追踪。```
process: open("/tmp/secret.txt")
│
▼
do_sys_openat2()
│
★ LID kprobe fires here
│ bpf_probe_write_user()
│ rewrites "/tmp/secret.txt" → "/tmp/.bypass_link"
│
▼
getname_flags() ← kernel copies the (rewritten) path
│
▼
security_file_open() ← LSM hooks check "/tmp/.bypass_link"
│ AppArmor: ALLOW ✓
▼
VFS opens inode ← same file content (hard link)
│
▼
return fd to process ← success, zero audit trace
sudo ./scripts/check_prerequisites.sh sudo ./scripts/setup_env.sh make sudo ./scripts/setup_demo.sh sudo ./scripts/run_demo.sh
### 演示输出```
╔══════════════════════════════════════════════════════════╗
║ Phase 1: AppArmor ENFORCING — access should be DENIED ║
╚══════════════════════════════════════════════════════════╝
$ /tmp/test_reader
[-] DENIED: open() failed: Permission denied (errno=13)
╔══════════════════════════════════════════════════════════╗
║ Phase 2: Loading LID — BPF kprobe pathname rewrite ║
╚══════════════════════════════════════════════════════════╝
[*] BPF kprobe attached to do_sys_openat2
╔══════════════════════════════════════════════════════════╗
║ Phase 3: With LID active — access should be GRANTED ║
╚══════════════════════════════════════════════════════════╝
$ /tmp/test_reader
[+] SUCCESS: Read 44 bytes: SECRET_DATA=this_is_protected_content_12345
╔══════════════════════════════════════════════════════════╗
║ Phase 4: Stealth check — audit log inspection ║
╚══════════════════════════════════════════════════════════╝
$ dmesg | grep apparmor | grep DENIED
(empty — no denial was ever generated)
IORING_OP_MSG_RING 与 IORING_MSG_SEND_FD 在 io_uring 环之间传输文件描述符,而不调用 security_file_receive()。所有其他文件描述符传输机制都会调用它:```
MSG_RING SEND_FD: __io_fixed_fd_install() ← NO security_file_receive()
FIXED_FD_INSTALL: receive_fd() ← security_file_receive() ✓
SCM_RIGHTS: receive_fd() ← security_file_receive() ✓
binder: security_binder_transfer_file() ✓
**漏洞位置:** `io_uring/msg_ring.c`, `io_msg_install_complete()` — 直接调用 `__io_fixed_fd_install()`,跳过了 LSM 钩子。
**通过 ftrace 验证:** `security_file_receive` 对 SCM_RIGHTS 触发,但对 MSG_RING 不触发。
**影响范围:** Linux 5.18+ 至 v7.1-rc3(截至 2026-05-17 未修复)。
**PoC:** [`findings/lid-002-iouring-msgring/msg_ring_bypass.c`](https://github.com/azqzazq1/lid/blob/HEAD/findings/lid-002-iouring-msgring/)
<br>
---
## LID-003:新挂载 API 绕过 AppArmor
新的挂载 API(`fsopen` + `fsconfig` + `fsmount` + `move_mount`)**从未调用 `security_sb_mount()`**——这是 AppArmor 实现的唯一挂载钩子。```
OLD API: mount("proc", "/mnt", "proc", 0, NULL)
→ security_sb_mount() → AppArmor: DENY ✗
NEW API: fsopen("proc") → fsconfig(CMD_CREATE) → fsmount() → move_mount()
→ security_sb_kern_mount() → AppArmor: (not implemented) → ALLOW ✓
AppArmor 注册了 4 个挂载钩子。SELinux 注册了 14 个。新的挂载 API 使用了只有 SELinux 实现的钩子。
发现的额外缺口:
open_tree(OPEN_TREE_CLONE):零安全钩子 —— 绕过绑定挂载策略mount_setattr():内核中根本不存在 LSM 钩子move_mount() 与分离挂载:AppArmor 看到 NULL 源路径详情: findings/lid-003-mount-api/
BPF 令牌子系统(Linux 6.9+)将 BPF 能力委派给用户命名空间中的非特权进程。内核定义了 9 个 BPF LSM 钩子。SELinux 实现了所有 9 个,并带有真实的 avc_has_perm() 强制。AppArmor 实现了 零。```
Kernel defines 9 BPF LSM hooks:
┌─────────────────────────────────────────────────────────────────┐
│ bpf, bpf_map, bpf_prog, bpf_map_create, bpf_prog_load, │
│ bpf_token_create, bpf_token_free, bpf_token_cmd, │
│ bpf_token_capable │
└─────────────────────────────────────────────────────────────────┘
SELinux: ████████████████████████████ 9/9 implemented AppArmor: 0/9 implemented Smack: 0/9 implemented
在Ubuntu/Debian上,一个具有bpffs委托的容器可以:
- 创建BPF令牌 → AppArmor看不到任何东西
- 使用令牌加载跟踪程序 → AppArmor看不到任何东西
- 委托CAP_PERFMON → 禁用Spectre缓解 → AppArmor看不到任何东西
**详情:** [`findings/lid-004-bpf-token/`](https://github.com/azqzazq1/lid/blob/HEAD/findings/lid-004-bpf-token/)
<br>
---
## LID-005: 来自默认Docker容器的AF_XDP tc出口绕过
AF_XDP的拷贝模式传输路径(`xsk_generic_xmit` → `__dev_direct_xmit`)**绕过容器接口上的tc出口分类器**。已使用tc u32 DROP-ALL验证——AF_PACKET被阻止,AF_XDP通过。
**然而:** 使用Cilium v1.19(kind集群,拒绝所有出口NetworkPolicy)进行测试——**Cilium未被绕过。** Cilium在节点侧veth对端入口(通过tcx/ingress的`cil_from_container`)实施强制,并具有源IP验证。AF_XDP数据包在`bpf_lxc.c:1603`处作为"Invalid source ip"被丢弃。影响仅限于仅依赖tc出口分类器进行网络策略的环境(纯Docker + tc规则,无CNI)。```
Normal TX path:
socket → dev_queue_xmit() → sch_handle_egress() → tc classifiers → driver
↑
ENFORCED (Cilium eBPF, Calico, tc u32)
AF_XDP copy-mode TX:
xsk_sendmsg() → xsk_generic_xmit() → __dev_direct_xmit() → driver
↑
SKIPPED (tc never runs)
在 Docker 默认容器上动态验证于内核 6.8.0:``` tc egress DROP-ALL on container eth0:
AF_PACKET sendto → ENOBUFS (BLOCKED by tc) AF_XDP sendto → 0 (BYPASS — 3 spoofed packets reached docker0 bridge)
数据包携带攻击者控制的以太网头部——伪造的MAC地址、伪造的IP地址——尽管有活动的DROP策略,仍能到达Docker桥接。
**PoC + 详情:** [`findings/lid-005-afxdp-tc-bypass/`](https://github.com/azqzazq1/lid/blob/HEAD/findings/lid-005-afxdp-tc-bypass/)
<br>
---
## 架构:为什么BPF LSM无法修复此问题```
LSM Hook Chain: call_int_hook(file_open, ...)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Lockdown │──>│ Capability │──>│ AppArmor │──>│ BPF LSM │
│ return 0 │ │ return 0 │ │ return -13 │ │ (never │
│ (allow) │ │ (allow) │ │ (DENY) ██ │ │ reached) │
└─────────────┘ └─────────────┘ └──────┬───────┘ └─────────────┘
│
loop breaks
RC = -EACCES
★ The LSM framework is correct. No module can undo another's denial.
★ LID operates OUTSIDE the framework — before hooks run, or where hooks don't exist.
┌───────────────────────────────────────┐
│ THE ATTACK TIMELINE │
│ │
Syscall Entry │ ★ LID ─── manipulates input │ │ │ (security check sees wrong data) │ ▼ │ │ LSM Check │ LSM allows (or never runs) ✓ │ │ │ │ ▼ │ │ Syscall Exit │ ★ SunnyDayBPF ─── rewrites telemetry │ │ │ (monitoring sees wrong data) │ ▼ │ │ Audit/Log │ SIEM sees nothing ✓ │ │ │ │ Combined: ghost access │ └───────────────────────────────────────┘
<br>
## 项目结构```
LID/
├── src/
│ ├── bpf/
│ │ └── lid.bpf.c # BPF kprobe — pathname rewriter (LID-001)
│ └── loader/
│ └── lid_loader.c # Userspace loader + event monitor
├── findings/
│ ├── lid-001-ebpf-pathname/ # eBPF pathname rewriting bypass
│ ├── lid-002-iouring-msgring/ # io_uring MSG_RING missing LSM hook
│ │ ├── msg_ring_bypass.c # PoC with ftrace verification
│ │ └── ADVISORY.md # Technical advisory
│ ├── lid-003-mount-api/ # New mount API AppArmor bypass
│ ├── lid-004-bpf-token/ # BPF token AppArmor/Smack zero coverage
│ └── lid-005-afxdp-tc-bypass/ # AF_XDP tc egress bypass from container
├── tests/
│ └── test_reader.c # Victim binary for LID-001 demo
├── scripts/
│ ├── check_prerequisites.sh # Verify system requirements
│ ├── setup_env.sh # Install build dependencies
│ ├── setup_demo.sh # Create demo environment
│ ├── run_demo.sh # Run full LID-001 demonstration
│ └── teardown.sh # Clean up everything
├── docs/
│ └── RESEARCH.md # Full technical research paper
├── publish/ # Articles for Medium, dev.to, etc.
├── Makefile
├── LICENSE
├── SECURITY.md
└── README.md
| 指标 | 可见性 | 备注 |
|---|---|---|
| AppArmor 审计日志 | 无 | 拒绝从未发生 |
auditd / journald | 无 | 未生成安全事件 |
dmesg | 一次性警告 | 通用 bpf_probe_write_user 消息 |
bpftool prog list | 可见 | 显示已附加的 kprobe(如果检查) |
| 磁盘上的硬链接 | 可检测 | find -samefile(缓慢、嘈杂) |
| 缓解措施 | 有效性 | 权衡 |
|---|---|---|
kernel.lockdown=confidentiality | 完全阻止 BPF | 导致合法监控失效 |
禁用 bpf_probe_write_user | 阻止 LID-001 路径重写 | 需要重新编译内核 |
fs.protected_hardlinks=1 | 限制硬链接创建 | 现代内核默认启用 |
监控 bpftool prog list | 检测已附加的探针 | 需要主动轮询 |
| 迁移到 SELinux | 基于 inode,阻止路径重写 | 迁移复杂 |
| 限制 io_uring | 阻止 LID-002 | 可能破坏应用程序 |
有关详细缓解指导,请参阅 docs/RESEARCH.md。
查看可重现性矩阵获取每项发现的完整详细信息。摘要:
| 发现 | 最低内核 | 权限 | 目标 |
|---|---|---|---|
| LID-001 | 5.x+ | root / CAP_BPF+CAP_PERFMON | 仅限 AppArmor |
| LID-002 | 6.0+ | 无 | 任何 (SELinux, AppArmor, Smack) |
| LID-003 | 5.2+ | CAP_SYS_ADMIN (用户命名空间可行) | 仅限 AppArmor |
| LID-004 | 6.9+ | CAP_BPF (用户命名空间可行) | AppArmor, Smack |
| LID-005 | 4.18+ | CAP_NET_RAW (Docker 默认) | tc egress (Cilium, Calico, 等) |
|
Azizcan Daştan
|
本工具仅发布用于授权的安全测试、研究和教育目的。不得用于您不拥有或未获得明确书面许可的系统。
LID — 因为完整性从未被锁定。