azqzazq1/LID
GitHub: azqzazq1/LID
LID是一个通过eBPF和io_uring等内核机制绕过Linux安全模块的研究项目,旨在揭示安全框架的架构盲点。
Stars: 11 | Forks: 1
```
██╗ ██╗██████╗
██║ ██║██╔══██╗
██║ ██║██║ ██║
██║ ██║██║ ██║
███████╗██║██████╔╝
╚══════╝╚═╝╚═════╝
```
Linux 完整性漂移
— "Linux 正在消亡" —
系统化发现绕过 LSM 安全保证的内核代码路径
城门从未被攻破。而是从旁边绕了过去。
## 什么是 LID? Linux 安全模块框架有一个维持了 **20 多年**的核心保证: 这个保证是正确的。LID 并没有破坏它。 **LID 找到了完全绕过 LSM 钩子的内核代码路径** —— 那些执行安全敏感操作而不咨询 LSM 框架的子系统。安全检查是正确的。问题在于内核从未进行询问。
## 理解 LID 是什么(以及不是什么) 每个发现都有两个不应混淆的不同维度: ### A) 策略可见性差距 架构盲点。问题不是“攻击者能否利用它?”,而是: - AppArmor/SELinux 实际 **看到** 了什么? - 审计日志 **记录** 了什么? - 你的 SIEM/EDR **观察** 到了什么? - 策略引擎 **认为** 发生了什么? 如果发生了一个安全敏感操作,而执行层甚至从未评估过它,你就有了一个可见性差距 —— 无论攻击者今天是否能够实际滥用它。这关系到 **合规性**、**取证** 和 **纵深防御假设**。 ### B) 实际提权路径 现实世界的可利用性问题: - 这是否跨越了特权边界? - 攻击者是否需要现有的 root/CAP_BPF 权限才能触发它? - 是否需要漏洞利用链,还是可以独立运作? - 实际影响是什么 —— 数据访问、权限提升、策略规避? **这两者是不同的。** 一个发现可能是一个关键的可见性差距(你的监控是盲的),却不是一个实际的权限提升(攻击者已经需要 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-001: eBPF 路径名重写 | 条件 | 是否必需 | 备注 | |:---|:---|:---| | 内核版本 | 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,而非基于路径名 | ### LID-002: io_uring MSG_RING | 条件 | 是否必需 | 备注 | |:---|:---|:---| | 内核版本 | 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 | ### LID-003: 新挂载 API | 条件 | 是否必需 | 备注 | |:---|:---|:---| | 内核版本 | 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 可能不会 | ### 快速参考:什么会阻止每个发现 | 环境 | LID-001 | LID-002 | LID-003 | |:---|:---|:---|:---| | Ubuntu 22.04+(AppArmor,默认) | **有效** | **有效** | **有效** | | Debian 12+(AppArmor) | **有效** | **有效** | **有效** | | RHEL/Fedora(SELinux) | 无效 | **有效** | 无效 | | `lockdown=confidentiality` | 无效 | **有效** | **有效** | | 非特权用户 | 无效 | **有效** | 取决于用户命名空间 | | 容器(无 CAP_BPF) | 无效 | 取决于 io_uring | 取决于 seccomp |
## 发现 | ID | 向量 | 目标 | 发生了什么 | |:---|:---|:---|:---| | [**LID-001**](findings/lid-001-ebpf-pathname/) | eBPF kprobe 路径名重写 | AppArmor | kprobe 在 `copy_from_user` 之前重写用户内存中的文件名 → AppArmor 检查的是错误的路径 | | [**LID-002**](findings/lid-002-iouring-msgring/) | io_uring MSG_RING SEND_FD | SELinux, AppArmor, Smack | fd 传输跳过 `security_file_receive()` —— 其他所有 fd 传输机制都会调用它 | | [**LID-003**](findings/lid-003-mount-api/) | 新挂载 API (fsopen/fsmount) | AppArmor | `security_sb_mount()` 从未被调用 —— AppArmor 唯一的挂载钩子被绕过 |
## 模式 每个发现都遵循相同的模式: ``` ┌─────────────────────────────────────────────────────────────┐ │ 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. │ └─────────────────────────────────────────────────────────────┘ ``` 两个内核子系统。不兼容的信任假设。一个间隙。
## 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) ```
## LID-002: io_uring MSG_RING 缺失 LSM 钩子 带有 `IORING_MSG_SEND_FD` 的 `IORING_OP_MSG_RING` 在 io_uring 环之间传输文件描述符时**没有调用 `security_file_receive()`**。所有其他 fd 传输机制都会调用它: ``` 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`](findings/lid-002-iouring-msgring/)
## 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/`](findings/lid-003-mount-api/)
## 架构:为什么 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. ```
## 伴随项目:SunnyDayBPF ``` ┌───────────────────────────────────────┐ │ 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 │ └───────────────────────────────────────┘ ```
## 项目结构 ``` 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 ├── 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 ```
## 隐身特征(LID-001) | 指标 | 可见性 | 备注 | |:---|:---|:---| | 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`](docs/RESEARCH.md)。
## 要求 有关每个发现的完整详细信息,请参阅[可复现性矩阵](#reproducibility-matrix)。摘要: | 发现 | 最低内核 | 特权 | 目标 LSM | |:---|:---|:---|:---| | LID-001 | 5.x+ | root / CAP_BPF+CAP_PERFMON | 仅 AppArmor | | LID-002 | 6.0+ | **无** | 任何(SELinux, AppArmor, Smack) | | LID-003 | 5.2+ | CAP_SYS_ADMIN(用户 ns 可) | 仅 AppArmor |
## 作者
| **Azizcan Daştan** Milenium Security - GitHub: [@azqzazq1](https://github.com/azqzazq1) |
## 免责声明 本工具仅用于**授权的安全测试、研究和教育目的。**请勿用于您不拥有或未获明确书面许可进行测试的系统。
LID —— 因为完整性从未被锁住。
标签:0day挖掘, AppArmor绕过, Docker镜像, eBPF技术, Linux内核, LSM框架, 内核代码路径, 内核安全, 内核漏洞, 安全保证绕过, 安全渗透, 安全盲点, 安全绕过, 安全钩子, 审计规避, 客户端加密, 情报收集, 漏洞研究, 私有化部署, 策略可见性缺口, 系统调用操纵, 绕过技术, 路径名重写, 防御规避, 零审计足迹