“guix substitute”和“guix pull”漏洞

Lobsters Hottest 新闻

摘要

GNU Guix 披露了 'guix substitute' 和 'guix pull' 中的多个安全漏洞,这些漏洞允许远程权限提升至构建守护进程用户、远程存储损坏、潜在的本地文件泄露以及拒绝服务;用户应立即升级。

<p><a href="https://lobste.rs/s/xg4bbg/guix_substitute_guix_pull">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/03 08:21

# ‘guix substitute‘ 与 ‘guix pull‘ 漏洞 — 2026 — 博客 — GNU Guix 来源:https://guix.gnu.org/en/blog/2026/guix-substitute-pull-vulnerabilities/ ## ‘guix substitute‘ 与 ‘guix pull‘ 漏洞 Caleb Ristvedt — 2026 年 7 月 2 日 在 `guix substitute`(由 `guix-daemon` 调用的辅助工具)中发现了若干安全漏洞(CVE 编号待分配),这些漏洞使得多种有害活动成为可能,包括**远程权限提升至构建守护进程用户**、**远程存储损坏**,以及可能**本地泄露构建守护进程用户可访问的敏感文件**。所有系统均受影响,无论 `guix-daemon` 是否以 root 权限运行;当 `guix-daemon` 未以 root 权限运行时,可能造成的危害较为有限。强烈建议您**立即升级您的守护进程**(参见下方说明),**并在升级时仔细考虑是否要对所有 guix 命令传递 `--no-substitutes`(参见升级部分中的说明)**。对 `guix substitute` 的远程利用仅要求受影响的系统尝试下载二进制替代项。任何配置的替代服务器(包括使用 `guix-daemon` 的 `--discover` 选项发现的服务器)均可利用此漏洞,中间人攻击亦可,无论替代服务器 URL 是否使用 `https`。对 `guix substitute` 的本地利用仅需能够连接到 `guix-daemon` 的套接字(默认情况下任何用户均可做到)。此外,在 `guix pull` 和 `guix time-machine` 中发现了另一个安全漏洞(CVE 编号待分配),使得任何能控制这些命令所使用的 channels 文件的人,都可以在运行该命令的用户拥有权限的任何位置创建或覆盖文件。无论 channels 文件是否在沙箱中求值,以及所用 channels 是否仅限于与受信任 channel 共享介绍信息的那些,此漏洞均可能发生。由于创建或覆盖文件的内容受限,这主要构成**拒绝服务**风险,但理论上可能造成更大危害。 ## 漏洞 影响 `guix substitute` 的三个不同漏洞已被发现,另有第四个漏洞影响 `guix pull` 和 `guix time-machine`: 1. (CVE 分配待定)Guile 代码用于解包替代项的过程——`(guix serialization)` 中的 `restore-file`——并未针对恶意输入进行加固,但它在下载替代项的同时就被调用来提取下载内容,而不是等到整个归档获取完毕*并验证其哈希值之后*。这些事实共同使得任何替代服务器(或任何能冒充该服务器的实体)都可以将任意文件写入受影响系统上守护进程用户有写入权限的任何位置。如果守护进程以 root 身份运行,这包括 `/etc/passwd`。为了避免依赖 X.509 公钥基础设施,获取可用替代项元数据(称为 *narinfo*)的过程 `fetch-narinfos` 不会验证服务器证书,因为 narinfo 的规范部分无论如何都需要经过签名才被视为有效。不幸的是,替代 URL 并非此类规范部分,因此它可被替换为攻击者控制的 URL。如果下载的替代项与 narinfo 中签名的哈希值不匹配,它将被拒绝,但那时已为时已晚:替代项在下载过程中已被提取,因此损害已经造成。这意味着尽管实际负责下载替代项的过程 `download-nar` 本身会验证服务器证书,在替代服务器 URL 中使用 `https` 也无法限制谁可以利用此漏洞,因为证书只需适用于攻击者控制的 URL 即可。 `restore-file` 还被其他工具使用,包括 `guix offload`、`guix archive --extract` 和 `guix challenge`。如果这些工具接收到不可信输入,它们也会以相同方式被利用。 2. (CVE 分配待定)获取可用替代项元数据(称为 *narinfo*)的过程——`(guix substitutes)` 中的 `fetch-narinfos`——不会验证它获取到的 narinfo 是否正是它请求的那个,其调用者(在 `(guix scripts substitute)` 中)也均未验证。因此,替代服务器(或任何能冒充服务器者)可能欺骗 `guix substitute`,使得任何存在授权替代项的存储项被用作任何其他也存在授权替代项的存储项的替代项。这所能造成的危害范围部分取决于授权替代服务器已签名或可以被说服签名的存储项,但至少可用于导致使用过时和不安全的软件版本。 3. (CVE 分配待定)`(guix scripts substitute)` 中 `guix substitute` 的实现允许使用 `file://` URI 来指定替代服务器 URI(用于查找 narinfo)以及在 narinfo 内指定从哪里下载相应归档。它不区分在 `guix-daemon` 命令行上传递的 `--substitute-urls` 和在 `guix` 命令行上传递的 `--substitute-urls`(客户端侧),后者优先于前者。这些 `file://` URI 的打开会遵循符号链接。因此,不可信客户端可能导致守护进程可读取的任何文件被读取。如果某一行看起来不像有效的 narinfo 行(它使用 recutils 格式),`guix substitute` 可能抛出异常,导致包含该行的回溯信息被传递给客户端。例如,一个只包含一行秘密口令的文件,如果守护进程用户可读取它,则其内容可能被任何本地用户泄露。此外,当 `file://` URI 被用作要下载的 nar 的 URI 时,如果它恰好是一个 Guix 和 Nix 使用的有效 nar(“规范化归档”),则可能被写入存储。尽管可能性不大。除了可能导致秘密泄露外,这还可用于干扰守护进程用户可跟踪的任何进程正在读取的任何文件(通过 `/proc/PID/fd` 中的文件)。 4. (CVE 分配待定)`guix pull` 和 `guix time-machine` 用于认证 channel 的过程——`(guix channels)` 中的 `authenticate-channel`——将源自 channel 名称的缓存键传递给 `(guix git-authenticate)` 中的 `authenticate-repository`。该缓存键用于确定一个存储先前已认证提交 ID 的文件名。如果 channel 名称形如“../../../../newfile”,则可能导致在用户主目录中创建“newfile”。它也可能覆盖已存在的“newfile”,但仅当该文件看起来已经像 Scheme 语法字符串列表时(因为内容必须先被读取和处理,然后才能写入新内容)。若执行写入操作,输出将只包括一个 Scheme 语法注释、换行符和与 git 提交标识符对应的十六进制字符串列表。这使得它难以用于实际攻击(除了拒绝服务),但请注意,由于它可以针对 `/proc` 中的文件,足够有创造力和知情度的攻击者可能进一步利用此漏洞。实际上,此漏洞只能在通过新增的获取远程 channel 文件的机制(https://guix.gnu.org/blog/2026/time-travel-without-borders/)获取远程 channel 文件时被利用。 ## 缓解措施 针对远程攻击者,漏洞 (1) 和 (2) 可通过不使用替代项来缓解,即向 `guix-daemon` 传递 `--no-substitutes`,或向所有 `guix` 命令传递 `--no-substitutes`。不过,仍然可以为单个客户端重新启用替代项,因此这无法防御利用 (1)、(2) 或 (3) 的本地攻击者,这些攻击无法缓解而必须通过更新修复。漏洞 (4) 可通过不使用不可信的 channels 文件运行 `guix pull` 或 `guix time-machine` 来缓解。 本博文末尾提供了一个检测这些漏洞是否存在的测试。可使用以下命令运行此代码: `` guix repl -- guix-substitute-and-pull-vuln-check.scm `` 执行后将以 4 行输出结束,分别以 `restore-file`、`fetch-narinfos`、`file-uris` 和 `cache-key` 开头,每行后跟一个冒号、一个空格,以及 `vulnerable` 或 `not vulnerable`,取决于正在运行的 `guix-daemon` 是否存在相应漏洞。如果所有 4 行均包含 `not vulnerable`,则 `guix repl` 以状态码 0 退出,否则以状态码 1 退出。某些测试在某些情况下可能无法产生结果。此时测试名称后的输出将以 `error:` 开头。无法产生结果的测试应视为不确定。如果没有任何替代项被授权,或通过任何配置的替代 URL 都无法访问当前 guix 的 `cfunge`、`hello` 或 `sed` 包的授权替代项,则 `restore-file` 和 `fetch-narinfos` 测试可能无法产生结果。如果 `cfunge` 可从某些垃圾回收根(如 profile)到达,则 `restore-file` 测试可能无法产生结果。如果无法通过 Codeberg 网络访问以获取 `guix-science` channel 的一小部分历史记录,`cache-key` 测试可能无法产生结果;可以通过将脚本中 `guix-science-url` 的定义修改为任何 URL(或文件名),其中包含 `guix-science` 仓库的副本,来解决此问题。 ## 修复 这些安全问题已通过一系列 11 次提交修复,从 ed0a9721f8a20d6ddcf6a0495302f502b3f7bb17(https://codeberg.org/guix/guix/commit/ed0a9721f8a20d6ddcf6a0495302f502b3f7bb17)开始,到 2ef8ed9f0df53bddf14bdecc2ea48c2d233213cc(https://codeberg.org/guix/guix/commit/2ef8ed9f0df53bddf14bdecc2ea48c2d233213cc)结束,作为拉取请求 #9665(https://codeberg.org/guix/guix/pulls/9665)的一部分。用户应确保已升级到提交 897832f374dcdc9eeaf19d01e70b9a92fccfc68c(https://codeberg.org/guix/guix/commit/897832f374dcdc9eeaf19d01e70b9a92fccfc68c)或任何后续提交,以防范这些漏洞。升级说明见下一节。 修复漏洞 (1) 涉及加固 `restore-file`,使其检测并拒绝无效的目录条目名称。具体来说,条目名称必须唯一、严格升序、非空、不等于 "." 或 "..",且不包含 "/" 或空字节。此外,当前用作 `restore-file` 的 `#:dump-file` 参数的过程已修改为强制重新创建目标文件且绝不遵循符号链接。最后一项更改的灵感来自于查看 `nix/libutil/archive.cc` 中 `parse` 的 nar 解析实现,其中确定该实现不易受攻击,但几乎达到了易受攻击的程度而未实际出现问题,仅因所使用的文件系统原语全部拒绝遵循符号链接或接受现有目标而得以幸免(详见此提交消息(https://codeberg.org/guix/guix/commit/3e5c3217f334805531e5db1680d97490999253f7))。为了避免下次有人以批判眼光查看它时再浪费 3 小时来确定这一点,该实现也被重写,更严格且更明显安全。当 Nix 中使用 `std::filesystem` 的更宽松的文件系统原语时,相同的代码导致了 CVE-2024-45593,这或许并不令人惊讶。 修复漏洞 (2) 涉及修改 `fetch-narinfos`,使其不包含与请求不匹配的结果。 修复漏洞 (3) 涉及修改 `(guix scripts substitute)`,以验证来自不可信来源的所有替代 URL 不是 `file://` URL,并且 narinfo 中的所有 nar URL 不是 `file://` URL(除非设置了特殊标志,该标志仅在测试套件中设置)。还进行了一些额外的加固,使得替代项在临时目录中恢复,并且仅在哈希值验证后才移动到最终的存储项路径。这仍然在验证哈希值之前恢复它们,因此无法阻止 (1),但确实确保攻击者控制的内容不会出现在可能曾是有效存储项(如果用户或程序未注意到最近的垃圾回收,可能仍认为它有效)的路径上。narinfo 读取代码也已修改,若 narinfo 文件的 StorePath、References 或 Deriver 字段包含不满足存储项路径语法要求的路径,则拒绝该 narinfo 视为无效。这的一个好处是我们现在有了验证存储项路径语法的过程。 修复漏洞 (4) 涉及更改 `authenticate-repository` 的默认 `cache-key` 计算方式。`cache-key` 现在不再源自 channel 的名称或(对于 `guix git authenticate`)仓库的 URL,而是源自介绍性提交的 ID(一个非常安全的十六进制字符串)。这也避免了某些奇怪且潜在危险的行为,即两个名称相同但其他方面完全不同的 channel 可能共享缓存的已认证提交 ID。对 `authenticate-repository` 进行了额外加固,将 `cache-key` 中所有出现的 `.` 转换为 `-`,这样即使提供了非默认缓存键,也无法逃逸出缓存目录。 ## 升级 鉴于本安全公告的严重性,**我们强烈建议所有用户立即升级 `guix` 和 `guix-daemon`**。 > **注意**:细心的读者可能注意到了两难处境:获取更新的最快方式是通过替代项,而缓解最严重的远程可利用漏洞的方式是禁用替代项。因此是否传递 `--no-substitutes` 是一个判断决定,必须考虑这些漏洞公开了多久、您系统的网络路径与替代服务器之间的暴露程度、相关系统自行构建 guix 的可行性(部分取决于自上次升级以来过了多久)、相关系统是否有多个用户,以及当然还有您的威胁模型。 **对于 Guix System**,步骤(https://guix.gnu.org/manual/devel/en/html_node/Getting-Started-with-the-System.html)是在 `guix pull` 后重新配置系统,然后重启 `guix-daemon` 或重启系统。例如: `` guix pull sudo guix system reconfigure /run/current-system/configuration.scm sudo herd restart guix-daemon `` 其中 `/run/current-system/configuration.scm` 是当前系统配置,但当然可以替换为用户选择的系统配置文件。 **对于其他发行版上的 Guix**,需要使用 `sudo` 执行 `guix pull`(因为 `guix-daemon` 以 root 身份运行),并重启 `guix-daemon` 服务,如文档所述(https://guix.gnu.org/manual/devel/en/html_node/Upgrading-Guix.html)。例如,在 systemd 管理服务的系统上,运行: `` sudo --login guix pull sudo systemctl restart guix-daemon.service `` 请注意,对于使用其发行版打包的 Guix(而非使用安装脚本(https://guix.gnu.org/en/manual/en/html_node/Binary-Installation.html))的用户,可能需要采取其他步骤或升级 G

相似文章

无国界的时间旅行

Lobsters Hottest

本文探讨了 GNU Guix 的 `time-machine` 和 `pull` 命令的一项新功能,该功能支持通过单行命令从频道部署软件,同时解决了下载并执行不受信任代码所引发的安全问题。

Nix Flakes 及其在 Guix 中的对应物

Lobsters Hottest

详细比较 Nix Flakes 与 Guix 包管理系统中的对应物,涵盖依赖声明、锁定、纯净性、输出、开发环境和系统配置。