【译文】XZ 后门的工作原理

版本 5.6.0 和 5.6.1 的 XZ 压缩实用程序和库附带了一个针对 OpenSSH 的后门。安德烈斯-弗罗因德(Andres Freund)在进行微基准测试时注意到 SSH 登录失败占用了大量 CPU 时间,从而发现了该后门,并由此追踪到了该后门。它是由 XZ 的共同维护者 “Jia Tan”(可能是不明身份者的别名)引入的。该后门是一种复杂的攻击,包含从 build 、link 到 run 的多个阶段。

社区对该攻击的反应与技术方面一样有趣。有关这方面的更多信息,请参阅这篇配套文章。

build 阶段

后门由几个不同的阶段组成,从软件包构建时开始。Gynvael Coldwind 写了一篇关于后门构建时间部分的深入调查。XZ 的发布是通过 GitHub 提供的,但后来 GitHub 关闭了维护者的账户,并将发布离线。与许多使用 GNU Autoconf 的项目一样,XZ 发布的版本提供了多个版本的源代码供下载–自动生成的压缩包包含源代码和版本库中的相关文件,以及包含生成的构建文件的版本。这些额外的文件包括项目的 configure 脚本和 makefile。发布包含生成文件的版本可以让软件的下游用户无需安装 Autoconf 即可进行构建。

但在本例中,维护者提供的源代码压缩包中的脚本并非由 Autoconf 生成。相反,其中一个 build 脚本在 m4/build-to-host.m4 中包含了利用漏洞的第一阶段。该脚本最初来自 Gnulib 库;它提供了一个宏,用于在构建环境使用的路径名样式与程序运行时环境之间进行转换。这些 XZ 版本中的脚本经过修改,以提取下一阶段的漏洞,该漏洞包含在 tests/files/bad-3-1corrupt_lzma2.xz。

表面上看,该文件是 XZ 测试套件的一部分,但它从未被这些测试使用过。该文件早在 5.6.0 版发布之前就已提交。该文件据说是一个已损坏的 XZ 文件,实际上是一个有效的 XZ 数据流,其中一些字节被交换了,例如,0x20 与 0x09 被交换,反之亦然。解码后,会生成一个 shell 脚本,用于解包和执行下一阶段的后门。

后门的下一阶段位于 tests/files/good-large_compressed.lzma。这就是 Freund 信息所附的 injected.txt 文件。该文件不仅包含下一阶段的脚本,还包含构成后门本身的附加二进制数据。最后的脚本会跳过从中提取的文件头,然后使用 awk 解密文件的剩余部分。最后,使用 XZ 命令行程序对解密后的数据流进行解压缩,以提取一个名为 liblzma_la-crc64-fast.o 的预编译文件,该文件也附在 Freund 的信息中。

link 阶段

提取的文件是一个 64 位可重置 ELF 库。build 过程的剩余部分会将其链接到最终的 liblzma 库,在某些发行版中,该库最终会被加载到 OpenSSH 中。这些发行版对 OpenSSH 进行了修补,以使用 systemd 发送守护进程就绪通知;而 libsystemd 又依赖 liblzma 来压缩日志文件。Lennart Poettering 发布了一些示例代码(由 Luca Boccassi 编写),展示了如何让应用程序使用 systemd 就绪通知,而无需调用整个库。当恶意 liblzma 被动态链接进程使用时,它会使用间接函数机制将自己卷入链接过程

间接函数是 GNU C 库(glibc)的一项功能,它允许开发人员包含一个函数的多个版本,并在动态连接时选择使用哪个版本。例如,间接函数适用于包含依赖于特定硬件特性的函数的优化版本。在这种情况下,后门提供了自己版本的间接函数解析器 crc32_resolve() 和 crc64_resolve(),可分别选择要使用的 crc32() 和 crc64() 版本。这种似是而非的推诿可能就是漏洞利用本身存在于 liblzma_la-crc64-fast.o 文件中的原因。

当动态链接器确定这些函数的位置时,它就会调用后门的解析器函数。此时,动态链接仍在进行中,因此链接器的许多内部数据结构还未被设置为只读。这样,后门程序就可以通过覆盖过程链接表(PLT)或全局偏移表(GOT)中的条目来操纵已经加载的库。然而,liblzma 在 OpenSSH 的链接顺序中加载得相当早,这意味着作为后门最终目标的 OpenSSL 密码学函数可能尚未加载。

