AI研究

Megalodon一天推5,718个恶意commit、GhostAction向3万仓库推假审计workflow,pull_request_target+secrets的供应链还守得住吗?

👤 为我痴狂 👁 18 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-08
首页› AI› AI研究› 正文
Megalodon一天推5,718个恶意commit、GhostAction向3万仓库推假审计workflow,pull_request_target+secrets的供应链还守得住吗?

摘要:2025年,两起针对开源软件供应链的事件把GitHub Actions的权限模型推到了聚光灯下:Megalodon在单日内向数千个仓库推送5,718个恶意commit,GhostAction则通过伪造的"安全审计"工作流向约3万个仓库植入恶意workflow。两者共同指向一个被长期低估的攻击面——pull_request_target触发器与secrets上下文组合后形成的"高权限+不可信输入"错配。本文以"信任边界漂移"为主线,拆解事件的技术机制、量化暴露面、给出可落地的检测与治理路径,并对CI/CD供应链安全的下一阶段演进作出研判。

关键词:软件供应链安全;GitHub Actions;pull_request_target;权限最小化;工作流投毒;SLSA

一、导语:当"自动化"成为攻击者的放大器

软件供应链攻击的形态在过去五年里发生了显著迁移。早期攻击者偏好污染构建产物(如2017年的NotPetya借道乌克兰会计软件)或劫持包管理器账号(如event-stream事件),而近两年公开披露的案例显示,攻击焦点正从"制品"上移到"流水线"本身。原因并不复杂:CI/CD系统天然持有代码读写、制品发布、云凭据等复合权限,一旦被攻陷,攻击者可以在合法流程内完成横向移动,留下的痕迹也更接近正常业务行为。

GitHub Actions是当前使用最广的托管式CI/CD平台之一。根据GitHub官方2024年Octoverse报告,平台托管仓库数量已超过4.2亿,Actions的月度工作流运行次数达到数十亿量级。规模带来便利,也带来同质化的攻击面:大量仓库复用了相似的workflow模板,而模板中的权限配置往往由"能跑通"而非"最小化"驱动。本文认为,Megalodon与GhostAction两起事件之所以能形成规模化影响,根本原因不是某个单点漏洞,而是信任边界在模板复用中被系统性地漂移了——开发者以为"来自fork的代码不可信",但pull_request_target的设计恰恰让不可信代码在可信上下文中执行。

本文的分析主线即"信任边界漂移"(Trust Boundary Drift):在CI/CD中,信任边界并非静态的物理隔离,而是由触发器语义、上下文变量、权限声明、密钥作用域四者共同定义的动态契约。任何一者的语义被误读,边界就会漂移。下文将沿这条主线,依次还原事件、拆解机制、量化暴露面、给出治理路径,并在最后讨论现有防御框架的边界与演进方向。

二、两起事件的还原:Megalodon与GhostAction

2.1 Megalodon:批量投毒的"工业化"尝试

2025年,安全研究社区披露了一个被命名为Megalodon的自动化攻击活动。根据公开的威胁情报摘要(来源:多家安全厂商2025年联合披露报告及GitHub安全公告汇总),该活动在单日内向数千个开源仓库推送了5,718个恶意commit。攻击手法并不复杂:通过自动化脚本fork目标仓库、在分支中注入恶意代码或修改CI配置、再以pull request形式提交,试图借助维护者的合并动作或自动化工作流的触发完成投毒。

需要说明的是,5,718这个数字是"被推送的恶意commit数量",而非"成功合并的数量"。公开披露中并未给出精确的合并成功率,因此本文在后续分析中将其作为"攻击尝试规模"而非"攻击成功规模"使用。这一点很重要:许多二次传播把该数字等同于"5,718个仓库被攻陷",属于对原始数据的误读。笔者认为,即便是尝试规模,它也揭示了一个事实——攻击者已经能够以极低成本对开源生态发起"广撒网"式骚扰,防御方若仍依赖人工审查,将陷入数量上的绝对劣势。

Megalodon的技术意义在于它验证了"提交层投毒"的可行性边界:攻击者不需要攻破任何账号,只需要利用开源协作流程本身的开放性。这与传统的账号劫持类攻击有本质区别,前者攻击的是"流程信任",后者攻击的是"身份信任"。

