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 的回复体内部,可以放置一条任意的方法调用(这是…