为此,后门程序添加了一个审计钩子。动态链接器在解析符号时会调用所有已注册的审计钩子。后门程序利用它来等待 RSA_public_decrypt@got.plt 符号被解析。尽管名称如此,但该函数实际上是处理 RSA 签名(解密操作)的一部分 – OpenSSH 在连接过程中验证客户端提供的 RSA 证书时调用了该函数。

运行阶段

一旦后门检测到该函数被链接,它就会用自己的版本替换该函数。目前仍在调查被修改版本的功能,但至少有一项功能是试图从所提供的 RSA 证书的公钥字段中提取命令(这意味着在这种攻击中使用的证书实际上无法正常用于身份验证)。后门程序会检查命令是否由攻击者的私钥签名,格式是否有效。如果是,后门程序就会以运行 sshd 的用户(通常是 root)身份直接运行给定命令。

Anthony Weems 对该漏洞的运行时部分进行了解释,其中包括检测使用该漏洞的尝试的蜜罐(honeypot ),以及生成命令有效载荷的代码。使用后门需要用私钥对要执行的命令进行签名,但攻击者的私钥不可用,因此需要对后门服务器打补丁,使其使用另一个私钥。这也意味着远程检测后门服务器几乎是不可能的,因为它们不会对不使用攻击者私钥的连接做出任何不同的反应。

最终,后门的效果似乎是,被入侵的 SSH 服务器在收到使用手工制作的 RSA 证书进行身份验证的连接时,可以运行攻击者控制的代码。

反分析

后门的设计使其在不直接检查 liblzma 的情况下很难被发现。例如,选择启用远程代码执行而不是身份验证绕过,意味着使用该漏洞不会检测到传统管理工具可能注意到的登录会话。后门代码还使用了多种技术来增加发现的难度。例如,审计钩子使用的字符串 “RSA_public_decrypt@got.plt “从未出现在漏洞利用程序的二进制文件中。相反,它使用三元组来保存各种字符串。Serge Bazanski 发布了恶意 liblzma 中以这种方式编码的字符串列表

检查该列表可以发现,RSA_public_decrypt 可能不是唯一受到干扰的函数;列表中还列出了其他几个加密例程。它还显示了用于干扰 OpenSSH 日志记录的各种函数和字符串。这一点尚未得到证实,但被入侵的 SSH 服务器很可能不会记录任何使用该漏洞的连接尝试。

后门还包括许多检查,以确保它在预期的环境中运行–这是现代恶意软件的标准预防措施,目的是增加反向工程的难度。后门只有在特定情况下才会激活,包括:在非图形环境中运行、以root身份运行(参见Freund的评论)、在位于/usr/sbin/sshd的二进制文件中运行、sshd具有预期的ELF头、其所有函数都没有被调试器插入断点。尽管存在这些障碍,但反向工程和解释后门代码其余部分的社区工作仍在进行中

该后门还包括修补 sshd 本身二进制文件的代码,以禁用 seccomp(),防止程序为其子程序创建 chroot 沙箱参见本评论)。后门代码总计 87KB,有足够的空间制造更多令人不快的意外。许多人都对该漏洞进行了总结,其中包括 Sam James 编写的综合常见问题解答,它还提供了其他资源链接。

安全

该漏洞被及时发现,因此几乎没有用户受到影响。Debian sid、Fedora Rawhide、Fedora 40 测试版、openSUSE Tumbleweed 和 Kali Linux 都曾短暂发布过受攻击的软件包。NixOS 不稳定版也发布了被入侵的版本,但由于它没有为 OpenSSH 链接 libsystemd 打补丁,所以不会受到攻击。Tan 还对 XZ 代码进行了其他一些修改,以增加检测和缓解后门的难度,例如破坏沙箱措施和先发制人地重定向安全报告。尽管该漏洞没有进入其稳定版本,但一些发行版仍在采取措施转移到不包含任何 Tan 提交的 XZ 版本,因此用户应期待很快看到与此相关的安全更新。读者也可以参考各自发行版的安全公告,了解更多具体信息。

本文文字及图片出自 How the XZ backdoor works

余下全文(1/3)
分享这篇文章:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注