2.2 GhostAction:伪造审计工作流的精准打击

与Megalodon的"广度优先"不同,GhostAction呈现出明显的"精度优先"特征。根据2025年公开披露的分析报告,该活动向约3万个仓库推送了伪装成"安全审计"的workflow文件。这些workflow在命名、注释和步骤设计上都刻意模仿常见的安全扫描模板(如依赖审计、密钥扫描),使维护者在快速浏览时难以分辨真伪。

GhostAction的关键设计在于触发条件与权限的组合。恶意workflow通常选择在pull_request_target或issues等事件上触发,并在步骤中引用secrets上下文,从而在无需维护者显式批准的情况下获得仓库级凭据的访问机会。一旦workflow被执行,攻击者即可通过外发请求将凭据或代码内容带出。

本文评述:GhostAction最值得警惕的不是技术复杂度,而是它把"安全工具"本身武器化了。当防御者习惯性地信任名为"audit"或"scan"的工作流时,命名就成了社会工程的一部分。这提示我们,CI/CD安全不能只做语法检查,还要做"语义可信度"评估——一个workflow的声明意图与其实际权限请求是否匹配。

2.3 两起事件的共性抽象

把两起事件放在同一坐标系下观察,可以得到一个清晰的共性:攻击者都在利用"可信上下文执行不可信内容"这一结构性缺陷。Megalodon利用的是PR流程中"代码来自fork但工作流可能在高权限下运行"的缝隙;GhostAction利用的是"workflow文件本身可被提交,而提交者未必是可信维护者"的缝隙。两者叠加,构成了从"投递"到"执行"的完整链路。

维度 Megalodon GhostAction
攻击目标仓库代码与构建产物仓库凭据与密钥
投递方式恶意commit / PR伪造workflow文件
触发依赖维护者合并或自动触发pull_request_target等事件
规模特征单日5,718次尝试约3万仓库受影响
防御重点PR审查与分支保护触发器语义与密钥作用域

表1:两起事件的维度对比(数据来源:2025年公开威胁情报摘要与GitHub安全公告,规模数字为披露口径,非本文实测)

三、机制剖析:pull_request_target与secrets的错配

3.1 两个触发器的语义差异

要理解风险,必须先厘清pull_request与pull_request_target的语义差异。前者在fork提交的PR上运行时,工作流使用的是fork方的代码,且默认不授予secrets访问权限,GITHUB_TOKEN也是只读的。这是GitHub有意的安全设计:不可信代码不应接触可信凭据。

后者pull_request_target的设计初衷是解决"需要给PR打标签、发评论、做分诊"这类场景——这些操作需要写权限,但不应执行PR中的代码。因此它的语义是:在基础仓库(base)的可信上下文中运行工作流,并授予secrets与写权限。问题在于,工作流文件本身来自base分支,但开发者常常在工作流中执行checkout去拉取PR的head代码,然后运行其中的脚本。这一拉一跑,就把不可信代码引入了可信上下文。

# 危险模式:pull_request_target + checkout PR head + 执行
on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}   # 拉取不可信代码
      - run: npm install && npm test                        # 执行不可信脚本
        env:
          TOKEN: ${{ secrets.DEPLOY_TOKEN }}                # 密钥暴露给不可信代码

上面这段配置在真实仓库中并不罕见。它的危险不在于语法错误,而在于语义误读:开发者以为"我用的是target触发器,所以安全",却忽略了checkout那一步已经把信任边界击穿。本文评述:这类配置的根源是"触发器安全性"与"执行内容安全性"被混为一谈。触发器只决定"在哪个上下文运行",不决定"运行什么代码"。真正的安全边界应落在"执行内容是否可信"上。

3.2 secrets上下文的暴露路径

在GitHub Actions中,secrets上下文包含仓库级、组织级和环境级三类密钥。它们的注入方式决定了暴露路径:

  • 环境变量注入:通过env:将密钥写入进程环境,任何子进程(包括不可信的构建脚本)都可读取。
  • 表达式插值:在run:中直接使用${{ secrets.X }},若该步骤执行的是PR代码,密钥会进入命令行历史或日志。
  • action输入传递:第三方action的with:参数接收密钥,若action本身被投毒,密钥即被外带。

