CodeQLEAKED:公共密钥泄露导致 GitHub CodeQL 的供应链攻击

John Stawinski

针对 GitHub CodeQL 的潜在供应链攻击始于一个简单的环节:一个公开暴露的密钥,每次有效期为 1.022 秒。 在这短短的一秒钟内,攻击者可以采取一系列步骤,从而在大多数使用 CodeQL 的仓库(GitHub 的代码分析引擎,受到数十万个仓库的信任)内的 GitHub Actions 工作流中执行代码。这种影响将波及公共 GitHub (GitHub Cloud) 和 GitHub Enterprise。

如果对 GitHub Actions 进行后门攻击听起来很熟悉,那是因为这正是威胁参与者在最近的 tj-actions/changed-files 供应链攻击中所做的事情。想象一下同样的供应链攻击,但这次不是对 tj-actions 中的 action 进行后门攻击,而是对 GitHub CodeQL 中的 action 进行后门攻击。

攻击者可以利用这一点来:

  1. 通过使用 CodeQL 泄露私有仓库的源代码来窃取知识产权
  2. 窃取使用 CodeQL 的工作流任务中的 GitHub Actions 密钥,并利用这些密钥执行进一步的供应链攻击。
  3. 在运行 CodeQL 工作流的内部基础设施上执行代码
  4. 通过在使用了 CodeQL 的仓库内利用 GitHub Actions 缓存来破坏 GitHub Actions 密钥

这是一个关于我们如何发现暴露的密钥,导致竞争条件和潜在的供应链攻击,以及 CVE-2025-24362 的故事。

注意:根据 GitHub 的公告,他们没有发现任何平台或系统被入侵的证据。

我们是如何走到这一步的?

2025 年 1 月,我从 Praetorian 的 Red Team 工作中抽出时间,开始了为期三个月的研究。我的目标是突破公共 GitHub Actions 漏洞利用的极限,并基于我们在 Black HatDEF CONSchmoocon 和 Black Hat Arsenal 上所做的演示。这项研究的工具和结论将被应用到我们的 CI/CD Professional Services EngagementsChariot(我们的 Continuous Threat Exposure Management 平台)中。

我的研究从扫描 GitHub Actions 工作流工件中的密钥开始。

密钥扫描

2024 年 8 月,Palo Alto 的研究员 Yaron Avital 发表了一篇关于在工作流工件中识别密钥的 文章。我预感仍然有密钥可以被发现,尤其是自这篇文章发表以来,没有太多的公开后续工作。

我构建了一个简单的 Actions Artifacts Secret Scanner 来开始。它从 GitHub Actions 工作流下载工件,递归提取其内容,并使用 Praetorian 的开源密钥扫描工具 Nosey Parker 扫描其内容中的密钥。

Actions Artifacts Secret Scanner 已集成到 Chariot 中,并作为 Gato 功能开源。

在运行此扫描程序一天后,它发现了一个可能导致 GitHub CodeQL 供应链攻击的密钥。

但首先,我需要看看这个密钥是否可用。

背景

在您理解这些术语之前,CI/CD 漏洞听起来很复杂。 让我们赶上进度。

GitHub Actions 是一个持续集成和持续交付 (CI/CD) 平台,允许执行在工作流中指定的代码,作为 CI/CD 过程的一部分。 当您将代码推送到 GitHub 存储库或创建拉取请求时,GitHub Actions 可以使用 YAML 文件中定义的工作流自动构建、测试和部署您的代码。

例如,假设您正在构建一个托管在 AWS 中的 Web 应用程序。 您可以配置 GitHub Actions 工作流,以便每当您将代码推送到存储库时,它都会自动进行测试,然后部署到 AWS。

如果您是 GitHub Actions 的新手,我们建议您阅读一些示例

每次工作流程运行都会生成一个 GITHUB_TOKEN — 一个特殊的、自动生成的 GitHub App 安装令牌,允许工作流程与存储库交互。 可以在工作流程文件、存储库级别或组织级别配置此令牌的权限,从而确定它可以在存储库中执行的操作。

简单来说:

如果令牌具有高权限,则令牌泄露 == 不妙。

什么是工作流工件?

我们在一个 GitHub Actions 工作流工件 中发现了公开暴露的密钥。

GitHub Actions 工作流可以将工作流“工件”上传到 GitHub Actions。 工作流工件可以是任何文件,并由该工作流保存以供以后使用。 默认情况下,工件对任何具有存储库读取访问权限的人公开可访问,并存储最多 90 天。

最后,什么是 CodeQL?

