1 of 39

npm should-i-install Malicious npm packages: Anatomy, Detection gaps and AI's role

DIPTENDU KAR, DERIAN STENGLEIN

2 of 39

whoarewe

3 of 39

Agenda

  • Brief history of npm malwares.�
  • Why NPM.�
  • Attack vectors: How attackers get in.�
  • The Payload tricks: How malwares are delivered.�
  • Can AI help in malware detection and deny-by-default.�
  • Possible Solutions.

4 of 39

The Growing Problem

  • 2023
    • > 5000 malicious packages taken down by npm
    • February saw >15,000 spam packages published to npm across two days
    • Npm statistics showed >2.5 million live packages and >184 billion downloads/month by the end of 2023
  • 2024
    • > 512,000 malicious packages out of estimated 4.8 million packages
    • Notable attacks:  LUMMA malware (PyPi), XZ Utils package backdoor

https://socket.dev/blog/2023-npm-retrospective

https://www.sonatype.com/state-of-the-software-supply-chain/introduction

5 of 39

Aug 24 – Sep 24

  • NX s1ngularity attack – August 26, 2025
    • More than 2000 secrets leaked.
    • 16 million+ weekly downloads (combined 8 packages)

  • Chalk/Debug compromise – September 8, 2025
    • 2 billion weekly downloads (combined 18 packages)
  • DuckDB compromise – September 9, 2025
    • 386K+ weekly downloads (combined 4 packages), continuation of above.
  • @ctrl/tinycolor compromised: Shai-Hulud worm – September  15, 2025
    • Total 180+ packages, @ctrl/tinycolor 2.2M+ weekly downloads
    • First self-replicating npm worm

6 of 39

Aug 24 – Sep 24

  • Talk submitted to BSides CT – August 24, 2025
  • NX s1ngularity attack – August 26, 2025
    • More than 2000 secrets leaked.
    • 16 million+ weekly downloads (combined 8 packages)
  • Talk tentatively accepted – September 5, 2025
  • Chalk/Debug compromise – September 8, 2025
    • 2 billion weekly downloads (combined 18 packages)
  • DuckDB compromise – September 9, 2025
    • 386K+ weekly downloads (combined 4 packages), continuation of above.
  • @ctrl/tinycolor compromised: Shai-Hulud worm – September  15, 2025
    • Total 180+ packages, @ctrl/tinycolor 2.2M+ weekly downloads
    • First self-replicating npm worm

This is just a coincidence – I promise I did not do all this just to get this talk accepted.

7 of 39

Why npm?

  • Unlimited packages
    • JS does not have a very good standard library large amounts of widely used “single function” libraries show up
    • Consequently, npm has a large dependency graph
    • 10 direct deps -> 683 transitive deps
  • Very easy to publish to npm, no manual review, no sandbox, no questions asked
  • npm runs everywhere - frontend, backend, CI/CD, AI CLI, WEB3, even desktop apps → credentials galore

https://github.blog/security/supply-chain-security/best-practices-to-keep-your-projects-secure-on-github/

8 of 39

is-windows

9 of 39

Attack Vectors: How attackers get in

  • Randomly named malicious package
  • Typosquatting
  • Dependency confusion
  • Maintainer account takeover

10 of 39

Random named malicious package

  • Users would not normally download
  • Stats like stars, homepage, dependencies, etc. are low suggesting very unpopular package
  • Every new package starts like this, grows overtime
  • Or, someone may inflate the number of packages to earn reputation and earn tea rewards

11 of 39

Typosquatting

12 of 39

Typosquatting

  • Lodash - legitimate�
    • lodah
    • lodahs
    • oldash
    • lodahs
    • lodqsh
    • lodashh
    • lodassh
    • lodarshe
    • lodash.js

13 of 39

14 of 39

15 of 39

The bright side

  • npm community reports malware – all examples of typos have been replaced with security holdings so you cannot download malicious packages
  • still don’t recommend making typos or downloading malware (copy-pasting helps)

16 of 39

Dependency confusion

  • https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
  • The attacker uses specific package names of internal packages of a target and publishes a malicious package on an external public repository with the exact name
  • The attacker then assigns a very high version number to this published package
  • Most default package managers prefer downloading the external malicious package because of its high version number rather than downloading a low version from the legitimate internal repository
  • Impact is likely limited to singular organizations

17 of 39

Dependency confusion

18 of 39

Maintainer account takeover

  • Compromise legitimate maintainer accounts (social-engineering mainly)
  • Release a new version with a malware
  • Inherits all stats, downloads, dependencies and hence TRUST
  • Chalk/Debug (phishing), NX (GitHub workflow injection), eslint-config-prettier (July 18, 2025)
  • Highest impact

19 of 39

Phishing mail - chalk/debug

20 of 39

Maintainer account takeover

21 of 39

The Payload Tricks: How malware gets delivered

  • Install-Time Scripts
    • Preinstall/postinstall hooks execute automatically
    • Can run code packaged within the library or download external payloads and execute them
  • Directly via index.js / the start file as specified by "main" in package.json
  • Payload types / Defense evasion strategies
    • Readable malicious code -> No defense evasion
    • Minified code
    • Obfuscated code
    • Bundled binary and execute the binary -> possibly max defense evasion

22 of 39

Readable malicious code - nx

23 of 39

