Inside a 37-Package npm Typosquatting Campaign Targeting Windows and WSL

11 mins read
·Supply Chain Security·
Thirty-seven npm typosquat packages converging through Windows and WSL into an infostealer targeting credentials, cryptocurrency wallets, and Telegram data

TL;DR:

  • OpenHack detected and correlated 37 npm typosquats carrying the same install-time malware.
  • Their postinstall hook XOR-decoded a GitHub-hosted Windows loader, beaconed to 193[.]70[.]34[.]101:20099, and launched the payload on native Windows and through WSL.
  • Static analysis linked the loader to a Go infostealer targeting browser credentials and sessions, payment-card data, cryptocurrency wallets and seed phrases, Telegram data, and host information.
  • Ordinary Linux and macOS hosts did not execute the observed Windows PE, but the package code still sent platform telemetry to the operator.
  • This was a coordinated typosquatting campaign, not a worm: we found no self-replication, maintainer-account propagation, or compromise of the legitimate Chalk, Commander, Lodash, TypeScript, React, or Axios packages.
  • All 37 npm package versions returned 404 when checked on August 17, but the GitHub payload remained retrievable. Windows or WSL systems that installed one of the packages should be treated as potentially compromised.

On August 16, 2026, OpenHack detected a burst of newly published npm packages impersonating popular JavaScript projects. Names such as chalk-core, comander-cli, loadashjs, raectjs, and axois-http were designed to catch a mistyped install command or look plausible during a hurried dependency search.

The packages were not independent experiments. Static correlation found the same 6,409-byte functional postinstall.js in all 37 releases after comment padding was removed. That script contained XOR-obfuscated configuration, a fixed telemetry endpoint, and platform-specific branches for native Windows and Windows Subsystem for Linux (WSL).

Our analysis reconstructed the following chain without installing a package or executing its code:

  1. A developer installs one of the typosquatted packages.
  2. npm invokes its postinstall lifecycle hook.
  3. The hook decodes a GitHub Release URL and a hidden PowerShell launcher.
  4. Every supported host sends its platform label to a fixed IP over cleartext HTTP.
  5. Native Windows and WSL hosts download and start a 22 MB Windows executable.
  6. That Rust loader decrypts and maps an embedded 64-bit Go payload.
  7. The Go payload collects browser data, cryptocurrency wallet material, Telegram data, and host information before packaging and exfiltrating it.

As of August 17, all 37 package-version endpoints returned 404 from the npm registry. The second-stage GitHub Release asset, however, remained retrievable when we last checked. Defenders should treat the indicators in this post as active until their own telemetry shows otherwise.

This Was a Typosquat Campaign, Not a Worm

There is no evidence in the samples we analyzed of self-replication, automated npm account takeover, or malicious republishing through compromised maintainers. The campaign is best described as a coordinated typosquat spray delivering an infostealer.

That distinction matters. A worm spreads by creating its own next set of victims. These packages relied on a user or automated system selecting one of the attacker's names. The shared code and compressed publication window show coordination, but coordination alone is not replication.

The legitimate chalk, commander, lodash, typescript, react, and axios packages were not implicated by this incident. The malicious releases used lookalike names; this was not a compromise of those upstream projects or their maintainers.

The Package Cluster

The incident joined 37 packages on a normalized source fingerprint rather than on naming similarity alone. Every linked release was version 1.0.0.

Target themeObserved malicious package names
Chalkchalk-core, chalk-lib, chalk-util, chalk-es
Commandercomand, comander-cli, comanderjs, commandorjs, commandor-cli, commandor-core, comander-lib, commandor-lib, commander-lib
Lodashloadashjs, lodash-lib, ladash-cli, lodahsjs, lodsh-cli, lodahs-cli, lodhash-cli
TypeScripttypescirpt-cli, typscript-cli, typesript-cli, typscript-core, typescriptt-cli, typescrip-cli, typescipt-cli, tyepescript-cli, typescirpt-core, tyepescript-core, typesript-core, typescipt-core, typescriptt-core
Reactraectjs
Axiosaxois-http, axious-core
Initial linked sampletestingsmthb1g

The naming strategy mixes simple transpositions (raectjs), omitted letters (typescrip-cli), extra letters (typescriptt-core), phonetic substitutions (commandor-core), and plausible package-family suffixes such as -core, -lib, and -cli.