CodeQL 是 GitHub 的代码分析引擎。 CodeQL actions 对 GitHub 存储库执行静态代码分析,以尝试识别漏洞。 在其生命周期内,他们发现了 数百个 CVE,从而保护组织免受漏洞侵害。

像 CodeQL 这样的安全工具通常需要访问敏感系统和数据,这使得它们成为攻击者有吸引力的目标。

如果 CodeQL 被破坏,那么最广泛使用的安全工具之一现在将成为后门。

查找令牌

在运行 Actions Artifact Secrets Scanner 一天后,它在 github/codeql-action 存储库工件中找到了一个令牌,该工件由 运行 发布。 Actions Artifact Secrets Scanner 下载了由“PR Check – Debug artifacts after failure工件失败后”工作流上传的“my-debug-artifacts” zip,递归提取了存储在其中的“my-db-java-partial.zip”文件,并运行了 Nosey Parker。 几秒钟之内,Nosey Parker 在崩溃报告中标记了一个以“ghs_”开头的 GitHub 令牌。

查找令牌

经过手动调查,我确认这是一个 GitHub App 令牌安装令牌,存储在一个包含执行该工作流的 GitHub Runner 的环境变量的文件中。

调查影响

密钥泄露很酷,但是我们可以使用此令牌做什么? 如果 GITHUB_TOKEN 仅具有读取权限,那么它的泄露影响将降至最低。

确定 GITHUB_TOKEN 权限的最简单方法是查看工作流日志。 为了对此进行调查,我导航到上传令牌的工作流的“Setup Job”步骤。

调查影响

GitHub 令牌具有完全写入权限。

我们可以花很多时间讨论 每个权限,但是让我们重点关注特别有趣的权限。

拥有这些权限后,攻击者在仓库篡改方面具有很大的潜力,但是仍然存在一个问题。 这些令牌仅在其特定工作流作业的持续时间内有效。 这意味着一旦作业结束,令牌就变得毫无用处。 要使攻击者能够滥用此令牌,需要发生三件事:

  1. 令牌需要具有某种写入权限(已经确认)。
  2. 令牌需要使用 V4 版本的上传工件 API,因为这是唯一允许您在作业完成之前(以及在作业完成之后,令牌无效)检索工件的版本。
  3. 上传工件和完成作业之间的时间需要足够长,以便我们下载、提取和使用令牌。

如果满足所有这些条件,则此公开暴露的令牌可用于发起对 CodeQL 的全面供应链攻击。 这就像发现保安人员不小心将他们的主钥匙短暂地留在众目睽睽之下,一遍又一遍地重复。

我们必须确定保安人员是否给我们足够的时间来窃取钥匙并在他们返回岗位之前使用它。

让我们进一步调查。 滴答滴答。

确定工件上传版本

识别工件上传版本通常很简单。 如果工作流使用 actions/upload-artifact@v4***,我们可以检索作业完成之前的工件。 如果它使用早期版本,我们将无法这样做。

在这种情况下,CodeQL 没有使用 actions/upload-artifact action; 他们手动使用源代码中的上传工件客户端。 代码注释表明它使用了版本 4。 这足以让我继续。

现在我们需要确定作业的持续时间是否足够长,以便我们检索和使用令牌。

计算我们的执行时间

查看此工作流的原始 GitHub 日志,我们可以看到两个关键时间戳:

计算我们的执行时间

“Finalizing artifact upload”发生在 17:22:09:888。

作业中的最后一步“Cleaning up orphan processes”发生在 17:22:10:911。

这意味着我们有大约 1.022 秒的时间来下载工件、提取 GitHub 令牌并使用它。 我注意到令牌在“Cleaning up orphan processes”步骤后大约一秒钟仍然有效,因此我们将其称为两秒钟。

保安人员给了我们两秒钟的时间来窃取钥匙并在他们返回之前使用它。攻击者是否有足够的时间来使用此令牌? 还是这又是另一个理论漏洞?

启动你的引擎

为了对此进行测试,我制作了一个 Python 脚本 artifact_racer.py。 工件竞赛执行以下操作。

  1. 持续查询 github/codeql-action GitHub 存储库,直到看到“PR Check – Debug artifacts after failure”工作流开始。
  2. 监视正在运行的工作流的工件。
  3. 一旦看到“PR Check – Debug artifacts after failure”工作流程运行,它就会下载工件并提取 GITHUB_TOKEN
    • 外壳进行文件操作和下载是提高速度的关键,尽管可能还有其他方法可以使其更快。
  4. 使用 GITHUB_TOKEN 创建一个新分支。
  5. 使用 GITHUB_TOKEN 将一个名为 poc.txt 的空文件推送到该分支。
  6. 为该提交创建一个新标签

