针对 Flatpak 系统助手(flatpak-system-helper)中路径遍历漏洞的教学性说明与测试模式概念验证。DeployAppstream D-Bus 方法接受一个 arch 字符串,该字符串会被放入文件系统路径并以 root 身份创建,且没有任何验证。一个活跃的本地用户可以传入 ../ 组件,导致这个 root 特权服务在预期的 appstream 目录树之外创建目录。
本仓库配合完整的技术分析与研究事后总结。它的存在是为了教育目的,为了帮助想要理解该机制的防御者,以及为了在您拥有或已获得书面许可进行评估的系统上进行授权测试。
本材料是在协调披露之后、并且在修复后的 Flatpak 版本发布之后发布的。仅对您拥有或明确授权测试的实验室机器运行该概念验证。不要对您不控制的系统运行它。您有责任遵守法律以及您所持有的任何授权范围。
系统助手中的 handle_deploy_appstream() 会验证 origin 参数,但不会验证 arch 参数。对于 OCI 远程仓库,arch 会流入 flatpak_build_file,然后进入 g_mkdir_with_parents,后者以 root 身份运行并以词法方式解析 ../。该方法的 polkit 操作是 org.freedesktop.Flatpak.appstream-update,其设置为 allow_active=yes,因此活跃的本地会话无需密码提示即可到达该代码。在已经存在 OCI 远程仓库的系统上,例如默认的 Fedora Workstation 及其 fedora 远程仓库,低权限用户可以在任意路径创建 root 拥有的目录。本仓库中的载荷针对的是 /root/.ssh 本身。
该漏洞,即发现 A,是在一条双漏洞利用链中演示的,用于在受控且授权的实验室环境中将未认证的本地用户提升为机器上的 root。该利用链的工作方式如下:发现 A 以 root 身份创建 /root/.ssh。发现 B,一个次级写入原语,随后可以将攻击者控制的内容写入该目录。两者结合,便可在攻击者选择的路径上为攻击者提供一个 root 拥有的文件。每个漏洞单独来看都是有限的:发现 A 只能创建目录,而发现 B 只能写入父目录已存在的位置。发现 A 提供了发现 B 所需的父目录,二者结合便成为 root 文件写入。
发现 B 是由本研究独立识别出的,但它并非一个新发现:它是研究事后总结中记录的 Deploy 额外数据写入路径中已知的、仍未修补的弱点。因此,本说明涵盖发现 A 并为其署名,而仅将发现 B 用作该利用链所依赖的已知、未修补组件。
本仓库仅演示发现 A。利用链方法论和防御经验记录在研究事后总结中。
该项目已经提供了 flatpak_is_valid_arch,它将架构名称限制为 [A-Za-z0-9_]。修复方法是在处理程序边界以及路径构建辅助函数内部对 arch 参数调用它。../ 值无法通过该谓词。请更新到修复后的 Flatpak 版本。
.
├── README.md 本文件。
├── requirements.txt PoC 的系统软件包先决条件。
└── poc_arch_traversal.sh 未验证 arch 参数的 PoC。
默认使用沙箱测试模式,生产
模式针对 /root/.ssh。仅供教育用途。
requirements.txt 中软件包的 Linux 机器。allow_active=yes。使用以下命令检查:
loginctl list-sessions 并确认您的会话显示一个 seat。/usr/libexec/flatpak-system-helper;脚本会首先在 Flatpak 源码树的 builddir/ 或 _build/ 中查找新构建的助手,而 FLATPAK_SYSTEM_HELPER 会覆盖一切。
有关所需的系统软件包,请参见 requirements.txt。生产模式会自动检测系统安装上现有的 OCI 远程仓库并使用它。任何 OCI 远程仓库都可以,即使是伪造的或不可达的,因为 mkdir 发生在注册表被联系之前。如果不存在 OCI 远程仓库,脚本会以 "No OCI remote found" 消息取消,并且不发送任何内容。
Fedora Workstation 已经自带 OCI fedora 远程仓库,因此在那里无需做任何事。使用 flatpak remotes --system 检查存在哪些远程仓库(OCI 远程仓库会在其选项列中显示 oci)。
这是整个流程中唯一需要 sudo 的命令,脚本绝不会替您运行它。使用以下命令添加一个:
sudo flatpak remote-add --system --no-gpg-verify oci-poc oci+http://127.0.0.1:19876
(flatpak 没有 --oci 标志,至少到 1.19.0 版本——本工作所基于的版本——都是如此;oci+ URL 前缀才是使该远程仓库成为 OCI 注册表的原因。)当没有注册表运行时,出现 "Warning: Could not update extra metadata" 消息是预期内的;远程仓库仍会被添加。
bash poc_arch_traversal.sh
脚本会启动一个私有 D-Bus 总线以及一个处于 --session 模式的助手,全部位于一个临时目录下。它使用载荷 ../../../../../root/.ssh 调用 DeployAppstream,并验证逃逸出的目录已在 <workdir>/root/.ssh 处创建,镜像真实目标。预期输出中间会出现一个 D-Bus 错误:那是 OCI 索引获取在目录已经创建之后失败,这是预期行为。
bash poc_arch_traversal.sh prod
脚本绝不会要求输入密码。它会自动检测 OCI 远程仓库,使用遍历载荷以未认证方式触发 DeployAppstream,并打印助手的响应。root 助手会创建 /root/.ssh,然后在不可达的注册表上失败,从而打印预期的 D-Bus 错误。如果响应反而是认证错误,则说明您不在活跃的本地会话中;请从机器的控制台运行它。可以使用 bash poc_arch_traversal.sh prod <remote> 强制指定特定的远程仓库。
触发本身以非特权方式运行,但确认结果需要 root,因此脚本将这一步留给您。运行之后,在同一台机器上:
sudo ls -laR /root/.ssh
sudo stat -c '%U:%G' /root/.ssh
该目录必须存在并且由 root:root 拥有。脚本输出中的 D-Bus 错误并非失败:它证明 mkdir 在获取之前已经运行,因为即使方法调用失败,逃逸出的目录仍然存在。
如果 /root/.ssh 在运行之前已经存在(例如在运行着 sshd 的主机上),那么该次运行对该路径而言是无操作,不会留下任何东西:助手的 lock 文件是临时的,会在方法返回时被移除,而 icons 仅在成功获取索引之后才会被写入。为了进行干净的验证,请针对一个尚不存在的路径运行。
sudo rm -rf /root/.ssh # 仅当它在运行之前不存在时
sudo flatpak remote-delete --system oci-poc
研究与撰写由 Yehia Ali Mohamed Ezzat 完成。
本仓库中的文档和代码仅供教育目的提供。使用风险自负,且仅在您获得授权的地方使用。