That last category is particularly useful to an attacker. A name like chalk-core does not merely look like a typo; it looks like it could be an internal component of a real package family.

Stage 1: Install-Time Execution

Each package declared a lifecycle script that routed installation into scripts/postinstall.js. npm lifecycle hooks execute during a normal install unless scripts are explicitly disabled, so the package did not need to be imported by an application.

The dropper imported Node.js networking and process-spawning functionality and selected behavior based on the host platform. Large integer arrays held encoded strings. The script decoded them with a repeated XOR key:

stf2026

Static decoding recovered two operationally important values:

  • a GitHub Release URL for main.exe
  • a PowerShell command that writes the executable into a temporary path and starts it with a hidden window

The obfuscation is not cryptographically meaningful. Repeated-key XOR only prevents a URL or command from appearing as a plain string in a superficial scan. Once the key and byte arrays are present in the same file, decoding is deterministic.

Stage 2: Telemetry on Every Platform

Before attempting payload execution, the script sends a platform label via an HTTP POST request to:

hxxp://193[.]70[.]34[.]101:20099/vote

Because this request uses cleartext HTTP, network controls can detect it without TLS interception. It also means that even a host that cannot execute the Windows payload may still reveal its source IP and platform to the operator.

This creates three different impact levels:

EnvironmentObserved behavior
Native WindowsBeacon, download main.exe, and start it hidden
WSLBeacon, invoke Windows powershell.exe from Linux, and launch the same Windows payload
Ordinary Linux or macOSBeacon observed in code; no compatible execution path for the PE payload was identified

The WSL branch is the notable one. Treating process.platform === "linux" as proof that a package cannot affect Windows misses the bridge available under WSL. The dropper looks for that environment and reaches across the boundary into the Windows host.

We did not observe a native Linux or macOS payload in this package set. That does not make installation harmless on those systems: the install hook still performs outbound telemetry, and a future payload at the same infrastructure could behave differently.

Stage 3: The GitHub-Hosted Loader

The decoded download location pointed to a GitHub Release asset in the repository path below. It is deliberately defanged here:

github[.]com/bebraz1/qPzM50V1AKG0rVlH/releases/download/null/main.exe

On August 17, we fetched the asset as inert data, did not execute it, and independently verified:

PropertyValue
Size22,969,344 bytes
SHA-2566f088ade49456db2422c3edfbb9998f4a3e9cce7c4c00a7279fb45d672a82b7d

Static inspection identified the outer executable as a Rust loader. Its purpose is to recover and map an embedded 64-bit Windows PE rather than perform collection itself.

The embedded payload has the following SHA-256:

1afff50ca4064310d3492c652e1c3168216dcb42063e0b26c223038db46b8731

Recovered symbols and strings identify that inner payload as a Go infostealer.

Stage 4: Collection and Exfiltration

The embedded Go payload contains functionality associated with:

  • browser credential, cookie, history, and payment-card collection
  • browser application-bound encryption bypass or injection logic
  • cryptocurrency wallet-file and seed-phrase discovery
  • Telegram tdata collection
  • host and user profiling
  • encrypted ZIP creation
  • upload through GoFile
  • webhook notification after upload

The presence of a capability in static code does not prove it ran on a specific victim. It does establish what the retrieved payload was built to do. We have no verified victim count and make no claim about successful exfiltration.

GoFile and generic webhook services are legitimate infrastructure and should not be blocked indiscriminately. Defenders should instead look for unexpected uploads from developer workstations, correlate them with package-install activity, and inspect surrounding process ancestry.

Detection and Hunting

Network indicators

TypeIndicatorRole
IP and port193[.]70[.]34[.]101:20099Install-time telemetry
URL pathhxxp://193[.]70[.]34[.]101:20099/votePlatform beacon
GitHub assetgithub[.]com/bebraz1/qPzM50V1AKG0rVlH/releases/download/null/main.exeWindows loader
Servicegofile[.]ioExfiltration service used by the payload
Serviceapi[.]ipify[.]orgPublic-IP discovery

The last two domains provide legitimate services. Hunt for contextually unusual use rather than treating all traffic to them as malicious.

File hashes