如果我可以创建一个新分支、添加一个文件并为该提交创建一个标签,那么这将证明攻击者可以在令牌过期之前将其用于恶意目的。

鉴于工作流程工件仅为〜21MBs,我认为我们有机会。 在针对测试存储库成功执行后,我继续使用 github/codeql-action 存储库。

执行概念证明

我运行了竞赛者。

然后我等待了。

大约两个小时后,“PR Check – Debug artifacts after failure”工作流程执行。 竞赛者成功检索了 GITHUB_TOKEN,创建了分支,推送了文件并添加了标签。

执行概念证明

分支 URL: https://github.com/github/codeql-action/tree/testpoc 提交 URL: https://github.com/github/codeql-action/commit/26fcd8e2368067be04a705a229590749a426fefe 标签 URL: https://github.com/github/codeql-action/releases/tag/testpoctag

创建新标签

创建标签的能力在此攻击中变得非常重要。 请记住,当我们进行操作时。

在确认 GITHUB_TOKEN 可以在短时间内使用后,我们负责任地将此漏洞披露给 GitHub。

如果您仍然不为所动?

使用 GITHUB_TOKEN,攻击者可以将恶意代码添加到任何未受保护的分支。 一种隐蔽策略是在合并前针对功能分支,走私少量恶意代码更改,然后等待其被合并。 由于 GitHub Actions 机器人频繁提交到 CodeQL Actions 存储库,因此这将特别有效。

他们还可以添加指向特定提交的标签。 例如,如果他们在分支上具有恶意代码,然后添加了 v3 标签,则任何手动使用 codeql-action…@v3 的人都将执行恶意代码。 稍后会详细介绍。

通过代码执行,您将能够破坏该作业中使用的任何 GitHub Actions 密钥,并泄露该存储库的源代码。 如果他们的 action 在内部基础架构上执行(这在使用 自托管 GitHub runner 中很常见),您还将在他们的内部网络或云环境中执行代码。

此攻击的影响将与最近的 tj-actions/changed-files 供应链攻击非常相似。

个影响令人印象深刻,但还不足以达到我在开始时所做的声明。 是的,通过这些途径,他们可以对手动使用 CodeQL action 的仓库发起供应链攻击。 但是,大多数组织不会手动包含这些 action。 他们只是进入其存储库设置,单击“启用 CodeQL”,然后从那里开始。

起初,我认为在您的存储库中启用 CodeQL 根本不会与 github/codeql-action 存储库进行交互。

我错了。

指数级影响

在与一些同事讨论了这个问题之后,我决定进一步调查。 启用 CodeQL 后实际上会发生什么?

本节是了解此漏洞的全部影响的关键。 请坚持下去。

为了进行调查,我创建了自己的公共存储库“John’s Top Secret Repo”,并启用了 CodeQL。

在使用默认设置启用 CodeQL 后,一个特殊的 GitHub Actions 工作流会在您的存储库中运行。 此 CodeQL action 不会显示在您的存储库工作流中,但是您可以导航到工作流日志以查看它在做什么。

在您的存储库设置中启用 CodeQL。

CodeQL 设置

观察 CodeQL 工作流程。

根据我的观察,CodeQL:

  1. 将您的存储库检出到文件系统
  2. 初始化 CodeQL
  3. 运行 CodeQL 扫描
  4. 上传扫描结果

CodeQL

让我们仔细看看步骤 3。

步骤 3。

如果这没有让您感到震惊,请再次查看。 请记住,我们有能力将标签推送到 github/codeql-action 存储库。

在后台,CodeQL 正在使用 v3 标签引用的提交来执行 github/codeql-action 存储库中的 action。 此标签不是不可变的,并且他们没有使用 工作流固定(GitHub 建议这样做),这意味着攻击者可以使用受损的 GITHUB_TOKEN 覆盖 v3 标签。 现在,如果攻击者删除然后将 v3 标签添加到其恶意提交中,那么每个使用默认 CodeQL 工作流程的存储库都将执行其恶意代码。

CodeQL

选择“高级 CodeQL”时创建的 Action 也使用带有 v3 标签的可重用 github/codeql-action。

CodeQL action 会检查它们在其中运行的每个存储库的源代码,这意味着恶意的 CodeQL action 可能会泄露使用默认 CodeQL 配置的任何存储库的源代码。

这将导致知识产权的重大泄露。 而且,如果您曾经在 Red Team 上操作过,您就会知道有多少硬编码密钥潜伏在私有源代码存储库中。