Readable malicious code - nx

  • Collects sensitive info
    • Environment variables (process.env), Hostname and OS details, Platform information
    • Cryptocurrency Wallets like MetaMask keystores, Electrum wallets, Ledger and Trezor data Exodus, Phantom, and Solflare wallets Generic keystore files (UTC--*, keystore.json, *.key)
    • Development Credentials like GitHub authentication tokens via gh auth token, npm registry tokens from ~/.npmrc, SSH private keys (id_rsa), Environment files (.env)
  • Exfiltration
    • Creates a public GitHub repository named s1ngularity-repository using stolen GitHub tokens.
    • Triple-base64 encodes all collected data, uploads the encoded data to results.b64 in the newly created repository
    • Makes the repository publicly accessible, exposing sensitive data
  • AI CLI Exploitation
    • Checks for gemini, claude, q and forces the AI tools to recursively scan the filesystem and write discovered sensitive file paths to /tmp/inventory.txt

https://www.stepsecurity.io/blog/supply-chain-security-alert-popular-nx-build-system-package-compromised-with-data-stealing-malware

24 of 39

Minified code - @ctrl/tinycolor (worm)

25 of 39

Minified code - @ctrl/tinycolor (worm)

  • Collects sensitive info
    • Downloads and executes TruffleHog, a legitimate secret scanner.
    • GitHub personal access tokens, AWS access keys (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), Google Cloud Platform service credentials, Azure credentials, Cloud metadata endpoints, NPM authentication tokens
  • Persistence
    • Injecting a GitHub Actions workflow file (.github/workflows/shai-hulud-workflow.yml) via a base64-encoded bash script.
  • Exfiltration
    • This workflow triggers on push events and exfiltrates to a hardcoded webhook.
  • Worm
    • NPM registry API to fetch up to 20 packages owned by the maintainer, then force-publishes patches to these packages adding the same bundle.js

https://www.stepsecurity.io/blog/ctrl-tinycolor-and-40-npm-packages-compromised

26 of 39

Obfuscated code - debug

No pre install or post install, obfuscated code in index.js

27 of 39

Obfuscated code - debug

28 of 39

Obfuscated code - debug

  • Cryptocurrency stealer.
  • Silently embeds a browser-side interceptor into frontend bundles.
  • Activates when a user visits website.
  • Hooks core functions like fetch, XMLHttpRequest, and wallet APIs (window.ethereum, Solana, etc.).
  •  Scans responses and transaction payloads for blockchain addresses (ETH, BTC, SOL, TRX, LTC, BCH).
  • Hijacks transactions before they’re signed. Alters transaction parameters (e.g., recipients, approvals, allowances). Even if the UI looks correct, the signed transaction routes funds to the attacker. Code contains hardcoded attacker's wallet address.
  • Around $50 worth of cryptocurrency stolen.

29 of 39

Bundled Binary – napi-postinstall

30 of 39

Bundled Binary – napi-postinstall

Automation gone wrong

31 of 39

Window of Opportunity/Exposure

NPM Package Published

Hour N

NPM Package Retracted

Hour N+X

Security Advisory Published

Hour N+X+Y

  • Possible window of exposure
  • Vendors and independent researcher analysis starts
  • Dependabot or similar
  • No exposure
  • Vendors and independent researcher analysis completed
  • Dependabot or similar - don’t affect
  • Residual risk (for affected)
  • No exposure
  • Everyone has data now. OSV, GHSA, Blogposts, etc.
  • Dependabot or similar - nope

EXPOSURE RISK

32 of 39

Role of AI and deny-by-default

33 of 39

Readable malicious code - nx

  • Can AI help – Yes with high degree of accuracy.
  • Easiest just send the content.
  • Deny-by-default – N/A, we already know what the code does.

34 of 39

Minified Code - worm

  • Can AI help – maybe but with less accuracy than readable
  • In this case – claude said this is a legit minified code.
  • Deny-by-default – No, legitimate packages contain minified code as well.

35 of 39

Obfuscated Code - debug

  • Can AI help – maybe
  • In this case – claude correctly identified from search patterns.
  • Deny-by-default – Yes, Legit packages DON’T usually contain obfuscated code.

36 of 39

Binary – napi-postinstall

  • Can AI help? maybe. Test case not run. Static analysis on binaries.
    • Reverse engineering, decompilation, work with symbols
    • Depends on binary type, elf, dll, exe etc.
    • Symbols stripped or not.
    • Binaries can be obfuscated as well.
    • Not as simple as – tell me what this does.
  • Deny-by-default: No. Legit packages like @anthropic-ai/claude-code uses binary to install.

37 of 39

Potential issues with full AI analysis

  • Size of file greater than context window
    • Readable code can be segregated by file or even by each function.
  • False positives & negatives. Non-deterministic.
  • Adversarial code specifically designed to avoid static analysis.
  • Bypass using prompt injection techniques.
  • Human-in-the-loop is possible and may be necessary but that requires more time (window).
  •  https://checkmarx.com/zero-post/bypassing-claude-code-how-easy-is-it-to-trick-an-ai-security-reviewer/

38 of 39

Possible solutions

  • Use tools provided by vendors like Socket, Aikido, Checkmarx, etc.
    • May be appropriate for orgs, but for developers (individual – small sized)?
  • Install any package in a sandbox first.
    • Time and resource and expertise.
  • Avoid FOMO of latest and greatest version.
    • Pin package versions to avoid auto updates.
    • Use “--ignore-script” or similar.
    • Short lived, granular token. (already enforced by npm now).
    • Do not merge dependabot PR asap. Hold up … wait a minute…or couple hours
  • Let NPM run these detection tools on their end before publishing a package (personal opinion).

39 of 39

Derian Diptendu

QUESTIONS?

THANK YOU

LinkedIn

LinkedIn