ArtifactSHA-256
Rust loader (main.exe)6f088ade49456db2422c3edfbb9998f4a3e9cce7c4c00a7279fb45d672a82b7d
Embedded Go PE1afff50ca4064310d3492c652e1c3168216dcb42063e0b26c223038db46b8731

Host and package-manager hunting

Defenders should look for:

  • any of the 37 package names in package-lock.json, npm-shrinkwrap.json, Yarn or pnpm lockfiles, local npm caches, CI logs, or artifact manifests
  • node or npm processes spawning hidden PowerShell during dependency installation
  • WSL processes invoking a Windows powershell.exe
  • downloads of the loader hash above from GitHub release infrastructure
  • HTTP POST requests to 193[.]70[.]34[.]101:20099/vote
  • unusual GoFile uploads from developer machines following an npm install

A registry 404 today does not remove a package from an existing lockfile, cache, build artifact, or developer workstation.

Response Guidance

If one of these package versions was installed on native Windows or under WSL, treat the host as potentially compromised—not merely the project directory.

  1. Isolate the affected host and preserve relevant process, network, npm, PowerShell, and WSL telemetry.
  2. Block the fixed IP and port, and block the exact GitHub asset path without broadly blocking GitHub.
  3. Hunt for both payload hashes across endpoints and artifact stores.
  4. Invalidate browser sessions and rotate credentials accessible from the affected user context.
  5. Treat stored browser passwords, payment data, wallet secrets, seed phrases, and Telegram session material as potentially exposed.
  6. Remove malicious versions from lockfiles and caches, then rebuild from a known-clean environment.
  7. Review CI runners that installed the packages; a short-lived runner can still leak durable credentials.

Simply deleting node_modules is not sufficient after an install-time payload has launched outside the package directory.

How OpenHack Correlated the Incident

Looking at any one package would have produced a useful malicious-package alert. The campaign became visible when the findings were treated as a set.

OpenHack's scanner handled package archives as hostile input. It verified and unpacked them under bounded conditions, inspected their contents statically, and never invoked npm lifecycle hooks, imported package modules, or evaluated source. Confirmed findings were assigned normalized code fingerprints. Once at least three findings shared an exact fingerprint, the incident pipeline correlated the package set and performed a campaign-level analysis.

For this cluster, normalization removed comment padding while preserving functional code. All 37 samples converged on the same fingerprint. The incident analysis then:

  • decoded bounded base64, hexadecimal, and repeated-key XOR artifacts
  • extracted network and file indicators
  • reconstructed platform-specific control flow
  • linked the outer loader and embedded payload hashes
  • separated observed behavior by Windows, WSL, Linux, and macOS
  • retained every raw finding behind the incident for review

This is the practical difference between a findings feed and incident detection. Thirty-seven nearly identical alerts are noisy. One correlated campaign with a shared delivery chain, exact indicators, and platform-specific impact is actionable.

Limitations

This report is based primarily on static analysis. We did not install the npm packages or execute either payload. Static analysis can establish code paths, embedded configuration, payload structure, and intended capabilities, but it cannot prove how many machines installed the packages or which capabilities successfully ran on a victim.

We also do not attribute this campaign to a named actor. Shared code proves that the packages belong to the same operational cluster; it does not establish the real-world identity of the operator.

The infrastructure status in this post is a point-in-time observation from August 17, 2026. The npm packages may remain available in mirrors or caches even though the registry endpoints returned 404, and the payload URL can be removed or replaced after publication.

Takeaways

Install scripts remain a high-value execution boundary. A package does not need to be imported to run code when npm lifecycle hooks are enabled.

WSL changes platform assumptions. Linux-hosted Node.js code can invoke Windows tooling and affect the underlying Windows environment.

Typosquats can be coordinated without being worms. Shared infrastructure and synchronized publishing describe a campaign. Self-replication is a separate behavior and was not observed here.

Correlation turns alerts into an incident. The strongest evidence came from joining exact source fingerprints across dozens of names and reconstructing the common chain.

Takedown is not remediation. Removal from the public registry does nothing to copies already present in lockfiles, caches, CI artifacts, or installed environments.

If you have additional telemetry related to these indicators, contact OpenHack through our security disclosure page.

Ananay Arora

Written by

Founder, OpenHack