Blog

DPRK npm Malware Detection: Auditing npm for AI-Generated Backdoors

DPRK npm Malware Detection: Auditing npm for AI-Generated Backdoors

North Korea’s Famous Chollima threat group is using Anthropic’s Claude Opus to generate and obfuscate malicious npm packages — a campaign ReversingLabs has named PromptMink — creating a DPRK npm malware detection AI-generated code audit problem that npm audit cannot solve alone. Since September 2025, the group has published over 15 bait packages under Solana and DeFi branding, each silently pulling in a second-layer payload that steals .env secrets, SSH keys, and crypto wallet credentials. This guide walks JavaScript developers and AppSec teams through what AI-written malicious code looks like, how to inspect install hooks manually, and which CI/CD guardrails are effective against this and future DPRK campaigns.

The PromptMink Campaign: How Famous Chollima Is Weaponizing AI

On February 28, 2026, Anthropic’s Claude Opus co-authored a commit to an open-source crypto trading agent called openpaw-graveyard. The commit added @solana-launchpad/sdk as a dependency — a package that passed static scans cleanly. What it did not advertise was its transitive dependency: @validate-sdk/v2, the actual stealer payload. ReversingLabs researchers caught the pattern and published technical details in late April 2026, attributing the campaign to Famous Chollima (also known as Shifty Corsair), a North Korean state-aligned group also linked to the Graphalgo fake-recruiter campaign.

The two-layer structure is deliberate. First-layer “bait” packages (MITRE ATT&CK T1195.001) contain no malicious code and return clean results on static analysis. The malicious logic lives entirely in second-layer packages that only execute once installed as transitive dependencies. Confirmed bait packages include @meme-sdk/trade, @validate-ethereum-address/core, @pumpfun-ipfs/sdk, @hash-validator/v2, graph-dynamic, graphlib-js, and gemini-ai-checker. The payload-layer packages — @validate-sdk/v2, bjs-lint-builder, csec-crypto-utils, and jito-proper-excutor — are named to sound like routine cryptographic utilities.

The campaign’s primary targets are cryptocurrency developers, Solana ecosystem projects, and DeFi applications. Famous Chollima reaches developers through social engineering on LinkedIn, job boards, and GitHub: developers are invited to complete a “coding assessment,” handed a repository containing the bait package, and compromised before they write a single line of their own code.

What AI-Generated Malicious npm Code Looks Like

Identifying LLM-authored malicious code requires knowing what artifacts these models leave behind. ReversingLabs analysts flagged the following patterns across PromptMink packages:

Leftover LLM prompts in code comments. One package contained a comment asking whether the README should also be obfuscated — an unanswered meta-question from the generating model. Human authors do not leave self-directed questions in production code.

Verbose TypeScript declarations and excessive JSDoc. Malicious packages in this campaign ship with fully typed TypeScript interfaces and detailed JSDoc comments that would be unnecessary overhead for a genuine data validation utility. LLMs over-document by default.

Disproportionate package sizes. Between September 2025 and January 2026, payloads were compact obfuscated JavaScript files (~5.1 KB). By March 2026, the packages had grown to 85–100 MB Node.js Single Executable Applications (SEAs) embedding compiled Rust NAPI add-ons — a size profile completely inconsistent with a “validation library.”

Rust NAPI add-ons with no legitimate purpose. The March 2026 variants ship pre-compiled platform-specific .node binaries generated using NAPI-RS. Legitimate validation libraries have no reason to bundle compiled Rust binaries.

The evolution from JavaScript to SEA to Rust mirrors the sophistication ramp seen in other DPRK tooling: the Rust payloads add full project source tree exfiltration (T1560) and SSH key persistence on both Linux and Windows (T1098.004), capabilities absent in the earlier JS variants.

DPRK npm Malware Detection Challenges: Why Traditional Scanners Miss AI-Generated Code

npm audit matches packages against a CVE database — it has zero visibility into newly published malicious packages with no assigned CVE. Standard SAST tools that pattern-match on obfuscation techniques (hex strings, eval(), Function() constructors) will miss cleanly LLM-generated code that structures its data access the same way legitimate libraries do.

The C2 evasion is also deliberate. Google’s Threat Intelligence Group observed that the UNC1069-linked Axios supply chain attack crafted its C2 traffic to use the domain prefix packages.npm.org, causing SIEM alerts to blend into expected npm registry traffic. The PromptMink campaign uses Vercel-hosted infrastructure and render.com to further blend in with normal SaaS-heavy development traffic.

