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

TL;DR:
- OpenHack detected and correlated 37 npm typosquats carrying the same install-time malware.
- Their
postinstallhook XOR-decoded a GitHub-hosted Windows loader, beaconed to193[.]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
404when 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:
- A developer installs one of the typosquatted packages.
- npm invokes its
postinstalllifecycle hook. - The hook decodes a GitHub Release URL and a hidden PowerShell launcher.
- Every supported host sends its platform label to a fixed IP over cleartext HTTP.
- Native Windows and WSL hosts download and start a 22 MB Windows executable.
- That Rust loader decrypts and maps an embedded 64-bit Go payload.
- 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 theme | Observed malicious package names |
|---|---|
| Chalk | chalk-core, chalk-lib, chalk-util, chalk-es |
| Commander | comand, comander-cli, comanderjs, commandorjs, commandor-cli, commandor-core, comander-lib, commandor-lib, commander-lib |
| Lodash | loadashjs, lodash-lib, ladash-cli, lodahsjs, lodsh-cli, lodahs-cli, lodhash-cli |
| TypeScript | typescirpt-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 |
| React | raectjs |
| Axios | axois-http, axious-core |
| Initial linked sample | testingsmthb1g |
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:
stf2026Static 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/voteBecause 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:
| Environment | Observed behavior |
|---|---|
| Native Windows | Beacon, download main.exe, and start it hidden |
| WSL | Beacon, invoke Windows powershell.exe from Linux, and launch the same Windows payload |
| Ordinary Linux or macOS | Beacon 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.exeOn August 17, we fetched the asset as inert data, did not execute it, and independently verified:
| Property | Value |
|---|---|
| Size | 22,969,344 bytes |
| SHA-256 | 6f088ade49456db2422c3edfbb9998f4a3e9cce7c4c00a7279fb45d672a82b7d |
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:
1afff50ca4064310d3492c652e1c3168216dcb42063e0b26c223038db46b8731Recovered 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
tdatacollection - 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
| Type | Indicator | Role |
|---|---|---|
| IP and port | 193[.]70[.]34[.]101:20099 | Install-time telemetry |
| URL path | hxxp://193[.]70[.]34[.]101:20099/vote | Platform beacon |
| GitHub asset | github[.]com/bebraz1/qPzM50V1AKG0rVlH/releases/download/null/main.exe | Windows loader |
| Service | gofile[.]io | Exfiltration service used by the payload |
| Service | api[.]ipify[.]org | Public-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
| Artifact | SHA-256 |
|---|---|
Rust loader (main.exe) | 6f088ade49456db2422c3edfbb9998f4a3e9cce7c4c00a7279fb45d672a82b7d |
| Embedded Go PE | 1afff50ca4064310d3492c652e1c3168216dcb42063e0b26c223038db46b8731 |
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 nodeor 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
POSTrequests to193[.]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.
- Isolate the affected host and preserve relevant process, network, npm, PowerShell, and WSL telemetry.
- Block the fixed IP and port, and block the exact GitHub asset path without broadly blocking GitHub.
- Hunt for both payload hashes across endpoints and artifact stores.
- Invalidate browser sessions and rotate credentials accessible from the affected user context.
- Treat stored browser passwords, payment data, wallet secrets, seed phrases, and Telegram session material as potentially exposed.
- Remove malicious versions from lockfiles and caches, then rebuild from a known-clean environment.
- 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.
