ua-parser-js npm Account Compromise (2021)
An attacker took over the maintainer account of ua-parser-js — a package with ~7M weekly downloads — and shipped versions containing a credential stealer (Windows) and a cryptominer (Linux).
Summary
On October 22, 2021, attackers gained publish access to the npm account of Faisal Salman, the maintainer of ua-parser-js, and shipped three malicious versions — 0.7.29, 0.8.0, and 1.0.0. The package had roughly 7 million weekly downloads and is a transitive dependency in Facebook, Microsoft, Amazon, IBM, and many other large codebases. The malicious versions executed a platform-aware payload: Linux installs got an XMRig cryptominer, Windows installs got the same miner plus a credential-stealing trojan that harvested browser-stored passwords, cryptocurrency wallets, and operating-system credentials. CISA issued an alert the same day urging immediate inspection and rotation.
Timeline
- 2021-10-22 ~12:15 UTC — Malicious
ua-parser-js@0.7.29published. - 2021-10-22 ~12:39 UTC —
1.0.0published. - 2021-10-22 ~14:25 UTC —
0.8.0published. - 2021-10-22 ~17:00 UTC — Maintainer Faisal Salman opens GitHub issue #536 confirming his npm account was compromised and the malicious versions were not his.
- 2021-10-22 ~18:00 UTC — npm removes all three versions; clean replacements published.
- 2021-10-22 evening — CISA publishes alert recommending all installs of the affected versions be considered fully compromised.
Attack Vector
The npm account was hijacked. The maintainer described the takeover as the result of a credential compromise outside of GitHub (no specific vector was confirmed publicly). Once the attacker had publish access, the technical chain was:
- Three escalating-severity versions published in rapid succession to maximize the reach window before detection.
- A
preinstallscript in the package ran on every install, executing a platform-specific payload. - On Linux: a shell script downloaded and executed an XMRig cryptominer binary (
jsextension). - On Windows: the script downloaded both XMRig and a
create.dllfile identified by multiple AV vendors as a variant of the DanaBot banking trojan / credential stealer. - DanaBot then performed standard credential harvesting: browser password stores, browser cookie jars, FTP credentials, cryptocurrency wallet files, and Windows credential vault contents.
Tokens & Credentials Exposed
The Windows payload's harvest scope is what makes this incident a credential incident, not just a cryptominer incident:
- Browser-stored credentials — Chrome, Edge, Firefox password stores. For developers, this typically includes session tokens for GitHub, npm, AWS console, and SaaS dashboards.
- Browser cookies — full session cookie databases, allowing post-2FA session resumption attacks.
- FTP credentials stored in popular FTP clients (FileZilla, etc.).
- Cryptocurrency wallet files —
wallet.dat, MetaMask local storage, hardware wallet companion app data. - Operating system credentials — Windows Credential Manager contents, including any cached domain credentials.
For an organization whose developer workstation pulled the malicious version, the practical exposure was every credential the developer's browser had ever logged into and every credential their OS had ever cached.
Confirmed Impact
- CISA emergency alert issued same-day, treating any install of affected versions as fully compromised.
- Multiple Fortune-500 incident response activations in the days following, focused on developer workstation forensics and credential rotation.
- DanaBot infrastructure correlation: the C2 endpoints used by the malicious payload were tied to existing DanaBot operations, suggesting a credential-stealing-as-a-service model rather than a one-off attack.
- No specific number of compromised organizations was published; given the package's ~7M weekly download volume and ~6-hour exposure window, the absolute count is presumed large.
Mitigation & Lessons
If you operated a development workstation or CI runner that installed ua-parser-js@0.7.29, 0.8.0, or 1.0.0:
- Treat the host as fully compromised. Reformat developer workstations; rebuild CI runners from clean images.
- Rotate every credential the host could plausibly have touched. Browser-stored credentials, cloud provider keys, source control tokens, OS credentials — assume the entire credential graph reachable from the host is exposed.
- Audit for persistence. DanaBot maintains persistence via scheduled tasks and registry run keys. A clean reformat is the only reliable removal.
The structural lessons mirror the 2018 ESLint incident:
preinstallhooks remain a sharp edge in the npm ecosystem. They run before any code review opportunity.- Account compromise via credential reuse continues to be the dominant npm account takeover vector. 2FA enforcement for top packages is the single most effective control.
- Lockfile pinning + provenance + reproducible builds is the modern multi-layer defense. Lockfile pinning alone would have rejected the malicious version unless the project explicitly ran
npm install(re-resolution) during the window.
Cremit Analysis
This incident is ghost-key, drifted-key, and unattributed-key all at once.
Ghost Key: the npm account credential — held by a single maintainer for years — was the only identity gating publish rights to a package consumed by hundreds of millions of dependent installs. There is no second face on the credential to ping when something looks off.
Drifted Key: ua-parser-js was originally a small utility for parsing browser User-Agent strings. It had drifted into production dependency trees of every major cloud provider and OS vendor by 2021. The trust model and security posture appropriate for a small utility (one maintainer, no review) was wildly inappropriate for the blast radius the package had accumulated.
Unattributed Key: every credential the malicious payload exfiltrated from a developer workstation was, in most organizations, not mapped to a specific workload, lifecycle policy, or rotation owner. After the incident, organizations that took it seriously spent days reconstructing what their developer machines actually held. Many credentials likely never got rotated because nobody knew they existed.
The NHI Severity Index score of 8.8 reflects ecosystem-wide blast radius (one of the most-installed packages in the registry), production reachability (any credentials the affected workstation touched), and variable privilege levels.
The incident also established the platform-aware preinstall pattern that the Shai-Hulud campaign of 2025–2026 would refine: detect the host environment, deliver a payload calibrated for the maximum credential yield available on that host. The Bitwarden CLI compromise targeting CI runners with cloud tokens, AI tooling configs, and SSH keys is the direct descendent of the technique introduced here.
References
- [1]ua-parser-js GitHub issue #536 — original disclosureprimary·2021-10-22·github.com
- [2]GitHub Security Advisory GHSA-pjwm-rvh2-c87wprimary·2021-10-22·github.com
- [3]CISA — Malware Discovered in Popular NPM Package, ua-parser-jsprimary·2021-10-22·cisa.gov
- [4]Popular NPM library hijacked to install password-stealers, minersreporting·2021-10-22·bleepingcomputer.com