GhostAction的手法正是利用了第三条路径的变体:伪造的"审计"workflow在步骤中调用看似正规的action,并把密钥作为输入传入,再由action内部发起外发请求。由于整个过程发生在合法工作流内,传统的边界防护(如网络出口白名单)若配置不严,很难拦截。

3.3 权限漂移的四个阶段

把上述机制串起来,可以得到"信任边界漂移"的四个阶段,这也是本文分析主线的核心模型:

阶段 边界状态 典型误判
S1 声明触发器语义定义初始边界"target=安全"
S2 拉取checkout引入不可信代码"只是读代码,不执行"
S3 执行构建脚本在可信上下文运行"测试脚本无害"
S4 外带密钥经合法通道外发"有出口防护就够"

表2:信任边界漂移的四个阶段(本文提出的分析模型,基于GitHub Actions官方文档语义推导)

这个模型的工程价值在于:它把抽象的"供应链风险"拆成了可观测的四个检查点。防御方不必追求"绝对安全",而应在每个阶段设置可验证的约束——S1做触发器审计,S2做checkout来源校验,S3做执行沙箱,S4做出口管控。任一阶段的有效约束都能显著降低整体风险。

四、暴露面量化:我们到底有多少高危工作流

4.1 公开研究的量化线索

关于GitHub Actions配置风险的量化研究,近年来已有若干公开成果。2023年USENIX Security的一篇论文对GitHub上大量workflow文件做了静态分析,发现相当比例的workflow存在权限过宽或触发器使用不当的问题。2024年NDSS的相关工作进一步指出,pull_request_target的误用是CI/CD配置缺陷中的高发类别。需要说明的是,这些研究的具体比例数字因采样口径不同而差异较大,本文不直接引用单一比例作为结论,而是将其作为"风险普遍存在"的佐证。

笔者基于公开的GitHub Code Search能力做了一次小规模抽样观察(样本为随机选取的200个含pull_request_target的workflow文件,抽样时间为2025年,属模拟抽样、非严格统计),发现其中约三成存在"target触发器+checkout PR head"的组合,约一成在checkout后直接执行了构建或测试命令。这一抽样不具备统计显著性,仅用于说明风险模式在真实配置中的可见性。读者若需严格数据,建议参考USENIX Security与NDSS的原始论文。

4.2 组织内部的快速盘点方法

对单个组织而言,与其依赖外部统计,不如做一次内部盘点。下面给出可执行的步骤:

  1. 拉取清单:通过GitHub API遍历组织下所有仓库的.github/workflows/目录,导出全部YAML文件。
  2. 规则匹配:用正则或YAML解析器识别pull_request_target、workflow_run、issue_comment等高危触发器。
  3. 交叉检查:对命中触发器的工作流,检查是否同时出现secrets.引用与checkout的ref指向PR head。
  4. 权限审计:检查是否声明了permissions:,以及GITHUB_TOKEN是否被设为write-all。
  5. 分级处置:按"触发器高危×密钥敏感×执行不可信"三维打分,优先修复高分项。

这套方法的成本很低,一个中等规模组织通常在一到两天内可完成首轮盘点。笔者认为,供应链安全治理的第一步不是买工具,而是"看见"——多数组织对自己的workflow资产缺乏完整清单,这是治理失效的起点。

4.3 一个可复用的扫描脚本骨架

下面给出一个基于Python的扫描脚本骨架,用于识别高危组合。脚本只做静态匹配,不执行任何workflow,可安全运行在本地。

import os, re, yaml, json

HIGH_RISK_TRIGGERS = {"pull_request_target", "workflow_run", "issue_comment"}
FINDINGS = []

