Qubes OS 官方在 QSB-118 里给出的用户建议只有一句话:“继续正常更新”。但把技术细节拆开看,这次的触发方式和后果,都不太像“正常更新”能轻松兜住的小修小补——一个从 dom0 传到已被攻陷 qube 的文件名,回传时能直接变成一条在 dom0 里执行的 shell 命令,拿下的是整台机器权限最高的管理域。

一个报错弹窗,怎么就成了命令注入

qvm-copy-to-vm 是 dom0 向某个 qube 复制文件的工具,走的是 Qubes 自研的轻量协议“qfile”。传输结束后,目标 qube 会回传一个确认包,里面带校验和、错误码,还有“最后接收的文件名”。

传输出错时,dom0 要把这个文件名显示进报错弹窗。为此有一道过滤函数 sanitize_remote_filename(),专门清洗这个不可信的名字——但它只干了一半的活:只替换非 ASCII 字符和双引号,shell 元字符原样保留。

清洗完的文件名接着被拼进一整条命令字符串,交给 system() 执行,用来弹出 kdialog 或 zenity 提示框。文件名里藏的特殊字符,这时候就能跳出双引号,变成一条被 dom0 直接执行的任意命令。

触发条件很明确:攻击者先要攻陷某个 qube,再等用户主动从 dom0 对这个 qube 发起一次复制。攻击面不算宽,但一旦踩中,拿到的是 dom0 的完全控制权——Qubes 安全模型里最高的一级。

攻击链:文件名如何变成dom0命令 已攻陷qube 回传文件名 藏有shell字符 sanitize() 只过滤引号 漏掉shell元字符 system() 拼接成字符串 交给shell执行 dom0命令注入 攻击者拿下 最高权限域

VM那头没这个问题,dom0这头却栽了

耐人寻味的是,qvm-copy-to-vm 在 qube 一侧的实现完全没受影响。同样要弹报错框,VM 端用的是 execlp(),参数一个个分开传进去,不经过 shell 解释,天然不怕特殊字符。

dom0 端偏偏选了 system(),把整条命令拼成字符串扔给 shell 跑。两条本该对称的代码路径,一边是加固过的写法,一边是教科书式的 command injection 隐患,多年没被统一审查出来。

同一功能,两种写法 dom0端 system() 拼接字符串交给shell 特殊字符可被注入 VM端 execlp() 参数分开传,不走shell 早就避开了同类问题

这也是 Qubes 隔离哲学里一个不太被讨论的盲区。整套系统的假设是“dom0 永远不信任 qube”,单向隔离做得很严。但 qvm-copy-to-vm 这种由 dom0 主动发起、又要等 qube 回传数据的操作,恰恰是隔离方向被悄悄反转的地方——数据从“不可信”的一端流回了“最可信”的一端,处理这条回传数据的代码,却没被同等级别地当成攻击面对待。

隔离越严实,越该小心它反着流的那一段。

“正常更新”四个字,盖住了一个版本缺口

官方公告说所有 Qubes OS 版本都受影响,但给出的修复包只有一个:Qubes 4.3 对应的 qubes-core-dom0-linux 4.3.22,修复方式是把 system() 换成 fork() + execlp(),参数分开传,彻底绕开 shell 解释。修复 commit 的标题直接写着“不要在命令里用不可信的部分调用 system()”,病根说得很透。

公告没提 4.2 及更早版本的对应补丁号。“继续正常更新”这句话,对 4.3 用户是真的成立;对还在用旧分支的用户,目前更接近“已知受影响、暂无补丁”的悬空状态。

  • 风险.4.2及更早版本用户目前只能等待,该漏洞未拿到修复版本号,也没有分配 CVE 编号。
修复覆盖了谁,没盖住谁 Qubes 4.3 4.3.22 已修复 4.2及更早 / CVE编号 未列版本 · 未分配

企业侧的漏洞管理大多靠 CVE 扫描器跑起来,一个只活在 QSB 列表里、没有 CVE 号的高危漏洞,很容易在合规流程里被漏掉。这是 Qubes 自建公告体系和主流漏洞生态之间的一处脱节,跟修复本身的质量无关,却直接影响漏洞能不能被发现。


高安全系统的漏洞,往往漏在自己最自信的地方

黑名单式过滤漏掉 shell 元字符,是安全代码里最古老的一类错误,几十年来在各种语言、各种项目里反复出现,连 Qubes 这种把隔离做到操作系统级别的项目也没能例外。这类漏洞的共性不是技术不够,而是防线画错了地方——大家把注意力都放在 qube 到 dom0 的主通道上,却没把报错信息这种“边角流程”当成正经的攻击面。

  • 结论.黑名单过滤漏掉特殊字符,是安全代码里最古老、也最容易被反复忽视的一类错误。

Qubes 这次的应对不算慢,补丁发得干净,把病根从 system() 连根换成 execlp(),不是打个补丁糊过去。但“所有版本受影响、只有一个版本有修复”这中间的空档,以及始终没拿到 CVE 号这件事,都是官方公告没主动摆到台面上的部分。能做的事很具体:先把 4.3 更新到位;还在用旧分支的人,补丁出来之前,最好先别用 dom0 给存疑的 qube 发文件。