How to Inspect Install Hooks Manually

Every PromptMink payload triggers via the scripts field in package.json. Begin here before allowing any unfamiliar package into your codebase.

Step 1: Read the scripts block.


cat node_modules/@validate-sdk/v2/package.json | python3 -m json.tool | grep -A 10 '"scripts"'

Any preinstall, install, postinstall, or prepare entry that invokes an external file or spawns a shell process requires manual review.

Step 2: Read the hook payload directly.


cat node_modules/@validate-sdk/v2/loader.mjs

Patterns that indicate a malicious loader: base64 strings decoded at runtime, require('child_process').exec() or .spawn() calls, reads of process.env.AWS_* or GITHUB_TOKEN, filesystem walks targeting .env or .json files, and outbound HTTP to domains unrelated to the package’s stated purpose.

Step 3: Enumerate all packages with lifecycle hooks in your dependency tree.


cat package-lock.json | python3 -c "
import sys, json
lock = json.load(sys.stdin)
for pkg, meta in lock.get('packages', {}).items():
    scripts = meta.get('scripts', {})
    if scripts:
        print(pkg, list(scripts.keys()), scripts)
"

This surfaces every hook across your entire transitive tree, not just direct dependencies.

Step 4: Flag oversized native add-ons.


find node_modules -name "*.node" | while read f; do
  size=$(du -m "$f" | cut -f1)
  if [ "$size" -gt 10 ]; then
    echo "ANOMALY: $f — ${size}MB"
  fi
done

PromptMink Rust payloads are 85–100 MB. A .node binary above 10 MB in a utility package is a strong indicator of abuse.

Step-by-Step Audit: Detecting AI-Generated npm Malware in Your Dependencies

Use this checklist for any existing project or when vetting a new dependency:

  • Enumerate the full dependency tree and save it as a baseline:

npm ls --all 2>/dev/null > deps-baseline.txt

Diff against this file after every package update.

  • Run the lifecycle hook audit from Step 3 above on every package-lock.json change.
  • Grep for known PromptMink C2 infrastructure across all installed JavaScript:

grep -rE "(validator\.uno|ipfs-url-validator\.vercel\.app|winstonjs\.site|changelog\.rest|api\.mywalletsss\.store|csec-c2-server\.onrender\.com)" node_modules/ 2>/dev/null
  • Scan for credential-targeting patterns (T1005, T1083):

grep -rE "(\.env|process\.env|id_rsa|authorized_keys|GITHUB_TOKEN|AWS_SECRET_ACCESS_KEY)" \
  node_modules/ --include="*.js" --include="*.mjs" --include="*.cjs" 2>/dev/null \
  | grep -v "\.test\." | grep -v "\.spec\." | grep -v "node_modules/.bin"
  • Check the publish date of every new dependency against your install date:

npm view @validate-sdk/v2 time --json

A package published the same day you ran npm install for it is a red flag in a production codebase.

  • Cross-reference against Socket.dev, which flagged the Axios supply chain payload within six minutes of publication using LLM-powered static analysis. Install the CLI:

npm install -g @socketsecurity/cli
socket scan create --org your-org .
  • Use Package-Inferno for deep static analysis before pulling any unfamiliar package:

docker run --rm -v $(pwd)/node_modules/target-package:/pkg package-inferno /pkg

Automated Tools for Ongoing Detection

Manual inspection does not scale across hundreds of transitive dependencies. Integrate these tools at the pipeline level:

  • Socket.dev — real-time scanning of npm and PyPI packages with specific detection for AI-generated malicious code, install script abuse, and known malicious infrastructure. Its “AI-detected potential malware” alert category was built in direct response to LLM-authored campaigns like PromptMink.
  • Aikido Security — combines SBOM generation with behavioral analysis and continuous dependency monitoring.
  • Splunk’s npm-threat-emulation framework — adversary simulation for testing whether your SIEM correctly detects npm lifecycle hook execution across Windows and Linux.

CI/CD Pipeline Guardrails

Unit 42 documented that the Shai-Hulud 2.0 campaign deliberately shifted from postinstall to preinstall hooks to guarantee execution in CI/CD pipelines before any build-stage scanning occurs. Apply these hardening steps:

1. Block lifecycle scripts by default in CI.


