D-Bus 语义投毒

摘要 D-Bus 是 Linux 桌面环境的神经中枢,Flatpak 和 Snap 等沙箱方案依赖 D-Bus 代理过滤不受信任的调用。然而,xdg-dbus-proxy 的过滤规则存在一个语义漏洞:它根据消息头中的发送者唯一名称来放行回复,却没有验证该名称是否由可信的进程生成。攻击者可以从沙箱内部伪造一条回复消息,冒充系统守护进程(如 org.freedesktop.systemd1),绕过接口白名单,直接调用 systemd 的管理方法创建恶意服务单元,从而逃逸沙箱并以宿主机权限执行任意代码。 D-Bus 安全模型与过滤代理 D-Bus 消息结构与身份标识 D-Bus 通信基于消息总线,最常用的会话总线和系统总线分别处理用户会话和系统级服务。 每条消息包含固定的头部字段:消息类型(方法调用、方法返回、错误、信号)、路径、接口、成员以及若干可选字段,其中 SENDER 字段表示消息的发送者唯一名称(如 :1.42)。总线守护进程 dbus-daemon 在连接建立时为每个连接分配唯一的名称,并在后续消息中自动添加 SENDER 字段——如果消息本身没有提供,daemon 会设置它;但如果消息已经包含了 SENDER,daemon 的行为在不同版本中存在差异:旧版本 daemon 可能直接信任消息中已有的 SENDER 值,而非强制覆盖。 这构成了第一道裂缝:若攻击者能够直接与对端套接字通信并伪造 SENDER,总线守护进程不会拒绝,接收端将信任伪造的发送者身份。 Flatpak 沙箱 Flatpak 等沙箱技术通过 Bubblewrap 限制应用的文件系统和网络访问,并通过 D-Bus 代理 (xdg-dbus-proxy) 控制应用与 D-Bus 总线的交互。代理运行在沙箱外部,根据预定义或动态协商的过滤器规则,对来自沙箱内部的消息进行审查。规则通常基于: 接口和方法名:例如只允许 org.freedesktop.portal.* 接口的调用。 发送者:允许来自特定 D-Bus 名称的回复。 访问方向:允许接收某些信号,禁止发送某些调用。 当沙箱内的应用发送一条方法调用消息时,代理检查目标接口和方法是否在白名单中。对于信号的订阅,代理也进行过滤。但关键的安全漏洞在于:代理在处理回复(类型为 method_return 或 error)时,会检查该回复是否对应于之前从沙箱发出的某个调用。为了将此回复路由给正确的调用者,代理会查看回复消息中的 reply_serial 字段(匹配之前调用的序列号)。然而,代理假设只有合法的服务进程才会生成带有正确 reply_serial 和 SENDER 的回复——它并未验证该回复消息是否真的来自目标服务,也未强制要求回复消息中的 SENDER 必须与之前调用的 DESTINATION 匹配。 过滤机制的语义漏洞 攻击场景:沙箱内的恶意应用希望调用系统总线上的 org.freedesktop.systemd1.Manager.StartTransientUnit 方法来创建一个恶意服务单元,该方法在沙箱的过滤器白名单中显然不存在。但攻击者可以采用以下步骤绕过: 沙箱应用向系统总线发送一条伪造的方法调用,声称目的地是 org.freedesktop.systemd1,但实际上这条调用并不真正发送到 systemd,而是由攻击者自己在沙箱内部的另一个线程接收并处理。 攻击者构造一条伪造的回复消息,类型为 method_return,reply_serial 匹配之前那条调用的序列号,SENDER 字段设置为 :1.XXX(实际 systemd 拥有的唯一名称),但这条回复实际上是由沙箱内部的恶意线程生成并通过沙箱与代理之间的套接字发送给代理的。 代理收到这条“回复”后,根据 reply_serial 找到之前尚未完成的调用记录,认为这确实是 systemd 对该调用的回复,于是将其放行——因为代理默认放行已许可调用的回复。 攻击者在构造这条“回复”消息的同时,在消息体中嵌入了另一个新的方法调用:在 method_return 的回复体内部,可以放置一条任意的方法调用(这是…

USB-C 模拟攻击

摘要 USB Power Delivery 3.1 通过 Configuration Channel 引脚协商高达 240W 的电力与 Alternate Mode 数据,但其协议栈在处理供应商自定义消息(VDM)时缺乏严格的长度验证。攻击者可利用 FPGA 或 STM32 微控制器模拟 PD PHY,发送畸形的 VDM 数据包,触发接收端解析器的堆溢出,进而在 USB4 主控或电源管理芯片的固件中执行任意代码。同时,通过伪造 e-Marker 电缆身份,攻击者能让主机误信其连接了高规格线缆,从而开放超过物理承载能力的电流,引发物理烧毁;或伪装成 DisplayPort Alt Mode 设备,在无用户授权的情况下建立视频输出通道,实现隐蔽监控。 PD协议栈 CC 引脚的半双工通信与 BMC 编码 USB-C 接口的 Configuration Channel(CC)引脚是 PD 协议通信的物理介质。与 USB 数据线不同,CC 采用单线半双工模式,使用双相标记编码(BMC)以 300 kbps 速率传输数据。消息以 64 比特的前导码开头,后接 SOP(Start of Packet)定界符,再跟 16 比特的消息头和可变长度的有效载荷,最后是 32…

AI 模型「溶枪」

摘要 开放神经网络交换格式(ONNX)通过 Protobuf 结构描述计算图,其灵活的自定义操作机制允许绑定任意外部动态库。这一设计便利了硬件厂商的算子优化,却也使得模型文件成为可注入恶意代码的“特洛伊木马”。攻击者可将后门逻辑伪装成合法的预处理或量化算子,在模型中嵌入定制的动态库加载指令。当模型被 ONNX Runtime 等服务加载时,隐藏的代码便会悄无声息地启动反向 Shell、窃取推理数据或篡改预测结果。由于计算图结构极其复杂,大多数静态扫描工具难以识别这类寄生在算子语义中的“溶枪式”后门。 ONNX 的自定义算子和外部数据信任链路 自定义算子的能力边界 ONNX 的自定义算子通过 op_type 和 domain 标识,由推理引擎注册的内核实现。ONNX Runtime 提供 RegisterCustomOp 接口,允许开发者动态添加算子,内核可以调用任意系统 API。这一能力原本是为硬件加速器(如 TPU)预留的,却也为攻击者打开了执行任意代码的通道。攻击者只需将恶意内核封装为动态库(libCustomOp.so),并在模型加载时通过配置文件或环境变量指定该库的路径,即可在推理进程上下文中执行任意操作。 更隐蔽的方式是,攻击者直接修改模型文件中的 external_data 字段,将恶意动态库打包为张量的“权重数据”。在 ONNX 规范中,大张量可通过 external_data 引用外部文件,但也可以将整个库嵌入模型内部。当模型首次推理时,自定义内核的初始化函数会解包该文件到临时目录并调用 dlopen,从而完成自我加载——整个过程无需用户额外配置任何库文件。 模型加载流程中的可注入点 ONNX Runtime 的模型加载流程主要包括:解析模型文件 → 构建计算图 → 注册内置和自定义算子 → 分配内存 → 执行图。 攻击者可劫持以下环节: 图构建阶段:通过修改 GraphProto 中的 node 条目,插入一个高优先级的自定义算子,声称用于“权重解密”或“校验和验证”。该算子会在模型加载后立即执行,先于正常推理。 算子注册阶段:若推理服务允许通过环境变量 ORT_CUSTOM_OP_LIBRARY 指定自定义库,攻击者可通过模型附带的启动脚本设置该变量,实现环境投毒。 外部数据解析阶段:利用 external_data 的 offset 和 length 字段,将一个看似正常的张量指向模型文件末尾的恶意 Shellcode,再由自定义算子读取并执行。 多阶段溶枪攻击链 环境准备与侦察 攻击者植入的第一个自定义算子名为 EnvProbe,域为 ai.utils。它在 ONNX 图中被标记为 Constant 节点的“消费者”,宣称负责“设备能力探测”。实际上,该算子执行以下操作: 检查当前进程权限和操作系统版本; 读取环境变量,确认是否在目标推理服务器上运行; 若判定为生产环境,则释放第二阶段载荷;否则保持静默。 这种选择性激活的策略有效规避了在开发或测试环境中被提前发现的风险。 动态库落地与持久化 EnvProbe 通过 external_data 获取一个压缩的共享库 libsyslog.so(伪装成日志库)。算子将其解压到 ~/.cache/ort_custom/libsyslog.so,并调用 dlopen 加载。该库的构造函数会: 修改 ~/.bashrc,追加一条自动拉取远程脚本的命令; 在 /etc/cron.d/ort_health 中写入一个每 10 分钟执行的定时任务,用于维持反向 Shell; 注册一个新的自定义算子 DataSink,用于后续数据窃取。 数据窃取与回传…

TrustZone 硅后门

摘要 ARM TrustZone 将系统划分为普通世界与安全世界,通过安全监控调用(SMC)实现切换。高通、联发科等厂商在实现 SMC 处理表时,常未严格验证调用 ID 范围,且允许在 EL2/EL1 特权下修改 SMC 表指针。攻击者可利用这些漏洞,劫持合法 SMC 调用,将其重定向至恶意处理函数,从普通世界读取安全世界内存(包括指纹模板、加密密钥),甚至将恶意载荷注入安全用户态(S-EL0)的可信应用(TA)中,实现持久化隐蔽后门。 SMC 指令与调用表 SMC 快速调用 在 TrustZone 架构中,普通世界(Normal World)的代码无论特权级多高,都无法直接访问标记为安全世界的物理内存或外设。唯一受控的跨域交互是通过 SMC(Secure Monitor Call) 指令。当普通世界执行 SMC #imm 时,CPU 陷入安全监控模式(Monitor Mode)或直接切换到安全世界的 EL3 固件。SMC 指令携带一个 16 位立即数,作为调用 ID,安全监控软件根据该 ID 查找预定义的函数表,执行相应的安全服务。 安全监控软件通常由 OEM 提供(如 Qualcomm 的 QSEE、MTK 的 Kinibi),运行在 EL3,充当 TrustZone 的看门人。其内部维护一个 SMC 调度表,每个条目包含一个调用 ID、处理函数指针以及参数个数等信息。 调用表的静态与动态注册 OEM 的 SMC 表通常分为两部分:编译期静态表(如高通 qsee_handle_smc)和动态注册表(用于后续加载的安全世界内核模块)。动态表的基址常存储在 EL3 固件的数据段中,可通过逆向固件或利用内存泄露漏洞获取。在某些实现中,动态表的尾指针未正确校验,导致调用 ID…

剪贴板即信道

摘要 异步剪贴板 API 为 Web 应用提供了读、写系统剪贴板的能力,但 navigator.clipboard.write() 的异步执行与用户 paste 事件的同步触发之间,存在一个容易被忽略的原子性缺陷。攻击者可利用同一用户手势先后调用 write() 和 read(),通过 Promise.race 构造竞态条件,在浏览器实际写入新内容之前读取到剪贴板中尚未被覆盖的残留数据——而这些数据可能来自另一个域,包含密码管理器自动填充的凭据、2FA 种子或私钥。更危险的是,结合密码管理器的“复制密码”功能和 WebAuthn 条件 UI 的自动填充行为,窃取甚至不需要用户额外的粘贴操作。 异步剪贴板 API 的安全模型 从 execCommand 到 navigator.clipboard 在 navigator.clipboard 出现之前,页面只能通过 document.execCommand(‘copy’) 和 document.execCommand(‘paste’) 操作剪贴板,且后者仅在浏览器扩展或用户主动粘贴时可用。异步剪贴板 API 允许页面使用 readText() 和 writeText() 直接读取和写入文本,前提是获得用户许可(对于 read,通常要求页面可见且由用户手势触发)并且传输数据遵循安全上下文(HTTPS)。 关键设计:write() 返回一个 Promise,在数据被系统剪贴板确认后解析;read() 同样返回一个 Promise,在读取到当前剪贴板内容时解析。二者都是在“任务队列”中异步执行的,但在一次用户手势(如 click 事件)中,可以同时调用两个方法而不相互阻塞。 同一个点击下的 write 与 read 考虑以下代码: button.addEventListener('click', async () => { navigator.clipboard.writeText('A'.repeat(100)); const data = await navigator.clipboard.readText(); console.log(data); }); 开发者预期 readText() 会返回刚才写入的 ‘AAA…’,但实际结果取决于 UA 的事件循环调度:如果 writeText() 的内部实现是“先调度写入任务,再解析 Promise”,而 readText() 在写入任务实际执行之前就已经从系统剪贴板读取了内容,那么 data 将是调用 writeText()之前 剪贴板中的旧数据。这个顺序差异构成了可被利用的竞态窗口。 竞态条件的微观时序 任务队列的调度间隙 浏览器主线程执行模型: 用户点击,click 事件处理开始。 调用 writeText(),UA 创建一个“写剪贴板”任务并将其排入任务队列,同时返回一个 Promise。 继续执行,调用 readText(),UA 创建一个“读剪贴板”任务排入队列,返回…

虚悬页表

摘要 内核页表隔离 (KPTI) 是防御 Meltdown 类攻击的关键机制,通过分离用户态与内核态页表,确保用户态代码无法直接窥探内核地址空间。在 ARM64 上,KPTI 由 CONFIG_UNMAP_KERNEL_AT_EL0 和 kpti 引导参数控制,利用 TTBR0_EL1 与 TTBR1_EL1 的切换将内核映射仅在异常入口时临时暴露。然而,ARM 体系结构的维护操作(Maintenance Operations)存在一个隐蔽缺陷:部分 SoC 的 TLBI(TLB Invalidate)指令和 IC IALLU 序列仅刷新 TLB 而不冲刷页表缓存(PTC, Page Table Cache),导致在异常返回(ERET)后,TTBR1 中残留的中间页表项仍可通过推测执行或预取被用户态感知。攻击者利用性能监控单元(PMU)的精确计数器,在用户态测量内核地址访问的延迟差异,即使 KPTI 已重新隔离页表,仍可通过这些“虚悬的页表”还原内核基址,从而击碎 KASLR。 双重身份页表 两个TTBR ARM64 的虚拟地址空间分为两个区域:0x0000_0000_0000_0000 到 0x0000_FFFF_FFFF_FFFF 为用户空间,0xFFFF_0000_0000_0000 以上为内核空间。当 MMU 进行地址翻译时,根据虚拟地址的最高位自动选择页表基址寄存器:用户地址使用 TTBR0_EL1,内核地址使用 TTBR1_EL1。在未启用 KPTI 的情况下,两个寄存器都指向同一个页表(通常包含内核映射),用户态代码虽不能直接访问内核内存(受页表权限 U/S 位控制),但可以通过侧信道探测 TLB 存在性。 ======================= 正常态:用户空间 (EL0) ======================= CPU 视角: +——————–+ +——————–+ | TTBR0_EL1 (受限表) | —-> | 仅映射:用户空间 | (只读内核映射表) +——————–+ +——————–+…