Knative冷启动劫持

摘要 Knative 通过保留最小数量的常驻 Pod 来加速无服务器函数的冷启动,但其容器复用策略在安全性与性能之间制造了一道危险的裂缝:当某个函数容器被重用时,其内存中可能残留上一个请求的变量、环境密钥、数据库连接池或临时文件。攻击者可以在 Serverless 平台上发起一个合法请求,利用容器复用时未清理的 tmpfs 或环境变量读取上一个租户的残留数据。部分平台为了降低延迟,甚至不重启 Node.js 或 Python 进程,仅清空 HTTP 上下文,使得跨请求的数据残留成为系统性缺陷。 Knative 冷启动优化与实例复用 冷启动的代价与优化 Knative Serving 基于 Kubernetes,为每个函数实例(Revision)创建 Pod 来服务请求。当函数长时间未收到请求时,Knative 会将其 Pod 缩减到零以节省资源。下一个请求到达时,需要重新创建 Pod、拉取镜像、启动容器、初始化运行时环境——这一“冷启动”过程可能耗费数秒,远高于用户体验可接受的延迟。 为了降低冷启动延迟,Knative 引入了 容器实例保持 策略:即使没有活跃请求,也保留一个或多个“温暖”的 Pod 在内存中等待。 当新请求到达时,这些温暖的 Pod 可以立即处理请求,避免镜像拉取和运行时初始化。这些温暖的 Pod 可能被同一个函数实例的多个请求复用,甚至在某些配置下被同一命名空间内的不同函数实例复用。 实例复用时的内存语义 当一个温暖的 Pod 被复用时,Knative 不会重新启动容器进程,而是直接向现有进程发送新的 HTTP 请求。对于 Node.js、Python 等解释型语言运行时,这意味着同一进程的内存空间将被多个请求共享。tmpfs、环境变量、全局变量和堆中的对象都保持原样,只有 HTTP 上下文被重新初始化。 这种“进程级复用”带来了显著的安全隐患:如果前一个请求在内存中留下了敏感数据,这些数据可能被后续请求读取。例如: 用户 A 提交了一个包含 API 密钥的函数请求,该密钥被存储在某个全局变量或 tmpfs 文件中。 用户 B 随后向同一函数实例发起请求,如果用户…

ARM64 GIC时序侧信道

摘要 ARM64 通用中断控制器在分发硬件中断时,会短暂挂起当前执行的指令流。在核心保存上下文期间,被中断的指令序列可能已经执行了部分加载指令,将敏感数据预取到 L1 数据缓存。攻击者若能够控制中断到达的精确时机(例如通过软件生成的中断 SGI),可在安全世界(EL3)的关键指令(如 eret)执行前中断它,使预加载的安全世界缓存行在普通世界可被探测,从而绕过 TrustZone 的隔离,窃取安全世界的加密密钥或敏感数据。 GIC 与 ARM64 异常入口的时序窗口 GICv3 中断分发路径 ARM64 平台的通用中断控制器(GIC)负责管理所有中断源,并向 CPU 核心分发。GICv3 由分发器(Distributor)和再分发器(Redistributor)以及 CPU 接口组成。软件生成中断(SGI)通过写 ICC_SGI0R_EL1 或 ICC_SGI1R_EL1 寄存器来触发,可指定目标核心和中断 ID。攻击者如果具备在普通世界 EL1 的代码执行能力,便可以向自身或其他核心注入 SGI,制造精确的本地中断。 当中断到达核心时,CPU 完成当前指令的边界,然后进入异常入口流程:保存处理器状态到栈,跳转到中断向量。ARM64 的中断向量表定义了不同异常级别和类型的入口。关键点是,从异常被触发到第一条异常处理指令执行之间存在固定的指令流水线延迟,并且在这个窗口内,处理器可能已经执行了后续的部分指令(由于分支预测和推测执行)。 安全世界与普通世界的切换 在 TrustZone 架构中,普通世界(Normal World)和安全世界(Secure World)的切换通过 smc 指令进入 EL3,或在 EL3 中通过 eret 返回普通世界。当安全世界正在执行敏感操作(如加密、认证)时,若被中断抢占,处理器必须先保存安全世界上下文,然后跳到中断处理程序。如果该中断处理程序位于普通世界(例如在 EL1 内核中),则会进行一次世界切换(从安全世界到普通世界),这涉及缓存和 TLB 的维护操作。 [EL0 用户态代码] │ ├─ (发生异常/中断, 如 SVC / IRQ) ▼ [硬件自动行为 (CPU Core)] ├─…