但是等等,还有更多

我们快完成了。 但是请记住,我答应了另外一件事:

在评估 CI/CD 攻击路径的影响时,我寻找破坏 GitHub Actions 密钥的方法。 通常,这些密钥是皇冠上的明珠所在。

如果 CodeQL action 以写入权限或与 GitHub Actions 密钥一起执行,那么利用代码执行来泄露这些密钥是微不足道的。 但是默认的 CodeQL action 使用的 GITHUB_TOKEN 仅具有读取权限,因此您无法执行仓库写入操作、后门发布或使用花哨的工作流程调度事件来窃取密钥,[就像 PyTorch 中发生的那样]。

默认的 CodeQL action 确实 在存储库的主分支中执行。 任何 GitHub 存储库的主分支都可以写入由整个存储库使用的缓存条目。 这为进行 GitHub Actions 缓存中毒提供了机会。

Adnan Khan 的这篇文章 详细解释了 GitHub Actions 缓存中毒,该文章记录了缓存中毒的发现和利用。 进行 GitHub Actions 缓存中毒的最简单方法是部署 Cacheract,这是一种通过缓存中毒持续存在于构建管道中的恶意软件。

如果攻击者在 CodeQL 工作流程中部署了 Cacheract,它将:

  1. 预测缓存条目
  2. 使用恶意 action 覆盖这些条目
  3. 在使用 action-cache 的任何工作流程中获得代码执行权限(大多数存储库都使用 Actions 缓存)
  4. 利用代码执行来破坏这些工作流程使用的 GitHub Actions 密钥、捕获特权 GITHUB_TOKEN 等

即使有人注意到恶意的 CodeQL action 并修复了该漏洞,Cacheract 也会继续使缓存中毒。

我花了十分钟寻找使用 CodeQL 和 actions/cache 的知名存储库,并确定了 HomebrewAngularGrafana

缓存中毒将使攻击者能够利用此 CodeQL 供应链攻击来获得对存储库和存储库密钥的写入权限。

恭喜,你成功了

我们现在已经实现了我在开头提到的所有影响亮点:

  1. 通过泄露使用 CodeQL 的所有私有存储库的源代码来破坏知识产权。
  2. 窃取使用 CodeQL 的任何工作流程作业中的 GitHub Actions 密钥,并利用这些密钥执行进一步的供应链攻击。
  3. 在运行 CodeQL 工作流程的内部基础设施上执行代码。
  4. 破坏使用 CodeQL 的仓库中任何使用 GitHub Actions 缓存的工作流程的 GitHub Actions 密钥。

恭喜,你成功了

像这样的供应链攻击非常可怕,尤其是在它们始于像公开暴露的凭据这样简单的事情时。 如果这是您第一次听说滥用 GitHub Actions 来发起供应链攻击,我会告诉您一个秘密:这些漏洞 一直发生 ,始终如一

GitHub Actions 滥用已经存在几年了,但它仍然是影响最大、最不被理解的漏洞类别之一。 这种情况正在慢慢开始改变(强调 ~慢慢~)。 DevOps 和安全社区需要致力于学习这些漏洞,以保护其组织免受风险。 像这样的漏洞以及最近的 tj-actions/changed-files 供应链攻击开始将这些问题带到公众的视野中。 这就是为什么我们投资于研究以发现这些漏洞并设计解决方案以防止它们的原因。

CVE-2025-24362

此披露的副作用是 CVE-2025-24362。 公开暴露的 GITHUB_TOKEN 位于 CodeQL Action 在代码扫描工作流程失败后上传的调试工件中。 CodeQL Actions 存储库有意识地触发了此失败,但是 CodeQL Actions 的其他用户可能会将自己的密钥作为环境变量暴露给工作流程,前提是他们的工作流程遇到了类似的失败。

此问题已在 CodeQL Action 版本 3.28.3 中修复。

即使此披露导致了 CVE,但最大的潜在影响仍然在于利用针对 CodeQL Actions 存储库的漏洞并发起针对 CodeQL 用户的供应链攻击。

补救措施

GitHub 具有我们所见过的最快速和最令人印象深刻的补救响应之一。

如果您担心自己的 GitHub Actions 工作流工件,可以采取以下步骤来限制密钥泄露的风险:

要了解有关此漏洞、我是如何发现它的以及如何在您自己的环境中检测到类似漏洞的更多信息,[请加入我的网络研讨会](https://www.praetorian.com/blog/codeqleaked-public-secrets-exposure-leads-to-supply-chain-attack-