npm install --ignore-scripts

Explicitly invoke required install scripts for known-good packages in your Makefile or pipeline step — do not allow open-ended script execution.

2. Enforce lockfile integrity.


npm ci

npm ci installs exactly what is in package-lock.json and fails if the lockfile diverges from package.json. Never use npm install in CI — it resolves version ranges and can silently pull newly published malicious versions.

3. Generate and diff SBOMs on every build.


npx @cyclonedx/cyclonedx-npm --output-file sbom.json

Fail the pipeline if the SBOM diff shows any new transitive dependency not present in the previous build’s SBOM.

4. Restrict network egress during npm install. Block all outbound connections in CI except registry.npmjs.org and any internal mirror. PromptMink payloads require network access to exfiltrate data — an install job with no external egress cannot phone home to validator.uno.

5. Alert on shell spawning from npm/node processes. In Splunk SPL:


index=endpoint sourcetype=sysmon EventCode=1
| where parent_process_name IN ("npm", "node", "npx")
  AND process_name IN ("curl", "wget", "bash", "sh", "powershell", "cmd", "python", "python3")
| table _time, host, parent_process_name, process_name, process_args
| sort - _time

This pattern — npm spawning a shell or network utility — is the common execution thread across every DPRK npm supply chain campaign documented in 2026.

Indicators of Compromise

| Type | Indicator | Association | |——|———–|————-| | Domain | validator[.]uno | PromptMink primary C2 | | Domain | ipfs-url-validator[.]vercel[.]app | PromptMink exfiltration endpoint | | Domain | winstonjs[.]site | PromptMink C2 | | Domain | changelog[.]rest | PromptMink C2 | | Domain | api[.]mywalletsss[.]store | PromptMink C2 | | Domain | csec-c2-server[.]onrender[.]com | PromptMink variant C2 | | IP | 45.61.161.146 | PromptMink infrastructure | | IP | 45.8.22.144 | PromptMink infrastructure | | IP | 45.8.22.52 | PromptMink infrastructure | | IP | 216.126.237.71 | Socket.IO RAT C2 | | SHA1 | bbcd50b6cf6f2f6f4b8a06261b5f23e47d098cf2 | @validate-sdk/v2 v1.22.11 | | SHA1 | 103db43d138b95cde454f1838ff10843dc8c5c51 | @solana-launchpad/sdk v1.0.2 | | SHA1 | ae4fe9f9a4f099de9132eb3346abcdd96dbeb39d | scraper-npm (PyPI) v1.0.4 | | File path | ~/.ssh/authorized_keys (modified) | PromptMink SSH persistence (T1098.004) | | File path | node_modules/@validate-sdk/v2/loader.mjs | PromptMink payload loader | | npm package | @validate-sdk/v2 | PromptMink second-layer payload | | npm package | bjs-lint-builder | PromptMink second-layer variant | | npm package | csec-crypto-utils | PromptMink updated payload | | npm package | aes-create-ipheriv | PromptMink second-layer variant | | npm package | jito-proper-excutor | PromptMink second-layer variant |

MITRE ATT&CK TTPs: T1195.001 (Supply Chain Compromise: Compromise Software Dependencies), T1566 (Phishing — fake job assessments), T1083 (File and Directory Discovery), T1005 (Data from Local System), T1041 (Exfiltration Over C2 Channel), T1098.004 (Account Manipulation: SSH Authorized Keys), T1560 (Archive Collected Data).

Conclusion

Famous Chollima has lowered the cost of npm supply chain attacks by outsourcing malware authorship to LLMs, and the resulting code is clean enough to bypass tools designed to catch human-written obfuscation. DPRK npm malware detection now requires combining manual hook inspection, behavioral scanning with tools like Socket.dev, and strict CI/CD controls — specifically --ignore-scripts and npm ci — to eliminate the install-time execution window these campaigns depend on. If you operate in crypto, DeFi, or fintech, treat any Solana-ecosystem package published in the last six months as unreviewed until audited.

See our guide on BlueNoroff’s fake Zoom campaign targeting crypto executives for additional DPRK attack-chain context →

See also: SAP npm Packages Backdoored by ‘Mini Shai-Hulud’ | GlassWorm VS Code Extensions Audit Guide | Hugging Face and ClawHub Malware Distribution

For any query contact us at contact@cipherssecurity.com

Leave a Reply

Your email address will not be published. Required fields are marked *