文件字节离开了 Qubes OS 最受信任的域,危险的数据却沿原路返回。
Qubes 安全团队在一份日期标为 2026 年 8 月 28 日、于 8 月 29 日发布的公告中披露了 QSB-118,这是 qvm-copy-to-vm 中的一处任意命令注入漏洞。当用户把文件从 dom0 复制到一个已经遭攻击者控制的 qube 时,目标端可以在传输错误确认消息中返回精心构造的文件名。dom0 随后将这个名称嵌入命令字符串,再把整段字符串交给 shell。攻击者没有把程序复制进 dom0;原本用于错误对话框的一个字段变成了程序。[1]
影响之所以严重,在于 dom0 管理着整个 Qubes 系统。触发条件却比标题所示的范围窄:攻击者须先控制一个 qube,随后还要由用户从 dom0 向同一个 qube 发起 qvm-copy-to-vm。公告没有报告现实环境中的利用活动,本文复盘的是一条已经披露的代码路径,不代表任何用户的机器已经失陷。[1]
这一区别至关重要,架构层面的经验同样如此。Qubes 的设计目标是让一个安全域失陷后,其他域仍能保持安全。[3] QSB-118 没有推翻隔离设计;它暴露的是一个特权协议端点:人们一直按单向外发文件来理解这项操作,底层协议却存在双向通信。
封面照片拍下了 Qubes OS 创始人 Joanna Rutkowska 于 2014 年在 LinuxCon Europe 演讲的情形。[8] 它只提供历史背景,与事件证据无关。十多年后,QSB-118 展示了这类项目为自身设定的披露标准:公开触发前提、确切的可信代码路径、受影响版本范围和修复办法。这样的精度让防御者既能正视漏洞,也能避免把一条通往 dom0 的攻击途径扩写成没有证据支撑的失陷结论。
返回路径藏在确认消息里
Qubes 刻意让 dom0 保持精简。文档称它为“精简的可信终端”,建议避免向其中复制资料,并提供 qvm-copy-to-vm <target_vm> <file>,让用户沿着看起来更安全的方向发送文件,也就是从 dom0 发往 qube。目标端会把文件存入 /home/user/QubesIncoming/dom0/。[4]
这个方向只描述了载荷的去向,没有涵盖整段通信。qvm-copy-to-vm 使用一种小型归档协议 qfile。目标端接收数据后,会向源端发送确认消息,其中包含校验和、错误代码及最后收到的文件名。如果目标端报告错误,dom0 会把返回的名称写进 GUI 消息。[1]
对于正常工作的对端,这个字段用来说明哪个文件传输失败。对端一旦失陷,它就成了从 qube 跨入 dom0、受攻击者控制的输入。
存在漏洞的路径分成两个阶段:
wait_for_result()将返回的名称交给sanitize_remote_filename()。该函数替换控制字符、非 ASCII 字符和双引号,却保留了其他可打印的 shell 元字符。- 发生错误时,
call_error_handler()经由gui_fatal()进入 dom0 的display_error()。这个函数为kdialog或zenity拼出一条命令,将消息插入其中,再调用system()。[1]
第一阶段让字符串适合显示,却没有消除 shell 解释带来的风险。命令用单引号包住消息,清理函数却不移除单引号,这一点尤其直接。更广泛地说,可打印 ASCII 中仍有字符在 shell 语法里具有含义。system() 一旦解析拼接后的文本,目标端返回的文件名便无法保证继续作为一个静态参数存在。[1][2]
这处漏洞发生在报告路径上:归档解析器拿到恶意状态后,准备说明出了什么问题。文件归档解析器接收复杂对象这一环节本身没有出错。错误分支获得的审查往往少于成功处理数据的流程,可它恰恰会在敌对对端送来异常状态时运行。
复制方向划错了信任模型
qfile 的设计有其合理历史。Qubes 早期通过带有文件系统的块设备传送文件,接收 VM 的内核因而要解析由潜在不可信发送方控制的分区表和文件系统元数据。项目后来改用基于 qrexec 的小型协议和受约束的解包器;现代传输通过标准输入和标准输出流动,不再挂载由攻击者塑造的磁盘映像。[5]
QSB-118 位于这项改进之外的下一层。内容通道收窄了,交互过程仍然需要控制消息。校验和可以告诉源端双方看到的字节是否一致,却无法让目标端提供的文件名获得可信属性。用户的一次操作可以授权传输,但不能让选定 qube 返回的每条消息都自动成为 dom0 的可信数据。
因此,更合适的边界定义需要同时描出三个方向:
- 文件内容从源端流向目标端;
- 确认消息、校验和、状态码和诊断文本从目标端流向源端;
- 在权限较高的端点上,每个字段都按不可信输入处理,与哪一方发起操作无关。
最后一条能捕捉单箭头数据流图遗漏的部分。错误详情、日志标签、进度消息、通知标题和对端报告的资源名称全都是输入。用途看起来无害,也不会抬高发送方的信任等级。
Qubes 的安全目标着眼于隔离域之间的强隔离;同一隔离域内部,各应用不享有同等级别的安全隔离。[3] qube 一旦失陷,攻击者就能以该 qube 的身份通信。dom0 端点必须据此处理边界另一侧传来的每一个字节。QSB-118 的确需要用户打开特定通道,但用户的意图是“把这个文件复制出去”,其中没有“让目标端编写一段 dom0 shell 程序”的授权。
4.3.22 版移除了命令解释器
修复方案没有继续扩充 shell 标点黑名单。提交 1a7840a 从 file-copy-vm/qfile-dom0-agent.c 中移除了 system()。新代码先把供人阅读的消息格式化到缓冲区,再调用 fork(),并用 execlp() 启动 kdialog 或 zenity。对话框文本作为一个独立参数传入,不再拼接成 shell 的源代码。[2]
关键的语义变化就在这里。使用参数向量 API 后,消息里的标点只会留在一个参数内部。此时没有 shell 语法可让这些字符获得第二层含义。显示层清理仍可提高诊断信息的可读性,却不再承担证明任意文本在所有 shell 上下文中均安全这一项无法完成的任务。
项目中已有一份可供对照的实现:VM 内部使用的错误报告代码通过 fork() 和 execlp() 启动对话框,没有调用 system(),因此公告认定该变体不受影响。dom0 补丁采用了同样更安全的进程边界。[1][2]
公告将所有 Qubes OS 版本列为受影响范围,并把 qubes-core-dom0-linux 4.3.22 列为 Qubes 4.3 的修复软件包。给用户的指示是继续按常规方式更新;公告发布时,该软件包计划经过短暂的社区测试期后,从 security-testing 转入稳定软件仓库。[1] 这个时间安排值得区分:公告确定的是修复版本。用户仍应避免安装未经验证的文件,也不能据此认定已结束生命周期的版本会收到等效软件包。
后果严重,触发条件有限
评估响应时,需要把后果与可达条件分别处理。
如果整条利用链完成,命令将在 dom0 中运行,攻击者由此可以控制 Qubes OS。[1] 后果波及整个系统,远超一个损坏的复制对话框。然而,仅安装了存在漏洞的软件包仍不足以触发攻击。公告记录的路径需要同时满足以下条件:
- 攻击者已经控制一个 qube;
- 用户主动从 dom0 向该 qube 复制文件;
- 恶意错误确认消息进入存在漏洞的 dom0 报告程序;
- dom0 软件包属于受影响版本。[1]
公告指出,日常 qube 间传输不会经过这条特定的 system() 路径。[1] 公告也未称攻击者能在缺少用户操作时远程发起 dom0 复制。分诊应以这些限制为依据:更新每一套仍受支持的系统,再重点调查曾使用 qvm-copy-to-vm 向疑似带有敌意的 qube 传输文件的环境。
对于历史上的可疑传输,补丁能防止攻击重演,却无法判定旧事件是否已经成功。应在常规保留期限清除记录之前保存相关 dom0 和 qube 日志,记录软件包版本,查明当时选定的目标端与文件,并把无法解释的 dom0 侧效应按安全事件处理。同时,不能仅因系统曾安装存在漏洞的软件包就认定已经失陷。公开记录的攻击能力只能证明风险存在,不能证明某台具体机器遭到了利用。
审计返回消息,再审计程序启动位置
第一轮后续审计沿协议展开。每一项从低信任组件跨入特权组件的操作,都要列出两个方向,包括只在拒绝、超时、部分完成或清理阶段出现的消息。每个文件名、错误字符串、标识符、计数和状态对象来自哪里,应在其使用位置标明,不能沿用整体操作的起点来判定。
第二轮审计沿程序启动展开。在特权组件中搜索 system()、popen()、sh -c 和拼接出来的命令字符串。应直接判断 shell 是否确有必要,逃逸函数是否正确只居其次。当程序及其选项都已知时,应使用接收固定程序和独立参数向量的执行 API。如果产品要求中确实包含 shell,应隔离固定脚本,并对跨入脚本的每个变量采用严格类型约束或允许列表。
随后测试失败路径。目标端测试工具应在每一种错误代码下返回包含空白字符、引号、展开语法、前导连字符、无效编码、最大长度文本和终端控制字符的文件名。测试断言要覆盖对话框出现之后的行为:系统只启动一个预期程序,每个选项都留在指定的参数位置,消息只作为数据显示,且不产生其他效果。这类测试能覆盖普通成功复制用例从未触及的路径。
最后还要按实际比例看待更大的公开记录。2026 年 7 月的一项分析统计了截至 2025 年的 109 份 Qubes 安全公告,其中 87 份——占 79.8%——主要归因于 Xen、CPU 或微架构,以及其他上游组件,源于 Qubes 核心逻辑的公告占少数。作者同时提醒,公开公告记录衡量的是已披露公告,无法反映潜藏漏洞的实际发生情况。[7] QSB-118 的特殊之处恰在于它是一项 Qubes 核心边界错误;不能由此推演出所有隔离措施都已失效。
独立的实际运维记录说明了这一区分为何重要。Guardian 的工程团队曾介绍如何用 Qubes 搭建离线、可重置的环境,用于处理敏感文档工作流,同时明确把适用范围限定为一个组织的经验,未将其作为普遍指引。[6] 那篇文章早于 QSB-118,也无法证明相关环境曾暴露于该漏洞。它呈现的却正是这类高后果工作:明确触发前提、及时更新并诚实分析信任边界,比安全表演或恐慌更有价值。
QSB-118 留下的长期经验可以直接表述:数据流方向不等于权限流方向。文件离开了 dom0,诊断消息随后返回。漏洞就藏在特权代码忘记第二段行程同样跨越边界的那一刻。
来源
- Qubes 安全团队,“QSB-118: Dom0 arbitrary code execution in qvm-copy-to-vm error reporting”——触发前提、qfile 确认字段、存在漏洞的调用路径、受影响版本、补丁安排和致谢。
- Marek Marczykowski-Górecki,“Do not use system() with untrusted parts of the command”,QubesOS/qubes-core-admin-linux 提交
1a7840a——dom0 错误对话框从命令字符串改用fork()和execlp()的补丁。 - Qubes OS 文档,“Security design goals”——隔离目标、域边界和跨域功能。
- Qubes OS 文档,“How to copy from dom0”——
qvm-copy-to-vm、目标路径和 dom0 的精简可信终端边界。 - Qubes OS 文档,“Inter-qube file copying (qfilecopy)”——从块设备转向 qrexec 的过程、解析器设计理由和受约束的目标端。
- The Guardian Engineering Blog,“When security matters: working with Qubes OS at the Guardian”——离线、可重置 Qubes 工作流的独立运维记录。
- Alfonso De Gregorio,“Qubes OS Security in the Public Record”,arXiv:2607.14587——对 2011 至 2025 年 QSB 归因的独立分析,以及对公告计数证据局限的明确说明。
- Krd,“LinuxCon Europe Joanna Rutkowska 03”,Wikimedia Commons——档案照片的作者、日期、活动说明、原始尺寸和 CC BY-SA 4.0 许可。