Analysis
Two popular React Native npm packages were compromised by a threat actor tracked as Glassworm, introducing a five-stage credential-harvesting attack chain that executes during npm install. The compromised packages — react-native-country-select (version 0.3.91) and react-native-international-phone-number (version 0.11.8) — together account for approximately 30,000 weekly downloads. Both have since been patched (0.4.0 and 0.11.9 respectively).
The analytically significant feature of this campaign is its command-and-control architecture. Rather than hardcoding a C2 domain or IP address, Glassworm resolves payload URLs from the Solana blockchain at runtime. An attacker-controlled wallet on the Solana Memo Program serves as a decentralized URL pointer: the malware reads base64-encoded payload addresses posted to that wallet and connects to the current payload server. This architecture means the C2 cannot be neutralized by domain takedown — the only way to disrupt payload delivery is to seize control of the Solana wallet, which is not a standard incident response action. This places Glassworm's infrastructure at C2 resilience tier 3, equivalent to the ICP canister architecture used by CanisterWorm.
Infrastructure overlap with the ForceMemo campaign (March 8–14, 2026) — identical Solana wallet, matching geo-filter logic, and the same AES encryption pattern — establishes Glassworm as a multi-campaign cluster, not a one-off incident. This finding documents the React Native arm of that cluster.
What we know
- Packages affected:
react-native-country-selectversion 0.3.91 (malicious); 0.4.0 is clean.react-native-international-phone-numberversion 0.11.8 (malicious); 0.11.9 is clean. - Combined reach: Approximately 30,000 weekly downloads — meaning a significant fraction of the React Native mobile development ecosystem was exposed.
- Discovery: First reported by StepSecurity. Datadog Security Research confirmed the activity and corroborated the ForceMemo infrastructure overlap.
- Geo-exclusion: The malware explicitly stops execution on systems with Russian locale settings (
LANG,LANGUAGE,LC_ALL) or Russian timezones (Europe/Moscow,Asia/Krasnoyarsk,Asia/Vladivostok,MSK). This pattern is consistent with Russian-origin malware avoiding domestic prosecution — a standard operational constraint for financially motivated actors operating from Russia.
Five-stage attack chain
The following stage analysis is sourced from StepSecurity's primary report, as characterized by Datadog Security Research. Primary-source verification of payload artifacts and exact IOCs is pending (Task #14).
Stage 1 — Preinstall execution. The compromised versions add a preinstall script to package.json that runs install.js automatically during npm install, before any user code executes. The script uses RC4-based string encryption and array rotation obfuscation characteristic of the javascript-obfuscator toolchain — the same obfuscation framework used in other npm supply-chain campaigns. At this stage, execution is guaranteed for any developer or CI/CD system that runs npm install with either affected package in the dependency tree.
Stage 2 — Geo-filtering. After a 10-second delay (a timing technique that can evade some sandboxes with short execution windows), the malware checks the environment for Russian locale and timezone markers. Russian locale detection halts execution entirely. The 10-second delay before the check is operationally meaningful: it allows the process to appear legitimate during automated analysis before taking any detectable action.
Stage 3 — Solana blockchain C2 resolution. The malware queries nine Solana RPC endpoints for the wallet address 6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ using the getSignaturesForAddress method. It reads base64-encoded payload URLs posted via the Solana Memo Program to that wallet. This resolves to the current payload server address.
This is the critical capability distinction. A conventional C2 domain can be taken down, sinkholes, or blocked at DNS. A Solana wallet cannot. The attacker can post a new payload URL to the wallet at any time — rotating the actual server behind the same permanent blockchain anchor. From a defender's perspective, there is no domain to block at Stage 3. Detection must occur at Stage 1 (preinstall hook) or Stage 4 (outbound connection to the resolved server).
Stage 4 — Payload delivery. The malware connects to the resolved server (45.32.150[.]251) with platform identification sent via an os HTTP header. The server delivers a base64-encoded, AES-256-encrypted payload. The AES key and IV are provided in the HTTP response headers — the decryption material travels with the payload, meaning the encrypted payload on disk is not a stable IOC.
Stage 5 — In-memory execution with persistence gate. The decrypted payload executes entirely in memory without writing to disk. On macOS and Linux, it uses eval(); on other platforms, Node.js vm.Script. A persistence lock file at ~/init.json prevents re-execution within 48-hour windows — indicating the operator does not want duplicate collection runs from the same machine. The lock file is also a detection artifact: its presence on a developer machine confirms prior execution even if the npm package has since been updated.
Why React Native was targeted
React Native mobile development environments are an undermonitored credential surface. The ecosystem profile that makes them valuable targets:
- Cloud service credentials for mobile backends. React Native applications routinely connect to AWS, GCP, Firebase, and similar services. Developer machines and CI/CD pipelines hold the credentials for those backends. An
npm installin a React Native project executes with access to the full credential surface of the developer's machine. - Lower security baseline than backend ecosystems. The mobile development community has historically received less supply-chain security tooling than backend-focused npm users. Preinstall hook monitoring and SCA scanning penetration is lower in mobile-focused teams than in DevOps-focused teams.
- High-volume packages as propagation multipliers. 30,000 weekly downloads is approximately one order of magnitude below the largest npm supply-chain targets (axios: ~50M/week), but still represents meaningful scale for initial credential collection. The goal is not maximum blast radius — it is reaching the specific credential classes the operator wants.
ForceMemo cluster connection: four-step analysis
Observation: The Glassworm campaign uses Solana wallet 6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ as its C2 resolver. The ForceMemo campaign (March 8–14, 2026), which targeted GitHub Python repositories, used an identical Solana blockchain C2 architecture with matching Russia geo-filter logic and AES-256 encryption. This infrastructure overlap was noted by Datadog Security Research in characterizing StepSecurity's findings.
Mechanism: A specific Solana wallet address is an operator-controlled artifact: it requires possession of the corresponding private key to post new payload URLs. Two independent campaigns reusing the same wallet address is not a coincidence — the wallet is the persistent C2 anchor for the same operator's infrastructure. The geo-filter logic is a separate corroborating artifact: the same set of Russian locale and timezone strings appearing in two campaigns indicates shared codebase, not independently-written malware that happens to target the same region.
Inference: Glassworm and ForceMemo share the same operator infrastructure and almost certainly the same codebase. This is a multi-campaign cluster. The March 8–14 ForceMemo campaign targeted GitHub Python repositories; the Glassworm campaign targeted React Native npm packages. The same actor is running operations across multiple ecosystems using the same Solana-backed C2 anchor.
Confidence bound: This inference depends on the wallet address match being confirmed against StepSecurity's primary analysis for ForceMemo. If the ForceMemo wallet address differs and the overlap is at the AES/geo-filter pattern level only, the confidence drops to moderate — pattern overlap alone is insufficient for cluster attribution. Primary-source verification of the ForceMemo wallet address is the key falsification check (Task #14).
Indicators of compromise
Compromised package versions:
react-native-country-selectversion 0.3.91 — malicious; patch to 0.4.0react-native-international-phone-numberversion 0.11.8 — malicious; patch to 0.11.9
Filesystem artifacts:
~/init.json— persistence lock file; presence confirms prior payload execution even if the malicious package version has since been replacedinstall.jsin preinstall script position — RC4-obfuscated; any preinstall hook running a file with this name warrants inspection
Network indicators (defanged):
45.32.150[.]251— payload delivery server; outbound connection duringnpm installfrom a React Native package is not expected behavior- Solana RPC endpoint queries for
getSignaturesForAddressfrom wallet6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ— any npm install-time query to a Solana RPC endpoint is highly anomalous
Behavioral indicators:
- 10-second sleep during
npm installexecution — timing evasion technique; legitimately no npm package requires a 10-second install delay - Outbound connection carrying
osHTTP header with platform identification from a package installation context
Detection and mitigation
- Check for
~/init.jsonon all developer machines. This file's presence is a confirmed-execution artifact that persists beyond package update. If found, treat the machine as compromised and rotate all credentials. - Update affected packages immediately.
react-native-country-select→ 0.4.0;react-native-international-phone-number→ 0.11.9. The malicious code is absent from these versions. - Audit CI/CD logs for Solana RPC connections. No npm package should make outbound connections to Solana RPC endpoints during installation. This is a class-level detection rule that covers the entire Glassworm/ForceMemo C2 architecture: block or alert on any outbound connection to
api.mainnet-beta.solana.com,solana-api.projectserum.com, or other Solana RPC endpoints fromnpm installcontexts. - Monitor for
45.32.150[.]251in egress logs. This IP has no legitimate use in development contexts. - Enable preinstall hook auditing.
npm install --ignore-scriptsprevents preinstall hooks from running. For CI/CD pipelines that can tolerate this constraint, it is the structural defense against this class of attack. For development environments where scripts are necessary, configure egress monitoring on the install process. - Do not rely on domain-block lists for this campaign. The blockchain C2 resolver means the actual payload server IP can change without modifying the malicious package. Domain-block strategies that work for tier-1 C2 do not apply here.
Attribution
The Russia geo-filter is the strongest available attribution signal: Russian-origin financially motivated actors routinely exclude domestic targets to reduce their legal exposure. This does not confirm Russian origin — the filter could be adopted by non-Russian actors for misdirection — but the pattern is operationally consistent with Russian-speaking financially motivated threat actors.
The multi-ecosystem pattern (GitHub Python repositories in ForceMemo, npm React Native in Glassworm) is consistent with a technically capable operator testing the same C2 architecture across different surfaces. The credential scope targeted (cloud credentials, developer tokens) aligns with a credential-collection monetization model rather than ransomware or destructive operations.
LAPSUS$ attribution or state-actor attribution for this campaign is unsupported by available evidence.
Criminal-market signal
No dark-web presence for Glassworm or ForceMemo campaign tooling has been observed. The Russia geo-exclusion, the sophisticated Solana C2 architecture, and the multi-campaign operational pattern are all consistent with an operator-run credential harvesting operation — the operator uses the credentials directly, not via criminal-market sale. Detection must occur at the registry and installation layer. Dark-web monitoring for this campaign class is expected to return clean negatives (H2 operator-run pattern).