文章总结: 文档披露GoogleCloudLooker产品因目录删除验证缺陷存在远程命令执行漏洞,攻击者通过精心构造的目录名称利用竞争条件在Git操作过程中执行任意命令。同时发现Kubernetes服务账户权限配置过高可能导致跨实例权限提升。谷歌已修复相关漏洞并强化了安全配置。 综合评分: 95 文章分类: 漏洞分析,WEB安全,云安全,红队,安全工具
在 Google Cloud 中执行远程命令并删除单个目录
Ots安全
2026年3月24日 14:33 广东
在小说阅读器读本章
去阅读
威胁简报
恶意软件
漏洞攻击
介绍
您好,我是RyotaK (@ryotkak ),GMO Flatt Security Inc. 的安全工程师。
不久前,我参加了由谷歌组织的Google Cloud VRP bugSWAT 现场黑客活动。
在此次事件中,我发现谷歌云的某项服务存在远程命令执行漏洞。由于该漏洞现已修复,我想在本文中分享其技术细节。
太长不看
Google Cloud 有一款名为 Looker 的产品,该产品具有管理 Git 存储库的功能。
当用户删除目录时,Looker 对目标目录的验证存在缺陷,导致可能误删包含代码仓库本身的目录。由于其他 Git 操作可以同时执行,因此在删除目录的过程中也可能触发其他 Git 相关操作。
利用这种竞争条件,攻击者可以在 Looker 服务器上执行任意命令。
虽然使用 Kubernetes 将实例隔离,但 Looker 服务帐户权限中的错误配置可能会允许权限提升,从而访问同一 Kubernetes 集群中的其他实例。
在向谷歌报告漏洞后,他们修复了远程命令执行漏洞和权限提升漏洞。
关于 Looker
Looker 是 Google Cloud 旗下的一款商业智能 (BI) 和数据分析平台。它通过交互式仪表板和报告,帮助企业探索、分析和可视化数据。Looker 可连接到各种数据源,使用户能够创建自定义数据模型并执行分析。
Looker有两种部署方式:云托管和自托管。由于我能获得用于Google Cloud VRP bugSWAT活动的自托管Looker实例,所以我专注于对自托管Looker进行逆向工程。
技术细节
Looker 中的 Git 集成
Looker 提供了一项名为 LookML 的功能,用于管理 Git 仓库(Looker 称之为项目)中的模型文件。用户可以向 Git 仓库推送或拉取更改,Looker 会自动应用这些更改。
为了与外部 Git 服务集成,Looker 通常使用 JGit 库(Git 的纯 Java 实现)。但是,当远程 Git 仓库通过 SSH 配置时,Looker 会使用原生的 Git 命令行工具,而不是 JGit。
它会在 Looker 服务器上的特定目录下创建检出的存储库,并针对该目录执行 Git 命令。
def self._cli_git_command(working_directory, command_words) [...] command_with_dir = "cd #{working_directory} && #{command}" Looker::Log.log_block_latency(:git, "_cli_git_command: command: #{command_with_dir}") do Open3.capture3(command_with_dir) end [...]
除了标准的 Git 操作外,Looker 还提供了一个通过 Web 用户界面管理文件的功能。用户可以通过 Looker 界面创建、编辑和删除文件或目录。
目录删除中的验证不当
当用户删除目录时,Looker 会执行以下代码:
post("/api/internal/projects/:project_id/delete_dir") do |_project_id| [...] dir_path_array = body["dir_path_array"] @project.delete_dir(dir_path_array) [...]
def delete_dir(dir_path_array) dir_name = dir_path_array.reject(&:empty?).join("/") dir_name = validate_dir_name(dir_name) dir_path = File.join(path, dir_name) [...] FileUtils.rm_rf(dir_path)
该validate_dir_name方法确保保留目录不会被篡改:
def validate_dir_name(dir_name) [...] if path_array.include?(Looker::Model::Project::DOT_GIT) raise(InvalidFileNameError.new("New path cannot include .git")) else nil end File.join(path_array.map do |s| Looker::Utils.sanitize_file_or_dir_name(s) HellToolJava end) end
.git由于如果目录损坏或被删除,可能会欺骗 Git 使用伪造的 Git 配置,因此该validate_dir_name方法会检查要删除的目录是否包含该目录,.git如果包含,则会引发错误。
例如,考虑一个具有以下结构的存储库:
--- .git directory (Can't be controlled by the user) ---.git/ HEAD config...--- worktree (Controllable by the user) ---HEADconfigobjects/refs/
如果该.git目录被删除,则针对此存储库执行的下一个 Git 命令将找不到该.git目录,并将在工作树目录中查找 Git 配置。
因此,如果工作树包含类似于目录内容的文件.git,Git 命令将使用这些配置。
例如,如果将以下配置作为config文件放置在工作树中,并且该.git目录被删除,则在运行类似命令时fsmonitor,将触发钩子并whoami执行该命令:git status
[core] bare = false worktree = "." fsmonitor = "whoami"
让我们回到这个validate_dir_name方法。它会正确检查要删除的目录是否包含.git:
def validate_dir_name(dir_name) [...] if path_array.include?(Looker::Model::Project::DOT_GIT) raise(InvalidFileNameError.new("New path cannot include .git")) else nil end File.join(path_array.map do |s| Looker::Utils.sanitize_file_or_dir_name(s) HellToolJava end) end
然而,此检查无法捕获值为 的dir_name情况/。
validate_dir_name方法返回后,delete_dir会将基本路径和冒号连接起来,构建要删除的完整路径dir_name。
def delete_dir(dir_path_array) dir_name = dir_path_array.reject(&:empty?).join("/") dir_name = validate_dir_name(dir_name) dir_path = File.join(path, dir_name) [...] FileUtils.rm_rf(dir_path)
因此,指定dir_path_array为[“/”]结果为dir_name,/要删除的完整路径变为存储库目录本身。
也就是说,即使攻击者能够删除整个代码库目录,上述攻击也需要工作树中存在伪造的 Git 配置文件。这意味着如果整个代码库被删除,攻击就无法进行。
……或者并非如此?
FileUtils.rm_rf 的内部结构
该FileUtils.rm_rf方法是 Ruby 标准库中的一个方法,可以递归地删除文件和目录。
通过对其内部代码的追踪,可以发现以下代码:
def remove_entry(path, force = false) Entry_.new(path).postorder_traverse do |ent| begin ent.remove rescue raise unless force end end
如图所示,它使用Entry_.postorder_traverse后序遍历目录树(先处理目录的所有子目录,然后再处理目录本身)。
如果我们欺骗 Looker 删除存储库中包含数千个文件和子目录的目录,会发生什么?
确实,删除所有文件和目录需要相当长的时间。如果我们能在.git目录删除之后、整个仓库删除完成之前触发对某个目录的删除,我们仍然有机会对部分删除的仓库执行 Git 命令。
控制删除令
内部Entry_.postorder_traverse使用该Dir.children方法列出目录内容,该方法使用readdir系统调用读取目录条目。
dir.c 第 952 行
https://github.com/ruby/ruby/blob/c5bd4acd30320a8e180ce9fcb24acdab4e10c73a/dir.c#L952
while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) { const char *name = dp->d_name; [...]
由于返回的条目顺序readdir取决于文件系统的实现,并且可能会有所不同,因此无法保证删除过程中文件和目录的处理顺序。
幸运的是,从攻击者的角度来看,ext4 文件系统上返回的条目顺序readdir具有一定的确定性。这使得攻击者可以通过精心命名目录来影响删除顺序。
因此,我能够按如下方式“喷洒”目录名称,以确定哪个目录名称能够确保该.git目录在删除过程中首先被处理:
.git/aaa/aab/ dir1/ file1 file2 file3 ... dir2/ file1 file2 file3 ... ... dir10000/ ...aac/...ccb/ccc/
通过将许多文件和目录放在特定目录下,并测量触发删除后存储库不可用所需的时间,我可以找到最佳目录名称,.git首先删除该目录,从而创建一个窗口来触发针对部分删除的存储库的 Git 操作。
远程命令执行
现在我们可以控制目录删除的时间.git,我们可以尝试在 Looker 服务器上执行任意命令。
攻击步骤如下:
- 创建一个包含伪造的 Git 配置的工作树的 Git 仓库,以便使用fsmonitor钩子执行任意命令。
- 创建一个包含许多文件和子目录的随机名称目录,然后尝试使用POST /api/internal/projects/:project_id/delete_dirAPI 删除存储库本身。
- 测量存储库不可用所需的时间。如果时间过长,则返回步骤 2(这表明.git目录尚未被删除)。
- 找到最佳目录名称后,再次准备仓库,并持续访问触发 Git 操作的端点(例如,git status)。在我的测试中,每秒 1 次请求就足够了。
- 调用POST /api/internal/projects/:project_id/delete_dirAPI 删除存储库目录。这将.git首先删除该目录,但完成大型目录的删除需要一些时间。
- 此时,步骤 4 中触发的 Git 操作将尝试使用部分删除的存储库,导致使用工作树中的 Git 配置并执行任意命令。
我使用以下 Git 配置来测试命令执行:
[core] bare = false worktree = "." fsmonitor = "echo \"$(whoami) $(uname -a)\" > ../output/pwned.model.lkml"
一旦git status在存储库删除期间执行,就会执行whoami和uname -a命令,并将输出写入../output/pwned.model.lkml,这是我可以正常访问的另一个存储库。
对其他 Looker 实例的权限提升
此时,我询问 Google Cloud 团队是否可以进一步调查该漏洞的影响,他们欣然批准了。
在获得 Looker 实例的反向 shell 后不久,我注意到该实例被隔离在 Kubernetes pod 中,从而限制了对其他 Looker 实例的影响。
然而,在检查挂载到 的 Kubernetes 服务帐户凭据时/var/run/secrets/kubernetes.io/serviceaccount,我发现权限过多:
{ "kind": "SelfSubjectAccessReview", "apiVersion": "authorization.k8s.io/v1", [...] "spec": { "resourceAttributes": { "namespace": "looker", "verb": "update", "resource": "secrets" } }, "status": { "allowed": true, "reason": "RBAC: allowed by RoleBinding..." }}
Looker 服务帐户有权更新looker命名空间中的密钥,该命名空间在多个 Looker 实例之间共享。
由于未获得读取密钥的权限,我无法直接读取现有密钥,这使得安全测试变得困难。没有读取权限,我无法在修改前备份现有密钥,这意味着任何更新都将是不可逆的,并且可能会破坏其他 Looker 实例。这一限制使我无法在不影响生产系统的情况下演示其全部影响。
因此,我将这一发现分享给了谷歌云团队,以便他们进行进一步调查。
经过调查,他们确认可以通过滥用此权限来提升权限,从而访问同一 Kubernetes 集群中的其他实例,并将该漏洞归类为 S0。
截至本文发布之时,谷歌云团队已修复了远程命令执行漏洞和权限提升漏洞。
结论
在本文中,我分享了在 Google Cloud VRP bugSWAT 活动中发现的 Google Cloud Looker 产品中远程命令执行漏洞的技术细节。
虽然该漏洞源于目录删除操作中的一个微小验证错误,但利用该漏洞却可以执行远程命令。这一漏洞凸显了正确输入验证的重要性——即使是微小的错误也可能导致严重的安全问题。
END
公众号内容都来自国外平台-所有文章可通过点击阅读原文到达原文地址或参考地址
排版 编辑 | Ots 小安
采集 翻译 | Ots Ai牛马
公众号 | AnQuan7 (Ots安全)
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Ots安全 《在 Google Cloud 中执行远程命令并删除单个目录》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论