GlassWorm Hijacks Popular React Native Packages — 130K Monthly Downloads Compromised

GlassWorm Hijacks Popular React Native Packages — 130K Monthly Downloads Compromised

On March 16, 2026, two legitimate NPM packages were compromised, and in both cases the package.json file was modified and a new pre-install script parameter added. This install script started an install.js script that hadn't been there in earlier versions:

This install.js script was a heavily obfuscated JavaScript loader. After researching the new versions we realized pretty quickly this was a known threat actor.

The GlassWorm threat actor hijacked the two popular React Native packages — react-native-international-phone-number and react-native-country-select — by compromising the maintainer's npm publishing token. The malicious releases were pushed directly to npm with no corresponding GitHub commits, deploying a multi-stage infostealer that uses Solana blockchain transactions as a dead-drop C2 resolver. The attack targets cryptocurrency wallets, npm and GitHub credentials, and browser profile data.

react-native-international-phone-number - Version 0.11.8

This package is a React Native library that helps developers automatically adjust and validate international phone numbers, and is downloaded more than 92,000 times a month.

react-native-country-select - Version 0.3.91

This package is a React Native library that helps developers build mobile apps that understand international calling standards, and is downloaded more than 42,000 times a month.

Compromised packages

Both packages are maintained by @AstrOOnauta. The malicious versions were published on March 16, 2026 at 10:49 and 10:54 UTC respectively — five minutes apart — with no corresponding GitHub releases. The gitHead field in both packages matched the previous clean version, confirming the attacker bypassed the normal development workflow entirely via a stolen npm publishing token.

Importantly, react-native-international-phone-number@0.11.8 depends on react-native-country-select@0.3.9 (the clean version), not the malicious 0.3.91. This confirms both packages were independently compromised rather than cascading through dependencies.

Attack Chain Overview ✓ Verified

1. Attacker publishes malicious versions using stolen npm token 2. npm install triggers preinstall hook: "node install.js" 3. install.js checks for Russian locale/timezone — exits if match (geofencing) 4. Queries Solana wallet 6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ 5. Decodes base64 payload URL from blockchain transaction memo 6. Downloads AES-encrypted stage-2 from 45.32.150.251 or 70.34.242.255 7. Decrypts payload using secretkey + ivbase64 from HTTP headers 8. Executes stage-2 in-memory via eval() or vm.Script — no disk writes 9. Stage-3 downloads Node.js runtime, establishes persistence 10. Kills browsers, harvests 9+ crypto wallets + npm/GitHub credentials 11. Exfiltrates archives to 217.69.3.152/wall

Stage 1: The Loader (install.js)

The malicious install.js file (~19KB, heavily obfuscated) was added to both packages with a new preinstall hook in package.json:

Shared loader hash (SHA-256): 59221aa9623d86c930357dba7e3f54138c7ccbd0daa9c483d766cd8ce1b6ad26

The loader uses RC4-based string encryption with array rotation (javascript-obfuscator toolchain) to hide its functionality.

Russia Geofencing

