文章总结: 文档剖析了软件供应链攻击利用隐性信任假设的原理,涵盖拼写仿冒、密钥窃取、流水线污染等攻击手段及XZUtils等案例。重点介绍了TypoGard、Zizmor、PyPI可信发布、Homebrew构建来源及GoCapslock等前沿防御工具,强调将隐性信任转化为可验证保证。建议开发者积极采用工具验证证明与功能,以提升供应链安全韧性。 综合评分: 90 文章分类: 供应链安全,安全工具,漏洞分析,安全建设,威胁情报
分析软件供应链攻击原理及防御工具
aeverj
红队工坊
2025年12月26日 06:19 北京
翻译自 Supply chain attacks are exploiting our assumptions
每当你运行 cargo add 或 pip install 命令时,你其实是在进行一次信任的跳跃。你相信下载的代码包含你期望的内容,来自你期望的来源,并且会按你期望的方式运行。这些期望如此根植于现代开发流程,以至于我们很少去思考它们。然而,攻击者正在系统性地利用这些假设中的每一个漏洞。
仅在 2024 年,PyPI 和 npm 就删除了数千个恶意软件包;多个知名项目的构建过程被直接注入了恶意软件;XZ Utils 后门差点进入全球数百万个 Linux 系统。
依赖扫描只能捕获已知的漏洞。当一个拼写相似的恶意软件包窃取你的凭证时,当一个被入侵的维护者发布恶意软件时,或者当攻击者污染构建流水线本身时,依赖扫描都无能为力。这些攻击之所以成功,正是因为它们利用了使现代软件开发成为可能的信任机制。
本文将剖析使软件供应链变得脆弱的信任假设,分析利用这些假设的近期攻击案例,并重点介绍各个生态系统正在构建的一些前沿防御措施——这些措施旨在将隐性信任转变为明确的、可验证的保证。
隐性信任
对许多开发者来说,软件供应链的安全始于且止于软件物料清单(SBOM,Software Bill of Materials)和依赖扫描,它们共同回答两个基本问题:你有什么代码,以及它是否包含已知漏洞?但了解你拥有什么只是最低要求。随着复杂攻击变得越来越常见,你还需要了解代码来自哪里,以及它如何到达你手中。
你相信自己正在安装预期的软件包。 你认为运行 cargo add rustdecimal 是安全的,因为 rustdecimal 是一个知名且广泛使用的库。或者等等,它的拼写是不是应该是 rust_decimal?
你相信软件包是由维护者发布的。 当一个流行的软件包开始附带预编译的二进制文件以节省构建时间时,你可能会选择信任软件包作者。然而,许多软件仓库缺乏强有力的验证机制来确认发布者就是他们声称的那个人。
你相信软件包是从源代码构建的。 你可能在一个注重安全的团队工作,在升级依赖项之前会审计公共仓库中的代码变更。但如果分发的软件包是从仓库中不存在的代码构建的,这种审计就毫无意义。
你相信维护者本身。 归根结底,安装第三方代码意味着信任软件包维护者。审计你所依赖的每一行代码是不现实的。我们假设那些成熟且被广泛采用的软件包的维护者不会突然决定添加恶意代码。
这些假设延伸到传统软件包管理器之外。当你运行 GitHub Action、使用 Homebrew 安装工具,或执行便捷的 curl ... | bash 安装脚本时,同样的信任关系也存在。理解这些隐性信任关系是评估和缓解供应链风险的第一步。
近期攻击
攻击者正在利用供应链每一层的信任假设。近期事件从简单的拼写仿冒到多年的攻击活动都有,展示了攻击者的战术如何不断演进和变得更加复杂。
以假乱真
拼写仿冒(Typosquatting)指的是发布一个名称与合法软件包相似的恶意软件包。运行 cargo add rustdecimal 而不是 rust_decimal 可能会安装恶意软件而不是预期的合法库。这种[确切的攻击][1]在 2022 年就发生在 crates.io 上。恶意的 rustdecimal 模仿了流行的 rust_decimal 软件包,但包含一个 Decimal::new 函数,在调用时会执行恶意二进制文件。
[1] https://blog.rust-lang.org/2022/05/10/malicious-crate-rustdecimal/
这种攻击的简单性使得攻击者能够轻松发起大规模活动,尤其是针对 PyPI 和 npm。自 2022 年以来,已经出现多次针对软件包的拼写仿冒活动,这些软件包的[每周下载量总计达 12 亿次][2]。仅 PyPI 和 npm 就发布了数千个恶意软件包。这类攻击如此频繁,以至于这里无法一一列举。2023 年,研究人员记录了[一次注册了 40 个流行 PyPI 软件包的 900 个拼写仿冒变体的活动][3],并发现了[在 crates.io 上暂存的恶意软件][4]。攻击只增不减,仅在 2024 年的一次活动中就[发布了 500 个恶意软件包][5]。
[2] https://blog.phylum.io/phylum-detects-active-typosquatting-campaign-targeting-npm-developers/
[3] https://blog.phylum.io/a-pypi-typosquatting-campaign-post-mortem/
[4] https://blog.phylum.io/rust-malware-staged-on-crates-io/
[5] https://blog.phylum.io/typosquatting-campaign-targets-python-developers/
依赖混淆(Dependency confusion)采用不同的方法,直接利用软件包管理器的逻辑。安全研究员 Alex Birsan 在 2021 年[演示并命名][6]了这种攻击类型。他发现许多组织使用的内部软件包名称要么被泄露,要么可以猜测。通过向公共仓库发布与这些内部软件包同名的软件包,Birsan 成功诱使软件包管理器下载他的版本。Birsan 的概念验证在三种编程语言和 35 个组织中发现了漏洞,包括 Shopify、Apple、Netflix、Uber 和 Yelp。
[6] https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
2022 年,一名攻击者使用这种技术在 PyTorch 的每日构建版本中[包含了恶意代码][7],持续了五天。一个名为 torchtriton 的内部依赖项托管在 PyTorch 的每日构建软件包索引中。攻击者向 PyPI 发布了同名的恶意软件包,该软件包优先级更高。结果,PyTorch 的每日构建版本在恶意软件被发现之前包含了五天的恶意代码。
[7] https://pytorch.org/blog/compromised-nightly-dependency/
虽然这些攻击发生在安装阶段,但其他攻击采取了更直接的方式,通过入侵发布过程本身来实施。
窃取的密钥
被入侵的账户是另一个常见的攻击载体。攻击者获取泄露的密钥、被盗的令牌或猜出的密码,就能够代表受信任的实体直接发布恶意代码。几个近期事件展示了这类攻击的规模:
- ctrl/tinycolor(2025 年 9 月):[自我传播的恶意软件][8]收集了 npm API 凭证,并使用这些凭证发布了更多恶意软件包。超过 40 个软件包被入侵,每周下载量超过 200 万次。
- Nx(2025 年 8 月):一个被入侵的令牌使攻击者能够发布[恶意版本][9],其中包含利用已安装的 AI CLI 工具(Claude、Gemini、Q)进行侦察的脚本,从数千名开发者那里窃取加密货币钱包、GitHub/npm 令牌和 SSH 密钥,然后将数据泄露到公共 GitHub 仓库。
- rand-user-agent(2025 年 5 月):一个[包含恶意软件的恶意版本][10]直到研究人员注意到尽管源代码几个月没有变化,但最近有新版本发布时才被发现。
- rspack(2024 年 12 月):被盗的 npm 令牌使攻击者能够在每周下载量合计 50 万次的软件包中[发布加密货币挖矿程序][11]。
- UAParser.js(2021 年 10 月):一个被入侵的 npm 令牌被用于发布包含加密货币挖矿程序的恶意版本。该库在攻击时每周下载量达数百万次。
- PHP Git 服务器(2021 年 3 月):被盗的凭证使攻击者能够[直接向 PHP 的源代码注入后门][12]。幸运的是,PHP 团队在任何发布之前就轻松发现并删除了更改的内容。
- Codecov(2021 年 1 月):攻击者在公共 Docker 镜像层中发现了一个部署密钥,并使用它[修改了 Codecov 的 Bash Uploader 工具][13],在被发现前数月内悄悄泄露环境变量和 API 密钥。
[8] https://www.stepsecurity.io/blog/ctrl-tinycolor-and-40-npm-packages-compromised
[9] https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c
[10] https://www.bleepingcomputer.com/news/security/supply-chain-attack-hits-npm-package-with-45-000-weekly-downloads/
[11] https://www.sonatype.com/blog/npm-packages-rspack-vant-compromised-blocked-by-sonatype
[12] https://news-web.php.net/php.internals/113981
[13] https://about.codecov.io/apr-2021-post-mortem/
窃取密钥仍然是最可靠的供应链攻击载体之一。但随着组织实施更强的身份验证和更好的密钥管理,攻击者正在从窃取密钥转向入侵使用这些密钥的系统。
污染的流水线
一些攻击者没有窃取凭证,而是通过入侵构建和分发系统本身,通过合法渠道分发恶意软件。通过直接向 CI/CD 流水线注入恶意代码,代码审查和其他安全检查被完全绕过。
2020 年的 SolarWinds 攻击是这类攻击中最知名的案例之一。攻击者入侵了构建环境,并在编译期间[直接向 Orion 软件插入恶意代码][14]。恶意版本的 Orion 随后被签名并通过 SolarWinds 的合法更新渠道分发。该攻击影响了数千个组织,包括多家财富 500 强公司和政府机构。
[14] https://www.solarwinds.com/blog/new-findings-from-our-investigation-of-sunburst
最近,在 2024 年末,一名攻击者[入侵了 Ultralytics 构建流水线][15]以发布多个恶意版本。攻击者利用项目 GitHub Actions 中的模板注入漏洞获取了 CI/CD 流水线的访问权限,并污染了 GitHub Actions 缓存,直接在构建中包含恶意代码。攻击时,Ultralytics 每周下载量超过 100 万次。
[15] https://blog.pypi.org/posts/2024-12-11-ultralytics-attack-analysis/
2025 年,一名攻击者修改了 reviewdog/actions-setup GitHub Action 的 v1 标签,[使其指向包含转储密钥代码的恶意版本][16]。这可能导致另一个流行的 action tj-actions/changed-files 通过其对 tj-actions/eslint-changed-files 的依赖被入侵,而后者又依赖于被入侵的 reviewdog action。这种级联入侵影响了数千个使用 changed-files action 的项目。
[16] https://www.wiz.io/blog/new-github-action-supply-chain-attack-reviewdog-action-setup
虽然污染流水线攻击相对于拼写仿冒或凭证盗窃来说较为罕见,但它们代表了攻击者复杂程度的升级。随着更强的防御措施到位,攻击者被迫向供应链上游移动。最坚定的攻击者愿意花费数年时间准备一次攻击。
恶意维护者
XZ Utils 后门于 2024 年 3 月被发现,差点危及全球数百万个 Linux 系统。攻击者花了两年多时间为项目做出合法贡献,然后获得了维护者访问权限。随后他们滥用这种信任,通过一系列看似无害的提交插入了一个复杂的后门,这本可以授予对任何使用被入侵版本的系统的远程访问权限。
归根结底,你必须信任你依赖项的维护者。安全的构建流水线无法防御决定插入恶意代码的受信任维护者。随着开源维护者日益不堪重负,以及 AI 工具使大规模生成令人信服的贡献变得更容易,这种信任模型正面临前所未有的挑战。
新的防御措施
随着攻击变得更加复杂,防御者正在构建与之匹配的工具。这些新方法正在将信任假设从隐性且可利用的变为明确且可验证的。每种方法都针对供应链中攻击者曾取得成功的不同层面。
TypoGard 和 Typomania
大多数软件包管理器现在都包含某种形式的拼写仿冒保护,但它们通常使用传统的相似度检查,如测量 Levenshtein 距离,这会产生大量需要人工审查的误报。
[TypoGard][17] 填补了这一空白,它使用多个上下文感知指标来检测拼写仿冒软件包,具有低误报率和最小开销,这些指标包括:
[17] https://github.com/mt3443/typogard
- 重复字符(例如,
rustdeciimal) - 基于键盘布局的常见拼写错误
- 交换字符(例如,
reqeusts而不是requests) - 软件包流行度阈值,以专注于高风险目标
该工具针对 npm,但其概念可以扩展到其他语言。Rust 基金会发布了一个 Rust 移植版本 [Typomania][18],已被 [crates.io 采用][19],并成功捕获了多个恶意软件包。
[18] https://github.com/rustfoundation/typomania
[19] https://github.com/rust-lang/crates.io/tree/main/src/typosquat
Zizmor
[Zizmor][20] 是一个用于 GitHub Actions 的静态分析工具。Actions 有很大的攻击面,编写复杂的工作流可能很困难且容易出错。工作流可能以许多微妙的方式引入漏洞。
[20] https://github.com/zizmorcore/zizmor
例如,Ultralytics 就是通过其工作流中的模板注入被入侵的。
- name: Commit and Push Changes
if: (... || github.event_name == 'pull_request_target' || ...
run: |
...
git pull origin ${{ github.head_ref || github.ref }}
...
由 pull_request_target 事件触发的工作流以对仓库密钥的写入权限运行。攻击者[从一个具有恶意名称的分支打开了一个拉取请求][21]。当工作流运行时,github.head_ref 变量扩展为恶意分支名称,并作为具有工作流提升权限的运行命令的一部分执行。
[21] https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-injection
reviewdog/actions-setup 攻击也部分通过更改 action 的 v1 标签使其指向恶意提交来实现。任何在工作流中使用 reviewdog/actions-setup@v1 的人都悄悄开始获取恶意版本,而无需对自己的工作流进行任何更改。
Zizmor 标记了上述所有问题。它包含一个危险触发器规则来标记由 pull_request_target 触发的工作流,一个模板注入规则,以及一个未固定使用检查,该检查会警告在使用 reviewdog/actions-setup@v1 时不要使用可变引用(如标签或分支名称)。
PyPI 可信发布和证明
PyPI 通过两个互补功能采取了重要步骤来解决几个隐性信任假设:可信发布(Trusted Publishing)和证明(attestations)。
Trail of Bits 与 PyPI 合作开发了[可信发布][22],它消除了对长期 API 令牌的需求。开发者不再存储可能被窃取的密钥,而是配置一次信任关系:”这个 GitHub 仓库和工作流可以发布这个软件包。”当工作流运行时,GitHub 向 PyPI 发送一个短期 OIDC 令牌,其中包含有关仓库和工作流的声明。PyPI 验证该令牌是由 GitHub 的密钥签名的,并响应一个短期 PyPI 令牌,工作流可以使用该令牌发布软件包。使用自动生成的、最小范围的、短期令牌大大降低了被入侵的风险。
[22] https://blog.trailofbits.com/2023/05/23/trusted-publishing-a-new-benchmark-for-packaging-security/
没有了长期且权限过大的 API 令牌,攻击者必须改为入侵发布 GitHub 工作流本身。虽然 Ultralytics 攻击表明 CI/CD 流水线入侵仍然是真实威胁,但消除用户手动管理凭证的需要消除了用户错误的来源,并进一步减少了攻击面。
在此基础上,Trail of Bits 再次与 PyPI 合作,在 2024 年末通过 PEP 740 引入了[索引托管的数字证明][23]。证明使用 Sigstore 将每个发布的软件包加密绑定到其构建来源。使用 [PyPI 发布 GitHub Action][24] 的软件包会自动包含证明,它作为软件包构建的确切位置、时间和方式的可验证记录。
[23] https://blog.trailofbits.com/2024/11/14/attestations-a-new-generation-of-signatures-on-pypi/
[24] https://github.com/pypa/gh-action-pypi-publish
图 1: 我们实现 PEP 740 了吗?
超过 30,000 个软件包使用可信发布,”[我们实现 PEP 740 了吗?][25]”追踪最流行软件包中证明的采用情况(在撰写本文时,前 360 个中有 86 个)。最后一部分,自动客户端验证,仍在进行中。像 pip 和 uv 这样的客户端工具还没有自动验证证明。在那之前,证明提供透明度和可审计性,但在软件包安装期间不提供主动保护。
[25] https://trailofbits.github.io/are-we-pep740-yet/
Homebrew 构建来源
隐性信任假设延伸到编程语言和库之外。当你运行 brew install 来安装二进制软件包(或称为 bottle)时,你相信下载的 bottle 是由 Homebrew 的官方 CI 从预期的源代码构建的,并且没有被找到方法入侵 Homebrew bottle 托管或以其他方式篡改 bottle 内容的攻击者上传。
Trail of Bits 与 Alpha-Omega 和 OpenSSF 合作,帮助使用 GitHub 的证明为 [Homebrew 添加了构建来源][26]。Homebrew 构建的每个 bottle 现在都附带加密证明,将其链接到创建它的特定 GitHub Actions 工作流。这使得被入侵的维护者更难悄悄用恶意版本替换 bottle。
[26] https://blog.trailofbits.com/2024/05/14/a-peek-into-build-provenance-for-homebrew/
% brew verify --help
Usage: brew verify [options] formula [...]
Verify the build provenance of bottles using GitHub's attestation tools.
This is done by first fetching the given bottles and then verifying their provenance.
每个证明都包括 Git 提交、运行的工作流和其他构建时元数据。这将信任假设(“我相信这个 bottle 是从我期望的源代码构建的”)转变为可验证的事实。
证明的实现通过”回填”过程处理历史 bottle,为系统启用之前构建的软件包创建证明。因此,所有官方 Homebrew 软件包都包含证明。
brew verify 命令使检查来源变得简单,尽管该功能仍处于测试阶段,验证默认情况下不是自动的。有计划最终将此功能扩展到第三方仓库,为更广泛的 Homebrew 生态系统带来相同的安全保证。
Go Capslock
[Capslock][27] 是一个静态识别 Go 程序功能的工具,包括以下内容:
[27] https://github.com/google/capslock
- 文件系统操作(读取、写入、删除文件)
- 网络连接(出站请求、监听端口)
- 进程执行(生成子进程)
- 环境变量访问
- 系统调用使用
% capslock --packages github.com/fatih/color
Capslock is an experimental tool for static analysis of Go packages.
Share feedback and file bugs at https://github.com/google/capslock.
For additional debugging signals, use verbose mode with -output=verbose
To get machine-readable full analysis output, use -output=jso`
Analyzed packages:
github.com/fatih/color v1.18.0
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.20
golang.org/x/sys v0.25.0
CAPABILITY_FILES: 1 references
CAPABILITY_READ_SYSTEM_STATE: 41 references
CAPABILITY_SYSTEM_CALLS: 1 references
这种方法代表了供应链安全的转变。功能分析不是关注谁编写了代码或代码来自哪里,而是检查代码实际上能做什么。一个意外获得网络访问权限的 JSON 解析库会立即引发警报,无论变更是来自被入侵的供应链还是直接来自维护者。
在实践中,静态功能检测可能很困难。运行时反射和不安全操作等语言特性使得完全准确地静态检测功能变得不可能。尽管存在局限性,功能检测仍作为针对供应链攻击的分层防御的关键安全网。
Capslock 为 Go 开创了这种方法,这个概念已经成熟,可以在其他语言中采用。随着供应链攻击变得更加复杂,功能分析提供了一条有希望的前进道路。验证代码能做什么,而不仅仅是它来自哪里。
未来之路
供应链攻击并没有放缓。如果说有什么变化的话,它们正变得更加自动化、更加复杂、更加精密,以瞄准更广泛的受众。拼写仿冒活动正在针对下载量达数十亿次的软件包,发布者令牌和 CI/CD 流水线被入侵以从源头污染软件,而耐心的攻击者在发起攻击前花费数年时间建立声誉。
使软件生态系统能够扩展的隐性信任正在被武器化来对付我们。理解你的信任假设是第一步。问自己这些问题:
- 我的生态系统会阻止拼写仿冒软件包吗?
- 它如何防止被入侵的发布者令牌?
- 我能验证构建来源吗?
- 我知道我的依赖项有什么功能吗?
一些生态系统已经开始构建防御措施。了解有哪些工具可用,并从今天开始使用它们。发布到 [PyPI][28] 或 [crates.io][29] 时使用可信发布。使用 [Zizmor][30] 检查你的 GitHub Actions。使用 [It-Depends][31] 和 [Deptective][32] 来了解软件实际依赖什么。在可行的情况下验证证明。使用 [Capslock][33] 查看 Go 软件包的功能,更重要的是,注意何时引入了新功能。
[28] https://docs.pypi.org/trusted-publishers/using-a-publisher/
[29] http://crates.io/
[30] https://github.com/zizmorcore/zizmor
[31] https://github.com/trailofbits/it-depends
[32] https://github.com/trailofbits/deptective
[33] https://github.com/google/capslock
但没有哪个生态系统是完全覆盖的。在工具缺乏的地方推动更好的默认设置。每一个经过验证的证明、每一个被捕获的拼写仿冒软件包、每一个被标记的有漏洞的 GitHub Action 都使整个行业更具韧性。我们无法完全消除供应链中的信任,但我们可以努力使这种信任变得明确、可验证和可撤销。
请在微信客户端打开
如果你对网络安全、红队攻防技术充满热情,渴望学习更多实战技巧,例如渗透测试、自动化脚本编写、免杀技术等, 欢迎关注我的公众号
在这里,我会持续分享更多高质量的技术文章,与你一同探索网络安全的奥秘,提升实战技能! 让我们一起在队攻防的道路上,不断精进,突破边界!
免责声明: 本文仅供安全技术研究与学习交流之用。 严禁将本文所提及的技术用于任何非法用途,包括但不限于未经授权的渗透测试、网络攻击、恶意代码传播等。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:红队工坊 aeverj《分析软件供应链攻击原理及防御工具》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论