CXL 内存池信任污染

摘要 Compute Express Link(CXL)3.0 通过 Type 3 内存扩展设备实现了多主机共享内存池,为云原生架构提供了前所未有的内存弹性。然而,共享意味着信任边界的扩张:当多个虚拟机(VM)通过 CXL 交换机共享同一物理内存池时,内存页的生命周期管理与安全隔离完全依赖于主机侧的内存分配器和 CXL 设备固件。如果设备在页释放后未执行可靠的清零或加密边界切换,攻击者便可在合法 VM 释放共享页后,立即从另一台主机申请分配同一物理页,读取前一个租户的残留数据——包括加密密钥、凭证和模型参数。更严重的是,部分 CXL 设备固件未严格验证主机发来的 HDM 地址范围,允许跨域访问。 CXL内存池 CXL协议与Type 3设备 CXL 构建在 PCIe 物理层之上,提供三种协议:CXL.io(用于配置和控制)、CXL.cache(用于缓存一致性)和 CXL.mem(用于内存访问)。Type 3 设备(如内存扩展卡)仅实现 CXL.io 和 CXL.mem,不参与缓存一致性,主要提供大容量内存扩展。 在 CXL 3.0 中,多主机共享内存池成为可能。通过 CXL 交换机和 Fabric Manager,多个主机(或虚拟机管理程序)可以访问同一块 Type 3 设备上的内存区域。这种架构允许动态分配和释放内存资源,提高数据中心的利用率。 多主机共享内存池 共享内存池打破了传统“一个主机一块内存”的边界。CXL 设备上的物理内存被划分为多个主机物理地址空间(Host Physical Address Space, HPAS),每个主机通过 HDM(Host-managed Device Memory)解码器将其分配给自己的地址空间。 安全性挑战在于: 内存释放后的数据残留:当一台主机释放一块 CXL…

Intel TDX MMIO 暗门

摘要 Intel Trust Domain Extensions 通过内存加密和 MRTD 测量为信任域提供硬件级隔离,使 VM 免受即使已被攻陷的 VMM 的侵害。然而,TD 与 VMM 之间共享内存的设计引入了一个根本性的信任悖论:TD 依赖 VMM 维护的 EPT 映射来访问共享页,而 EPT 由不可信的 VMM 完全控制。攻击者(即恶意 VMM)可在 TD 向共享页写入数据后、读取响应前的极短时间内,将 EPT 中的 GPA→HPA 映射切换到包含恶意数据的物理页,实现 L2 地址劫持。由于 TDX 的运行时 EPT 变更不会被证明报告捕获,该攻击可完全绕过远程验证。 TDX 的信任边界 信任域与不可信 VMM Intel TDX 将虚拟机封装为信任域,其内存由 MKTME(多密钥全内存加密)引擎使用 TD 私钥加密,VMM 无法解密读取。TD 的 CPU 状态、寄存器和缓存也被隔离。远程验证方通过 MRTD(TD 内容的 SHA-384 测量)确认…

WebRTC 插入流音频劫持

