Windows 过滤管理器中的 Attached 结构体混淆

摘要 Windows 过滤管理器(FltMgr.sys)通过 Attached 结构体将微过滤器绑定到卷设备栈,维护着每个微过滤器实例在 I/O 栈中的位置关系。然而,在 FltUnregisterFilter 卸载路径中,该结构体的引用计数管理存在竞态条件窗口,攻击者可利用并发 I/O 请求使 Attached 结构体在仍被引用时被释放,导致 use-after-free。这种结构体混淆使得攻击者能够覆写内核池中的控制数据,劫持卷设备栈上的回调指针,最终实现从低权限用户到 SYSTEM 的本地权限提升。本文从过滤管理器的 Attached 结构体架构出发,逐层剖析 FltUnregisterFilter 卸载路径中的竞态窗口、引用计数缺陷及卷设备栈劫持机制,构建基于 Windows 内核驱动和用户态触发器的完整 PoC,并讨论基于引用计数完整性监控和卸载路径审计的防御方案。 过滤管理器与 Attached 结构体 过滤管理器在文件系统栈中的位置 Windows 过滤管理器(Filter Manager,FltMgr.sys)是文件系统过滤驱动框架的核心组件,位于文件系统驱动(如 NTFS、FAT32)与遗留过滤器之间。它允许微过滤器驱动注册回调例程,以拦截和处理文件系统操作(如创建、读取、写入、重命名)。 过滤管理器维护了一个“帧”概念,用于在 I/O 栈中插入微过滤器。 每个帧包含一组在相同高度(altitude)上运行的微过滤器实例,帧的排列顺序决定了微过滤器的调用顺序。当微过滤器加载时,过滤管理器将其回调函数插入到 I/O 处理路径中;当微过滤器卸载时,过滤管理器需要从所有卷设备栈中移除其回调。 Attached 结构体的角色 在过滤管理器的内部实现中,Attached 结构体(_FLT_ATTACHED_VOLUME 或类似命名)是连接微过滤器实例与卷设备栈的关键数据结构。它存储了以下信息: 卷对象指针:指向该微过滤器实例所附加的卷设备对象。 设备栈位置:微过滤器在设备栈中的相对位置(在哪个设备对象之上或之下)。 回调注册表:该微过滤器在此卷上注册的所有 I/O 回调函数。 引用计数:跟踪有多少未完成的 I/O 操作正在引用此 Attached 结构体。 当一个微过滤器通过 FltAttachVolume 或自动附加机制绑定到某个卷时,过滤管理器为该微过滤器分配一个…

Linux io_uring 固定缓冲区逃逸

摘要 io_uring 的固定缓冲区机制通过预注册用户页并长期持有 FOLL_PIN 引用,消除了每次 I/O 操作的页锁定开销。然而,这种“一次注册、永久持有”的设计在 IORING_OP_SPLICE 的异步执行路径中暴露出了引用计数管理的结构性缺陷。CVE-2022-4696 揭示了 io_splice 在异步工作队列中执行时缺少 IO_WQ_WORK_FILES 标志,导致 current->nsproxy 的引用计数未被正确递增,而 get_uts 调用却会使用它,最终造成 use-after-free。更危险的是 PinTheft 攻击(CVE-2026-43494),它通过 RDS 零拷贝发送路径的 double-free 窃取 io_uring 固定缓冲区注册的 FOLL_PIN 引用,将匿名页的引用计数从 1024 降至零,使该页被释放并重新分配为 SUID-root 二进制的页缓存。攻击者随后利用悬空的 io_uring 固定缓冲区指针覆写页缓存,植入恶意 ELF 载荷,执行 SUID 程序即获得 root shell。 io_uring 固定缓冲区 固定缓冲区 io_uring 的核心性能优势之一在于消除了每次 I/O 操作的系统调用开销。但即使绕过了系统调用,每次 I/O 仍然需要将用户缓冲区映射到内核可访问的内存中——这一过程通过 get_user_pages 完成,涉及页表遍历、TLB 填充和引用计数递增。 固定缓冲区机制将这一开销从“每次操作”降低到“每次注册”。用户通过 io_uring_register…

5G 核心网的 SBI 消息窃听

摘要 5G 核心网从传统点对点架构转向基于服务的架构,所有网络功能通过 HTTP/2 上的 RESTful API 进行通信。然而,这一架构变革在提升弹性的同时,也将电信核心网暴露在了与云原生微服务相同的攻击面之下。CVE-2026-55068 揭示了 free5GC NRF 的 RegisterNFInstance 处理器在未验证 NF Profile 的情况下接受注册请求,攻击者可通过 SBI 注入携带恶意 IP 端点的伪造 NF 配置,使后续所有 NF 发现请求返回攻击者控制的地址,从而将控制面信令重定向至攻击者基础设施。与此同时,CVE-2026-44320 暴露了 NEF 回调路由组完全缺失 OAuth2 认证中间件的问题——任意伪造的 Bearer Token 即可穿透认证边界,直达 SMF 回调处理器。 5G SBI 的信任模型与攻击面 从点对点到服务化架构 5G 核心网的根本性变化在于将传统的点对点接口替换为基于 HTTP/2 的服务化接口。在 4G EPC 中,MME 通过 S1-MME 接口与 eNodeB 通信,通过 S6a 接口与 HSS 通信,每个接口都有专用的协议和信令格式。5G…

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 个(原始…