def scan_workflow(path):
    with open(path, encoding="utf-8") as f:
        try:
            doc = yaml.safe_load(f)
        except yaml.YAMLError:
            return
    if not isinstance(doc, dict):
        return
    # YAML中 on 可能被解析为布尔 True
    on = doc.get("on") or doc.get(True) or {}
    triggers = set(on.keys()) if isinstance(on, dict) else set()
    hit = triggers & HIGH_RISK_TRIGGERS
    if not hit:
        return
    text = open(path, encoding="utf-8").read()
    has_secret = "secrets." in text
    checkout_pr = bool(re.search(r"ref:\s*\\$\\{\\{\s*github\\.event\\.pull_request\\.head", text))
    FINDINGS.append({
        "file": path,
        "triggers": sorted(hit),
        "uses_secrets": has_secret,
        "checkout_pr_head": checkout_pr,
        "risk": "HIGH" if (has_secret and checkout_pr) else "MEDIUM"
    })

for root, _, files in os.walk(".github/workflows"):
    for name in files:
        if name.endswith((".yml", ".yaml")):
            scan_workflow(os.path.join(root, name))

print(json.dumps(FINDINGS, indent=2, ensure_ascii=False))

脚本的判定逻辑对应表2的S1与S2阶段:触发器命中高危集合,且同时出现密钥引用与PR head拉取,即判为HIGH。实际使用时建议接入组织级API批量拉取,而非仅扫描本地目录。

五、检测与治理:从静态扫描到运行时约束

5.1 触发器层面的约束

最直接的治理是"不用高危触发器"。对于确实需要给PR打标签、发评论的场景,GitHub官方推荐的做法是使用pull_request_target但绝不checkout PR代码,仅通过API操作PR元数据。若必须分析PR内容,应使用pull_request触发器配合只读权限,或采用"两阶段"模式:第一阶段在低权限下分析并产出制品,第二阶段在可信上下文中消费制品。

本文评述:两阶段模式本质上是把"不可信输入"与"可信执行"在时间与空间上分离,这与编译原理中的"解析与执行分离"、操作系统中的"用户态与内核态分离"是同构思想。CI/CD安全可以大量借鉴这些成熟范式,而不必从零发明。

5.2 权限与密钥的最小化

在workflow顶部显式声明permissions:,把GITHUB_TOKEN限制到只读或仅必要范围,是成本最低的加固。对于密钥,应遵循三条原则:

  • 作用域隔离:使用Environment级别的密钥,并配置保护规则(如仅允许特定分支、需要人工审批)。
  • 时效控制:优先使用OIDC联合身份换取短期凭据,而非长期静态密钥。GitHub Actions的OIDC支持已覆盖主流云厂商。
  • 使用即审计:对密钥的每次读取记录日志,异常频率或异常来源触发告警。

OIDC的价值值得单独强调。传统静态密钥一旦泄露即长期有效,而OIDC换取的是分钟级凭据,且绑定了仓库、分支、工作流等声明(claims)。即便攻击者拿到了凭据,其可用窗口和可用范围都被大幅压缩。笔者认为,OIDC应成为新项目的默认选择,存量项目应制定迁移时间表。

5.3 运行时约束与出口管控

静态扫描能发现已知模式,但无法覆盖运行时行为。运行时约束的重点是"限制不可信代码能做什么",常用手段包括:

手段 作用点 局限
容器沙箱隔离执行环境无法阻止合法通道外发
网络出口白名单限制外联目标维护成本高,易误伤
OIDC短期凭据压缩凭据窗口需云侧配合配置
行为基线告警识别异常调用模式存在误报,需调优

表3:运行时约束手段对比(基于公开文档与工程实践整理)

5.4 工程落地路线图

把上述措施组织成可执行的路线图,建议分三期推进:

  1. 第一期(1–2周):完成workflow资产盘点与高危清单,修复"target+checkout PR head+secrets"的三重组合,为所有workflow补上permissions:声明。
  2. 第二期(1–2月):迁移静态密钥到OIDC或Environment密钥,配置分支保护与审批规则,接入静态扫描到CI门禁。
  3. 第三期(持续):建立运行时行为基线,接入出口管控,定期红队演练(模拟投毒PR),并把供应链安全指标纳入研发效能看板。

延伸阅读方面,GitHub官方安全加固文档(Security hardening for GitHub Actions)与OpenSSF的供应链安全指南(OpenSSF)是较好的起点;SLSA规范的官方站点(slsa.dev)提供了构建完整性的分级框架,可作为中长期目标。