Before executing any payload, the loader checks whether the system belongs to a Russian-speaking user:

  • Environment variables: LANG, LANGUAGE, LC_ALL
  • System username
  • Timezone: Europe/Moscow, Asia/Krasnoyarsk, Asia/Vladivostok, MSK, and others
  • If any check matches, the malware silently exits. This is a well-known pattern in Eastern European and Russian-linked cybercrime operations to avoid targeting domestic users and law enforcement attention.

    Solana Blockchain C2 Resolution

    Rather than hardcoding a C2 domain that could be taken down, GlassWorm uses the Solana blockchain as a censorship-resistant dead-drop resolver:

  • The loader monitors Solana wallet: 6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ
  • It polls nine Solana RPC endpoints as fallbacks (including socket.network, getblock.io, publicnode.com)
  • It reads transactions from the Solana Memo Program via getSignaturesForAddress
  • Transaction memos contain base64-encoded JSON: {"link":"base64_url_here"}
  • Decoded URLs point to rotating payload servers
  • Verified blockchain data (queried March 17, 2026):

  • 5 active payload URLs found in transaction memos
  • Primary C2: 45.32.150.251 (4 different payload paths)
  • Secondary C2: 70.34.242.255 (1 payload path)
  • This technique allows the attacker to rotate payload servers at any time by simply posting a new Solana memo transaction — without modifying any malware code already deployed in the wild. Taking down one payload server is ineffective as the attacker can redirect to a new one within seconds.

    Solana RPC endpoints

    All 9 Solana RPC Endpoints (Complete List):

  • https://api.mainnet-beta.solana.com (Official Solana)
  • https://solana-mainnet.gateway.tatum.io (Tatum)
  • https://go.getblock[.]us/86aac42ad4484f3c813079afc201451c (GetBlock)
  • https://solana-rpc.publicnode[.]com (PublicNode)
  • https://api.blockeden[.]xyz/solana/KeCh6p22EX5AeRHxMSmc ← .xyz domain
  • https://solana.drpc.org (dRPC)
  • https://solana.leorpc[.]com/?api_key=FREE (LeoRPC)
  • https://solana.api.onfinality.io/public (OnFinality)
  • https://solana.api.pocket.network/ (Pocket Network)
  • Persistence Lock

    The loader writes ~/init.json with a timestamp to prevent re-execution within a 48-hour window.

    Stage 2: Encrypted Payload Delivery

    The loader sends an HTTP GET request to the payload URL resolved from Solana, with the operating system type sent in the os header (e.g., os: linux, os: win32, os: darwin).

    Example verified payload URLs from blockchain:

  • http://45.32.150[.]251/3e4Tg8V%2F8aCmOJKipASADg%3D%3D
  • http://45.32.150[.]251/IZeqdX38aTS6j3EvNTaC8g%3D%3D
  • http://70.34.242[.]255/htghOzp3JHTwH%2FyM96%2BgUA%3D%3D
  • The response contains a base64-encoded, AES-encrypted stage-2 payload. The decryption key and IV are delivered via HTTP response headers:

    Header Example Value

    secretkey szfNmayz6fgt6ojbAuVhjEAOWMMxw7iS

    ivbase64 ZMM7q5jBwUbsYFo7/8ZdxA==

    The decrypted payload is executed entirely in memory:

  • macOS/Linux: eval() execution
  • Windows: Node.js vm.Script sandbox
  • No stage-2 artifacts are written to disk, making forensic recovery difficult.

    Stage 3: The Stealer (Windows-focused)

    The stage-3 payload is a comprehensive Windows-focused infostealer.

    Persistence

  • Creates scheduled tasks via schtasks
  • Adds Windows Registry Run key for startup persistence
  • Downloads Node.js runtimes (v22.9.0, both x86 and x64) to %APPDATA%\\\\_node_x86 and %APPDATA%\\\\_node_x64
  • Cryptocurrency Wallet Theft

    The stealer targets 9+ cryptocurrency wallets via browser extension storage paths:

    Browser Extension Wallets:

  • MetaMask (Ethereum & EVM chains)
  • Trust Wallet (multi-chain)
  • OKX Wallet
  • Braavos (StarkNet)
  • Desktop Wallet Applications:

  • Exodus (multi-currency)
  • Atomic Wallet
  • Guarda
  • Coinomi
  • Daedalus (Cardano)
  • The malware kills browser processes (taskkill /F /IM chrome.exe, /IM firefox.exe) to unlock wallet extension files, then harvests:

  • Wallet seed phrases (if stored in extension)
  • Private keys
  • Extension storage databases
  • Transaction history and metadata
  • Developer Credential Theft

    Harvests developer credentials that could enable further supply chain attacks:

    npm config get # npm registry credentials git credential fill # GitHub/git credentials

    Browser Data Exfiltration

  • Terminates Chrome and Firefox processes
  • Harvests browser profile data (cookies, saved passwords, extensions)
  • Exfiltration Endpoints

    Endpoint Purpose

    45.32.150.251 /get_arhive_npm/. Archive exfiltration

    217.69.3.152 /wall Wallet data exfiltration

    Discovery & Response Timeline

    March 13, 2026 Previous clean versions published (0.11.7 and 0.3.9) March 16, 10:49:29 UTC react-native-international-phone-number@0.11.8 published (malicious) March 16, 10:54:18 UTC react-native-country-select@0.3.91 published (malicious) March 16, 11:49 UTC Security issue #165 filed on phone-number repo March 16, 11:55 UTC Security issue #11 filed on country-select repo March 16, 13:15 UTC Maintainer @AstrOOnauta deprecates both compromised versions

    Total time from publication to detection: ~1 hour Total time from detectio/n to deprecation: ~1.5 hours

    The malicious releases were flagged based on:

  • New versions published without corresponding GitHub releases
  • Addition of preinstall scripts absent in prior versions
  • Heavily obfuscated code in install.js
  • Version numbering inconsistencies (0.3.91 vs expected 0.3.10)
  • Indicators of Compromise

    Malicious Packages

    react-native-international-phone-number@0.11.8 react-native-country-select@0.3.91

    File Hashes

    install.js SHA-256: 59221aa9623d86c930357dba7e3f54138c7ccbd0daa9c483d766cd8ce1b6ad26

    Network IOCs ✓ Verified

    Primary C2: 45.32.150.251 (Vultr AS20473) Secondary C2: 70.34.242.255 Exfil endpoint: 217.69.3.152 /wall Archive exfil: 45.32.150.251 /get_arhive_npm/ Example payload URLs (from blockchain): - <http://45.32.150[.]251/3e4Tg8V%2F8aCmOJKipASADg%3D%3D> - <http://45.32.150[.]251/IZeqdX38aTS6j3EvNTaC8g%3D%3D> - <http://45.32.150[.]251/8gBwxEq4hvau8hrteHQsEQ%3D%3D> - <http://45.32.150[.]251/R5eXSjHiL85Bb%2B3nKEhl2w%3D%3D> - <http://70.34.242[.]255/htghOzp3JHTwH%2FyM96%2BgUA%3D%3D>

    Blockchain IOCs ✓ Verified

    Solana wallet: 6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ RPC method: getSignaturesForAddress Transaction count: 5 memos with payload URLs (as of March 17, 2026) Google Calendar: calendar.app.google/2NkrcKKj4T6Dn4uK6

    Related Domains

    socket[.]network n[.]xyz p[.]link

    Filesystem Artifacts

    ~/init.json (persistence lock with timestamp) %APPDATA%\\\\_node_x86\\\\ (downloaded Node.js runtime) %APPDATA%\\\\_node_x64\\\\ (downloaded Node.js runtime)

    Build Environment Fingerprint

    Node.js 22.22.0 / npm 10.9.4

    Remediation

    If you installed either compromised package version:

  • Check your installed version immediately:
  • npm ls react-native-international-phone-number npm ls react-native-country-select

  • If you have the malicious version, assume compromise:
  • Rotate all npm tokens and GitHub credentials
  • Check for ~/init.json on your system
  • Check for %APPDATA%\\\\_node_x86 and %APPDATA%\\\\_node_x64 directories (Windows)
  • Review scheduled tasks and Registry Run keys for unauthorized entries
  • Check cryptocurrency wallet extensions for unauthorized transactions
  • Review browser stored passwords and consider them compromised
  • Update to clean versions:
  • npm install react-native-international-phone-number@0.11.7 npm install react-native-country-select@0.3.9

  • Lock your dependency versions using package-lock.json and consider using npm audit as part of your CI/CD pipeline.
  • Conclusion

    The GlassWorm React Native attack is a textbook example of why npm token security is critical. A single stolen publishing token gave the attacker the ability to push malicious code to 130,000+ developers per month — with no code review, no PR, and no GitHub trail. The attack's use of Solana blockchain for C2 resolution makes traditional domain takedowns ineffective, and the in-memory execution of encrypted payloads makes forensic detection difficult.

    The rapid compromise of these two NPM packages by the GlassWorm threat actor shows they are an active and adaptable operator willing to target multiple ecosystems in rapid succession. Developers should enable npm two-factor authentication, use --ignore-scripts where possible, and monitor for unexpected version publications in their dependency tree.

    Verification Methodology

    All network IOCs and blockchain data in this post were independently verified on March 17, 2026 by:

  • Deobfuscating install.js using the malware's own RC4 decryption functions
  • Querying Solana mainnet at https://api.mainnet-beta.solana.com using the getSignaturesForAddress RPC method
  • Extracting 5 transaction memos containing base64-encoded payload URLs
  • Decoding URLs to confirm C2 infrastructure at 45.32.150.251 and 70.34.242.255
  • The C2 servers were offline at the time of verification (likely taken down after public disclosure), but the Solana blockchain remains an immutable record of the attack infrastructure.

    How can Safety help protect you from these attacks?

    Traditional vulnerability scanning happens too late - after potentially malicious code is already in your system. Which means that ASPM and EDR solutions don't protect you from this type of threat.

    But all is not lost, as the Safety Firewall protects develoeprs and CI pipelines proactively. Every package installation request is analyzed before reaching public repositories. Malicious, vulnerable, and policy-violating packages are automatically blocked before they can enter your systems, preventing rather than just detecting threats.

    You can sign up for a free Safety account and try the Safety Firewall HERE.

    Feel free to reach out to me with any questions!

    Read the full article