摘要 WebRTC 通过 RTCRtpSender.setParameters() 允许动态调整编码参数,其 encodings 数组用于描述 simulcast 各层的 rid 和 active 状态。然而,Chromium 实现中,对该数组与底层 RTP 流同步源的映射更新并非原子操作——攻击者可在重协商信令阶段插入一个携带伪造 rid 的高优先级编码条目,调用 setParameters() 后,内部编码器可能为这个新条目分配独立的 SSRC,而该 SSRC 并未出现在远端 SDP 中。此时音频数据会被同时发送到合法接收者和攻击者控制的 SSRC,形成无声监听。更隐蔽的是,利用 RTCRtpContributingSource 统计信息无法及时反映这一变化,使得劫持长期隐匿。 Simulcast与RTCRtpEncodingParameters的映射关系 Simulcast 的 RID 与 SSRC WebRTC 的 Simulcast 允许一个发送器(RTCRtpSender)同时生成多个不同码率或分辨率的视频/音频流,每个流由一个独立的 SSRC 标识。 在 SDP 协商中,a=simulcast:send 属性声明了这些流的 rid(Restriction Identifier),接收方根据 rid 识别流。例如: a=simulcast:send low;medium;high a=rid:low send pt=96;max-br=100000 a=rid:medium send pt=96;max-br=500000 a=rid:high send pt=96;max-br=1000000 在发送方,RTCRtpSender.getParameters().encodings 返回一个数组,每个元素对应一个 simulcast 层,包含 rid、active、maxBitrate 等属性。底层媒体引擎根据这些参数创建独立的 RTP 编码流,每个流对应唯一的 SSRC,并且该 SSRC 会通过 a=ssrc: 属性在 SDP 中声明。正常情况下,rid 与 SSRC 的映射在 setParameters() 调用后保持不变,除非明确移除或禁用某层。 setParameters的非原子性 setParameters() 允许修改 encodings 数组的某些字段(在…

Swift 中的不可见角色

摘要 Swift 5.10 引入了严格并发检查,通过 @Sendable 闭包和 Actor 隔离在编译期防止数据竞争。然而,这些安全保证几乎完全依赖于编译器的静态分析,运行时缺乏强制机制。攻击者可利用 UnsafeMutablePointer 将非 Sendable 类型强制转换为 @Sendable 闭包,并将其跨 Actor 传递;在另一个 Actor 上通过 withUnsafeCurrentTask 执行任意代码,破坏原 Actor 的状态,实现数据竞争和类型混淆。更隐蔽的是,利用 TaskLocal 传递裸指针,或通过 GCD 直接调度绕过 @MainActor 隔离,可在服务器端 Swift 应用(如 Vapor)中实现持久化的隐蔽后门。 Sendable与 Actor 隔离 Sendable协议的类型安全承诺 Sendable 是 Swift 并发模型的核心协议之一,用于标记可以安全地跨并发域传递的类型。值类型(如 Int、String)、无内部共享状态的 final class 以及显式标记为 @Sendable 的闭包均符合 Sendable。编译器会检查所有跨 Actor 或 Task 传递的值的类型,如果非 Sendable 类型被错误传递,则发出严格并发检查的编译错误或警告。 在底层,@Sendable 闭包会被编译器转换为捕获不可变副本或同步原语保护的上下文,但这一转换完全发生在 SIL(Swift Intermediate Language)层,最终生成的 LLVM IR 中并没有额外的运行时检查。也就是说,一旦绕过编译器的静态检查,运行时将不加区别地执行任何闭包。 Actor 隔离的执行语义 Actor 是一种引用类型,通过串行执行队列保护其内部状态。所有对 Actor 可变状态的访问都必须通过 await 异步调用或在 Actor 内部的同步代码中进行。编译器负责保证这一约束:在 Actor 外部,无法直接访问其存储属性或调用未标记 nonisolated 的方法。 运行时实现上,Actor 携带一个内部的 DistributedActor 标识和串行执行器。当任务需要在 Actor 上执行时,会被调度到该执行器上排队。但 Actor 的执行器本身是可替换的,且 GCD 等旧并发 API 可以绕过这一调度机制,直接将任务提交到任意队列执行。这构成了 Actor…