eBPF 程序间隐蔽信道

摘要 eBPF 映射为内核态程序提供了高效的数据交换机制,其原子性保证仅限于单个 64 位操作。当多个 eBPF 程序共享同一个 BPF_MAP_TYPE_ARRAY 或 BPF_MAP_TYPE_HASH 时,涉及多个映射字段的复合操作存在天然的竞态窗口。攻击者可在一个容器的 eBPF 程序中快速修改映射的多个字段,与此同时,另一个容器的 eBPF 程序在读取前观察到不一致的中间状态,从而建立基于时序的隐蔽信道,绕过容器网络隔离与安全策略。更关键的是,bpf_spin_lock 的适用范围有限——它仅保护映射值内部字段的原子性,无法阻止攻击者利用映射操作之间的竞态。 eBPF 映射的并发语义 原子性保证的边界 eBPF 映射是内核中存储键值对的数据结构,支持 BPF_MAP_TYPE_ARRAY、BPF_MAP_TYPE_HASH、BPF_MAP_TYPE_PERCPU_ARRAY 等多种类型。对于哈希映射,bpf_map_update_elem 和 bpf_map_lookup_elem 在单个条目上提供原子读写——这意味着一个 64 位的值在任一时刻要么被完全写入,要么被完全读出,不会出现撕裂读写。 然而,复合操作的原子性并不存在。当攻击者需要同时修改两个或多个映射条目,或者在一个映射条目上执行“读取-修改-写入”序列时,多个指令之间存在可被其他程序观察到的窗口。例如: // 非原子复合操作 __u64 *counter = bpf_map_lookup_elem(&stats_map, &key); if (counter) { __u64 old = *counter; *counter = old + 1; // 读-改-写 竞态 __u64 *timestamp = bpf_map_lookup_elem(&time_map, &key); if (timestamp) { *timestamp = bpf_ktime_get_ns(); // 与 counter…

镜像仓库幻影层

摘要 OCI 镜像以层为单位分发,每层通过 tar 包与白文件(whiteout)控制文件删除与覆盖。容器运行时在解压镜像时,runc 的 mountToRootfs 会逐层应用变更,此过程对符号链接的处理存在边界歧义:攻击者可构造恶意镜像,先创建指向宿主根目录的符号链接,再利用后续层的挂载与白文件操作,绕过文件系统的访问控制,在容器启动阶段完成宿主机文件系统的逃逸。 OCI 镜像与容器运行时基础 镜像分层与 tar 层 OCI 镜像由多个只读层叠加而成,每一层是一个 tar 归档,保存相对于根文件系统的变化。镜像构建时,每个 RUN、ADD 等指令会生成一个新的层,最终通过联合文件系统(如 OverlayFS)合并成一个完整的根文件系统视图。 容器启动时,containerd 调用 runc 创建容器。runc 根据 OCI 配置,先挂载根文件系统(通常是 overlay),然后执行 rootfs 的准备工作,包括处理各层的文件操作。 runc 的 mountToRootfs 流程 runc 的 libcontainer/rootfs_linux.go 中,mountToRootfs 负责将各层挂载到容器的根文件系统路径。 流程大致如下: 挂载根文件系统(例如通过 overlay)到临时目录或直接使用 pivot_root。 应用各层:对于每个镜像层,runc 会遍历 tar 包中的条目,执行创建、修改、删除等操作。 处理 whiteout 文件(.wh. 前缀)和 opaque 目录文件(.wh..wh..opq)以隐藏或删除底层内容。 挂载卷、设备等。 在应用层的过程中,runc 会处理符号链接。具体逻辑在 mountToRootfs 中调用 mountToRootfs 的辅助函数 doMount,并依赖 os.Lstat 和 os.MkdirAll 等。对于符号链接,runc 可能在某些路径上直接跟随链接,导致对宿主机文件系统的意外操作。 符号链接与白文件的滥用 符号链接的跟随与逃逸 OCI 镜像的 tar 包可以包含符号链接。如果攻击者在一个层中创建一个指向宿主根目录(例如 /)的符号链接,然后在后续层中通过该符号链接写入文件,由于解压过程通常在宿主命名空间中执行(在挂载命名空间隔离之前),写入操作可能逃逸到宿主机文件系统。 runc 在处理各层时,会先应用层的 tar 包到容器的 rootfs 目录。这个过程发生在 pivot_root 之前,此时 rootfs 是一个宿主机路径(例如 /var/lib/containerd/rootfs/…)。如果 tar 包中包含符号链接并指向 /,那么后续在该符号链接下的写入操作可能影响到宿主机根目录。 白文件清空机制 白文件(以 .wh. 开头的文件)用于在更高层中删除低层的文件或目录。runc 在处理白文件时,会将低层的目标路径替换为删除操作。如果目标路径是一个符号链接,且该链接指向宿主目录,则删除操作可能删除宿主上的文件。 攻击者可以利用这种“删除”操作来掩盖逃逸行为,或者在逃逸后清理痕迹。 嵌套挂载与保护目录的绕过…

XFRM隐藏隧道