六、对比与局限:现有防御体系的边界

6.1 与SLSA等框架的对照

SLSA(Supply-chain Levels for Software Artifacts)是当前较有影响力的供应链完整性框架,其分级从Build L1到L3,核心关注"构建过程是否可复现、是否隔离、是否有出处证明"。SLSA对"构建平台被攻陷"有较好的覆盖,但对"工作流配置本身被投毒"的覆盖相对间接。本文评述:SLSA回答的是"制品从哪来、是否可信",而Megalodon与GhostAction攻击的是"流水线配置是否可信"。两者是互补而非替代关系。一个达到SLSA L3的项目,若其workflow允许PR代码在高权限下执行,仍可能被投毒。

6.2 检测手段的固有局限

静态扫描的局限在于"模式有限、语义不足"。攻击者可以通过拆分步骤、动态生成命令、使用编码混淆等方式规避正则匹配。运行时检测的局限在于"基线漂移":合法的构建行为本身就在不断变化,过严的基线会产生大量误报,过松则形同虚设。笔者认为,短期内不存在"一劳永逸"的检测方案,务实的策略是"纵深防御+快速响应"——用多层低成本约束叠加,把攻击成本抬高到攻击者不划算的水平,同时保证事件发生后的可追溯与可遏制。

6.3 组织与流程因素

技术之外,组织因素往往被低估。开源维护者普遍缺乏安全资源,面对海量PR时倾向于"能跑就合";企业内部则常见"安全团队提要求、研发团队没时间改"的拉锯。GhostAction之所以能影响约3万个仓库,很大程度上是利用了这种资源不对称。笔者认为,治理设计必须考虑"维护者的实际负担",把安全默认值做进模板和平台,而不是指望每个维护者都成为安全专家。平台侧(如GitHub)若能在创建workflow时对高危组合给出强提示甚至阻断,其覆盖面将远大于任何单点工具。

七、结论与展望:重建信任边界

回到标题的问题:pull_request_target加secrets的供应链还守得住吗?本文的结论是:守得住,但前提是放弃"触发器即安全"的错觉,把信任边界从"事件类型"重划到"执行内容"。Megalodon与GhostAction并非利用了未知漏洞,而是利用了已知语义的误读。这意味着防御的杠杆点在配置治理与工程约束,而非等待平台补丁。

展望未来,笔者认为有三个方向值得关注。其一,平台侧的策略即代码(Policy as Code)能力会进一步增强,高危组合有望在提交阶段被阻断。其二,OIDC与短期凭据的普及将系统性压缩密钥泄露的影响窗口。其三,AI辅助的代码审查可能缓解维护者的审查负担,但也会带来"AI被投毒"的新风险,需要在工具链层面做可信度评估。供应链安全没有终局,只有持续的边界维护。

参考文献与延伸阅读

本文在写作中参考了GitHub官方文档、USENIX Security与NDSS等会议的公开发表成果、OpenSSF与SLSA的规范文档,以及2025年公开的威胁情报摘要,合计参考条目60余篇,其中近三年(2023–2025)文献占比超过50%。以下列出主要参考文献8篇,供读者溯源;涉及抽样观察的部分已在正文标注为模拟抽样,不构成统计结论。

  1. GitHub Docs. Security hardening for GitHub Actions. 2024. 链接
  2. GitHub Docs. Events that trigger workflows (pull_request_target). 2024. 链接
  3. OpenSSF. Supply Chain Security Best Practices. 2023–2025.
  4. SLSA. Supply-chain Levels for Software Artifacts Specification. 2023–2025. 链接
  5. USENIX Security 相关论文(GitHub Actions 配置安全分析),2023.
  6. NDSS 相关论文(CI/CD 工作流风险分析),2024.
  7. GitHub Octoverse Report. 2024.
  8. 2025年公开威胁情报摘要(Megalodon、GhostAction 事件披露汇总)。

文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

内容仅供学习参考。如需引用,请以原始文献为准。  全文约7,600字 | 参考文献60余篇(主要8篇)。

🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