摘要 Linux 内核的 XFRM 框架负责 IPsec 变换,其中 ESP 的 BEET(Bound End-to-End Tunnel)模式在解密 IPsec 包后,会重构内部 IP 头。然而,在某些内核路径中,用于构造新 IP 头的 skb 区域未被完全初始化为零,导致内核堆中残留的指针或数据碎片被嵌入数据包并返回给远端用户。攻击者可从互联网发送特制的空载荷 ESP 包,在解密后的响应中泄露堆上的内核基址、堆地址甚至密钥材料,从而彻底击溃 KASLR 并为后续内核利用铺路。 BEET 模式 IPsec ESP 隧道 IPsec ESP 支持两种经典模式:传输模式仅加密传输层以上内容,保留原始 IP 头;隧道模式则加密整个原始 IP 数据包,并添加新的外部 IP 头。BEET 模式是两者的混合:它使用外部 IP 头进行路由,但内部 IP 头在解密后被重新计算,并以最小开销嵌入外部 IP 头之后。这种模式常用于移动 IPv6 和端到端加密场景。 其核心特性对比可以参考以下表: 维度 传输模式 隧道模式 BEET 模式 IP 标头数量 1 个(原始…

RISC-V 物理内存保护漏洞

摘要 RISC-V 架构通过物理内存保护与基于页表的虚拟内存两套机制协同控制内存访问。PMP 由机器模式配置,提供物理地址上的粗粒度权限;MMU 由监管者模式管理,实现细粒度的页级保护。然而,RISC-V 特权规范对两者权限叠加规则的定义存在模糊空间——部分微架构实现采用“最严格”策略,部分采用“最宽松”或“时序竞争”策略。攻击者可利用这一裂隙,在 S‑mode 下构造特殊的 PMP 与页表配置组合,使物理内存保护被虚拟内存权限意外覆盖,进而从 S‑mode 提升至 M‑mode 权限等级,读取或篡改安全监控固件。 RISC-V 的两层内存保护 PMP 物理内存保护是 RISC-V 机器模式下的安全基石。PMP 单元由最多 64 个配置寄存器(pmpcfgX)和地址寄存器(pmpaddrX)组成,支持三种地址匹配模式:自然对齐 4 字节区域(NA4)、自然对齐 2 的幂区域(NAPOT)和范围模式(TOR)。每个区域的访问权限由独立的 R(读)、W(写)、X(执行)位控制,并有一个 L(锁定)位。一旦 L 位被置位,PMP 配置对于更低特权级(包括 S‑mode)变为只读,即便 M‑mode 软件也无法修改,除非系统复位。 PMP 检查发生在物理地址完成转换后:当一条 load/store/指令 fetch 请求抵达物理地址总线前,硬件会遍历所有 PMP 条目,找到第一个匹配的区域,并根据访问类型和当前特权级(M、S、U)决定是否允许。若检查失败,对于 load/store 会触发访问错误异常,对于指令 fetch 则产生指令访问错误异常。 MMU RISC‑V S‑mode 通过页表(通常是 Sv39/Sv48 格式)实现虚拟地址到物理地址的转换。页表项包含 U(用户)、R、W、X 位,权限检查发生在翻译阶段。对于 S‑mode…

PAC 绕过新维度

摘要 ARM64 的指针认证通过 PACIA/PACIB 指令为返回地址和函数指针附加密码学签名,阻断了传统 ROP/JOP 攻击。然而,PAC 的密钥分域——指令地址用 A-key,数据地址用 B-key——留下了裂隙。攻击者可以在 JavaScript JIT 编译过程中喷射精心构造的 shellcode,利用 JIT 引擎生成混合了返回指令序列的代码,在指令域签名却在数据域校验,从而绕过 PAC 验证。 ARM64 PAC密钥分域与签名 PAC 指令与密钥 ARMv8.3-A 引入了指针认证(PAC),使用基于 QARMA 的轻量级 MAC 算法为 64 位指针生成认证码,嵌入指针的高位。核心指令: PACIA Xd, Xn:使用指令 A-key 对 Xn 生成签名,存储到 Xd。 PACIB Xd, Xn:使用指令 B-key 对 Xn 签名。 AUTIA Xd, Xn:验证 A-key 签名,若失败则将 Xd 设为无效指针。 AUTIB Xd, Xn:验证 B-key 签名。 A-key 用于指令地址(如返回地址、函数指针),B-key…

Git LFS 指针文件的竞态条件与 Smudge Filter 的隐蔽持久化

摘要 Git Large File Storage 用轻量指针文件替代大文件,再通过 Smudge Filter 在 checkout 时透明下载真实内容。这套机制原本是为了解决版本控制中的大文件管理问题,却在不经意间开辟了一条隐蔽的持久化通道:指针文件仅通过格式校验即可被认定为合法,而 Smudge Filter 在 checkout 时执行的命令完全由仓库内的 .gitattributes 控制。攻击者可以在指针文件通过验证后、实际下载内容完成前的微秒级窗口内,利用竞态条件将恶意载荷替换为“真实”文件,或直接注册一个伪装成 LFS Filter 的恶意 Smudge Filter,在 git clone 的瞬间执行任意命令。 Git LFS信任机制 LFS 指针文件的静态格式 Git LFS 的核心设计理念是“用指针文件替代大文件”。在 Git 仓库中,一个被 LFS 管理的大文件(如 video.mp4)会被替换为如下结构的指针文件: version https://git-lfs.github.com/spec/v1 oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393 size 341283 这个文件仅包含三行文本:版本声明、对象 OID(SHA-256 哈希)、文件原始大小。Git 对 LFS 指针的合法性校验仅基于两点: 第一行匹配version https://git-lfs.github.com/spec/v1。 第二行以oid sha256: 开头,后跟 64 个十六进制字符。 只要满足这两个格式条件,Git 就认为该文件是一个有效的 LFS 指针,并在后续操作中触发…