Fake Payment-SDK npm/PyPI Supply-Chain Campaign
Executive Summary
On 2026-07-07, Socket detected a coordinated supply-chain campaign that pushed 17 npm modules and 4 PyPI packages masquerading as SDKs for well-known payment brands — PaySafe, Skrill, and Neteller. Each npm package shipped four rapid versions (1.0.0–1.0.3); the PyPI packages shipped a single malicious 1.0.0. They were flagged malicious within roughly six minutes of publication, but any developer or CI job that installed one in that window is exposed.
The packages present a convincing SDK facade: calls immediately return { success: true, method, path } so the developer sees no error, while a delayed background routine harvests every environment variable whose name contains KEY, SECRET, TOKEN, PASS, AUTH, or API (values truncated to 100 chars), fingerprints the host, and exfiltrates to an ngrok-hosted C2. Prime targets include PAYSAFE_API_KEY, AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, and NPM_TOKEN.
The payload includes anti-analysis logic — it aborts on hosts with fewer than two CPU cores or with security keywords (sandbox, analyzer, cuckoo, vmware, vbox, malware) in the hostname/username — and hides its C2 behind a three-step decode (XOR, character shift, string reversal).
Attacker objective: steal long-lived CI/CD and cloud credentials from developer and build environments, then reuse them for downstream cloud access, further package publishing (NPM_TOKEN), or source-code access (GITHUB_TOKEN).
Defender priority: Deny the named packages in your registry proxy and scrub them from every lockfile (§7/§10); hunt Q1–Q3 for the ngrok C2 domain, the package names in install command lines, and the known payload hashes; then rotate every CI/CD and cloud secret reachable from any host that installed one — the credential exposure is the real incident, not the package file.
Source Review & Web Hunter Notes
| Tier | Source | Key finding | Carry fwd |
|---|---|---|---|
| 2 | Socket (originating research) | Cluster of npm+PyPI malware published 2026-07-07; 17 npm + 4 PyPI; payment-SDK facades; detected in ~6 min | Yes |
| 2 | GBHackers | Env-var harvest (KEY/SECRET/TOKEN/PASS/AUTH/API); fake success response; ngrok C2 caliber-spinner-finishing.ngrok-free.dev:443; sandbox evasion (<2 CPU cores, security hostnames) | Yes |
| 2 | CyberPress | Confirmed package names (paysafe-node, paysafe-checkout, skrill-payments, paysafe-sdk); XOR + char-shift + string-reversal C2 decode; 3 payload SHA-256 hashes | Yes |
Sourcing note: This is a fresh (7-day) supply-chain event; primary attribution is Socket's automated detection, corroborated by GBHackers and CyberPress. Atomic IOCs used in this pack — the three SHA-256 payload hashes, the ngrok C2 domain, and the package names — are drawn directly from those reports and cited in §18. No indicator here is fabricated.
Hunt Brief & Attack Chain
Hunt hypotheses (fidelity-ordered)
| # | Hypothesis | MITRE | Telemetry | Conf |
|---|---|---|---|---|
| H1 | A host resolved the campaign's ngrok C2 domain | T1071.001, T1572 | DnsRequest | High |
| H2 | An install command referenced one of the malicious package names | T1195.002 | ProcessRollup2 | High |
| H3 | A known payload hash was written or executed on a host | T1195.002, T1059.007 | ProcessRollup2 / PeFileWritten | High |
| H4 | A dev/CI host reached any *.ngrok-free.dev tunnel from node/python | T1572, T1041 | DnsRequest | Med |
| H5 | A package manager spawned a network client during install (beacon) | T1195.002, T1071 | ProcessRollup2 | Med |
Attack chain
| Step | Action | Telemetry / hunt angle |
|---|---|---|
| 1 · Delivery | Malicious packages published to npm/PyPI under payment-brand names | Registry audit; lockfile/manifest grep (§7) |
| 2 · Install | Developer or CI installs the package (typo/confusion or dependency pull) | ProcessRollup2: npm/pip CommandLine with package name (Q2) |
| 3 · Evade | Payload aborts on <2 CPU cores or security-keyword hostnames; decodes C2 (XOR/shift/reverse) | Low direct telemetry — anti-sandbox is in-process |
| 4 · Harvest | Reads env vars matching KEY/SECRET/TOKEN/PASS/AUTH/API; fingerprints host | In-process (JS/Python) — not directly visible; infer from install + egress |
| 5 · Exfil | Sends secrets to ngrok C2 over 443 | DnsRequest for C2 domain (Q1); ngrok tunnel egress (Q4) |
Consolidated IOC Table
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| domain | caliber-spinner-finishing.ngrok-free.dev | high | detect | C2 / exfil endpoint (:443) |
| sha256 | ce09810adca70ebec87bc455380ef629ceaa2a0d926149d9115604060167682c | high | detect | Campaign payload |
| sha256 | b2ea8d69f6792a87327ffde2ee4551bb6b99617f53e1ba71bf9a70f45dbc57ea | high | detect | Campaign payload |
| sha256 | 8a70a5c1075f2dea4db94633ddc64b0d03d0385fdeda7c226acc944331febf43 | high | detect | Campaign payload |
| npm pkg | paysafe-checkout, paysafe-node, paysafe-js | high | detect | Malicious (versions 1.0.0–1.0.3) |
| npm pkg | skrill-payments, neteller | high | detect | Malicious (brand facade) |
| pypi pkg | paysafe-sdk, paysafe-payments, paysafe-api, paysafe-kyc | high | detect | Malicious (version 1.0.0) |
| behavior | Env harvest of KEY/SECRET/TOKEN/PASS/AUTH/API → ngrok | high | hunt | Credential exfil routine |
| infra | *.ngrok-free.dev egress from a build/CI host | medium | hunt | Tunnel abuse (many campaigns) |
The package-name and hash IOCs are point-in-time (the packages were pulled quickly); the ngrok domain is perishable. Treat the SHA-256 and domain as short-lived detect entries and the package names primarily as a registry/lockfile audit (§7).
Affected Surface & Telemetry Matrix
| Surface | Required telemetry | Priority | Gap risk |
|---|---|---|---|
| Developer workstations (node/python) | ProcessRollup2 (install cmds), DnsRequest, NetworkConnectIP4 | High | Personal/BYOD dev machines may lack sensor |
| CI/CD build runners | ProcessRollup2, DnsRequest; CI job logs | Critical | Ephemeral runners / containers often unmonitored by EDR |
| Source repos & lockfiles | Manifest/lockfile content (grep) — native | Critical | Not EDR-visible; needs SCA / repo scan |
| CI/CD & cloud secrets | N/A — preventive (rotation, short-lived tokens) | Critical | Exfil is in-process; assume exposure on any install |
Telemetry reality: the malicious logic runs inside the node/python interpreter, so the harvest step (env read) is not directly visible to EDR. Detection leans on the install command, the DNS/egress to C2, the payload hash, and lockfile audits — plus fast secret rotation as the real mitigation.
ATT&CK Mapping
| Tactic | Technique | Observed behavior | Query / Control |
|---|---|---|---|
| Initial Access | T1195.002 Compromise Software Supply Chain | Malicious payment-SDK packages installed from npm/PyPI | Q2, Q3 · registry deny |
| Execution | T1059.007 JavaScript / T1059.006 Python | Package payload runs at import/install | Q5 · --ignore-scripts |
| Defense Evasion | T1497 Virtualization/Sandbox Evasion | Aborts on <2 CPU cores / security hostnames | — (in-process) |
| Defense Evasion | T1027 Obfuscated Files or Information | C2 hidden via XOR + char-shift + string-reversal | Q3 (hash) |
| Credential Access | T1552.001 Unsecured Credentials in Files/Env | Harvests env vars matching KEY/SECRET/TOKEN/PASS/AUTH/API | secret rotation · §12 |
| Command & Control | T1572 Protocol Tunneling · T1071.001 Web Protocols | Exfil via ngrok tunnel over 443 | Q1, Q4 · egress block |
| Exfiltration | T1041 Exfiltration Over C2 Channel | Secrets + host fingerprint sent to ngrok C2 | Q1, Q4 |
Native Audit-Log Hunts (non-CQL)
The highest-value hunts for a package-supply-chain event live in your source repos, lockfiles, and registry proxy — not on the endpoint. Run these across every repo and build cache.
Lockfile / manifest audit (repos + build caches)
# Search every repo, lockfile, and node_modules/site-packages cache for the names: paysafe-checkout paysafe-node paysafe-js paysafe-sdk paysafe-payments paysafe-api paysafe-kyc skrill-payments neteller # Files to grep: package.json, package-lock.json, yarn.lock, pnpm-lock.yaml, # requirements.txt, poetry.lock, Pipfile.lock # Any hit = a host/repo that pulled the package -> treat as exposed, rotate its secrets.
Registry proxy / SCA logs
# In Artifactory/Nexus/Verdaccio or your SCA tool, query download events for the # package names above since 2026-07-07. Map each download to a host / CI job / user.
CI/CD secret exposure & provider abuse
# For every host that installed a package: enumerate which secrets were in its env # (KEY/SECRET/TOKEN/PASS/AUTH/API) and rotate them. Then review provider-side logs: # - GitHub: audit log + token usage for GITHUB_TOKEN # - AWS: CloudTrail for the AWS_SECRET_ACCESS_KEY principal # - npm: publish events for NPM_TOKEN
Step 3–4 (sandbox evasion + env harvest) run inside the interpreter and leave little endpoint telemetry — the lockfile/registry audit is what actually scopes the blast radius. Pair it with the DNS/egress hunts (Q1, Q4) to catch hosts that reached the C2.
CrowdStrike LogScale CQL Hunt Queries
Looks for: any host resolving the campaign's exact ngrok C2 domain — a direct, high-fidelity exfil indicator.
// HUNT: DNS resolution of the payment-SDK campaign C2 (ngrok tunnel) // CONF: high FP: low COST: low | REQUIRES: DnsRequest telemetry #event_simpleName=DnsRequest | DomainName=/(^|\.)caliber-spinner-finishing\.ngrok-free\.dev$/i | table([ComputerName, DomainName, ContextBaseFileName, aid], limit=200)
Looks for: a package-manager process whose command line contains one of the known-malicious package names — catches the install even where the C2 was unreachable.
// HUNT: npm/pip install command referencing a known-malicious payment-SDK package // CONF: high FP: low COST: low | REQUIRES: process telemetry with CommandLine #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(node|npm|npx|yarn|pnpm|pip[0-9.]*|python[0-9.]*)$/i | CommandLine=/(paysafe-checkout|paysafe-node|paysafe-js|paysafe-sdk|paysafe-payments|paysafe-api|paysafe-kyc|skrill-payments|neteller)/i | table([ComputerName, UserName, ParentBaseFileName, CommandLine, aid], limit=200)
Looks for: the three published payload hashes written or executed on any host.
// HUNT: Known payment-SDK malware payload by SHA256 // CONF: high FP: low COST: low | REQUIRES: process or file-write telemetry with hashes #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2|PeFileWritten|NewExecutableWritten/ | SHA256HashData=/^(ce09810adca70ebec87bc455380ef629ceaa2a0d926149d9115604060167682c|b2ea8d69f6792a87327ffde2ee4551bb6b99617f53e1ba71bf9a70f45dbc57ea|8a70a5c1075f2dea4db94633ddc64b0d03d0385fdeda7c226acc944331febf43)$/i | table([ComputerName, UserName, FileName, SHA256HashData, aid], limit=200)
Looks for: node/python resolving any *.ngrok-free.dev tunnel — a broader net for campaign infrastructure rotation. FP: some developers use ngrok legitimately.
// HUNT: Node/Python process resolving an ngrok free tunnel (staged-package exfil) // CONF: medium FP: medium COST: low | REQUIRES: DnsRequest with process context // TUNING: allowlist developers/teams with sanctioned ngrok use; scope to build/CI host groups first #event_simpleName=DnsRequest | DomainName=/\.ngrok-free\.dev$/i | ContextBaseFileName=/^(node|npm|npx|python[0-9.]*|pip[0-9.]*)$/i | table([ComputerName, DomainName, ContextBaseFileName, aid], limit=200)
Looks for: an install-time process spawning a network client — the shape of a post-install beacon. FP: many legitimate postinstall scripts fetch binaries.
// HUNT: npm/pip install spawning a network client (post-install exfil beacon) // CONF: medium FP: medium COST: low | REQUIRES: process telemetry // TUNING: exclude known-good postinstall build steps and internal registry mirrors; scope to CI host groups #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(npm|npx|yarn|pnpm|pip[0-9.]*)$/i | FileName=/^(curl|wget|nc|ncat|socat|node|python[0-9.]*)$/i | table([ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, aid], limit=200)
CrowdStrike Custom IOA Recommendations
| IOA / control | Pattern | Exclusions | Action |
|---|---|---|---|
| Malicious Package Install | CommandLine of npm/pip contains a name from the campaign list | None (names are attacker-chosen) | Detect (high) → alert IR |
| Campaign C2 IOC | Domain + 3 SHA-256 as Falcon Custom IOCs | None | Detect/Prevent (short expiry) |
| ngrok Egress from CI | *.ngrok-free.dev from a build-runner host group | Sanctioned ngrok users/teams | Detect → review |
Deployment path: load the domain + hashes as Custom IOCs immediately (short expiry — they are perishable). Promote Q2 to a detection; keep Q4/Q5 as hunts until tuned. The durable control is the registry deny-list + secret rotation, not the IOC block.
Machine-Readable IOC Appendix
type,value,action,severity,expiration,description,tags domain,caliber-spinner-finishing.ngrok-free.dev,detect,high,2026-10-14,Payment-SDK campaign C2,campaign:PaymentSDK-SupplyChain sha256,ce09810adca70ebec87bc455380ef629ceaa2a0d926149d9115604060167682c,prevent,high,2027-01-14,Payment-SDK payload,campaign:PaymentSDK-SupplyChain sha256,b2ea8d69f6792a87327ffde2ee4551bb6b99617f53e1ba71bf9a70f45dbc57ea,prevent,high,2027-01-14,Payment-SDK payload,campaign:PaymentSDK-SupplyChain sha256,8a70a5c1075f2dea4db94633ddc64b0d03d0385fdeda7c226acc944331febf43,prevent,high,2027-01-14,Payment-SDK payload,campaign:PaymentSDK-SupplyChain
# npm: paysafe-checkout paysafe-node paysafe-js skrill-payments neteller # PyPI: paysafe-sdk paysafe-payments paysafe-api paysafe-kyc
# Env-var name patterns harvested (truncated to 100 chars): KEY SECRET TOKEN PASS AUTH API # High-value targets: PAYSAFE_API_KEY, AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, NPM_TOKEN # Exfil: HTTPS to *.ngrok-free.dev (C2 decoded via XOR + char-shift + string-reversal) # Anti-analysis: aborts if CPU cores under 2 OR hostname/user contains # sandbox | analyzer | cuckoo | vmware | vbox | malware
# Confirm these are in place after remediation: npm ci --ignore-scripts # CI installs do not run postinstall pip install --require-hashes ... # hash-pinned installs # Registry proxy allow-list enabled; named packages denied # Egress allow-list on build runners (registries only; no ngrok) # CI secrets are short-lived OIDC tokens, not long-lived KEY/TOKEN env values
Detection Validation Gates
| Gate | Check | Pass criteria |
|---|---|---|
| Telemetry ready | DnsRequest + ProcessRollup2 flowing from dev and CI host groups | Recent events visible for representative hosts |
| IOC loaded | Domain + 3 SHA-256 imported as Custom IOCs | Q1/Q3 have data to match; IOC status active |
| Positive test | In a lab, run npm install paysafe-node (from a private stub) and a DNS lookup to the C2 | Q1 & Q2 fire |
| Baseline | Run Q4/Q5 over 7 days | Allowlist sanctioned ngrok / postinstall before alerting |
| Scope complete | Lockfile/registry audit (§7) run across all repos | Every install mapped to a host; its secrets rotated |
Hardening — Tiered
- Deny the named packages in your registry proxy and remove them from every lockfile / build cache. (MITRE M1042 Disable or Remove Feature or Program)
- Rotate every CI/CD and cloud secret reachable from any host that installed a package (GITHUB_TOKEN, NPM_TOKEN, AWS keys, PAYSAFE_API_KEY, etc.). (key hygiene; supports M1027)
- Block egress to
*.ngrok-free.devfrom build runners and, where feasible, from developer networks. (M1037 Filter Network Traffic) - Load the domain + 3 hashes as Custom IOCs (short expiry) and hunt Q1–Q3. (M1021 / detection)
- Front all installs with a private registry proxy (Artifactory/Nexus/Verdaccio) on an allow-list; enable SCA/Socket scanning that blocks newly-published, low-reputation packages. (M1016 Vulnerability Scanning)
- Disable install-time scripts in CI —
npm ci --ignore-scripts, hash-pinned pip installs — so a malicious package can't auto-execute. (M1038 Execution Prevention) - Replace long-lived CI secrets with short-lived OIDC tokens, minimally scoped, so a harvested value expires fast. (M1026 Privileged Account Mgmt · M1041 Encrypt Sensitive Information)
- Enforce lockfile integrity + dependency pinning + provenance (npm provenance / SLSA attestations) in CI so unexpected packages fail the build. (best-practice — SLSA / OpenSSF) no formal benchmark
- Network-isolate build runners with egress allow-listed to package registries only. (M1030 Network Segmentation)
- Continuous secret scanning + automated rotation across repos and CI. (M1016)
Deployable Playbooks
1 · Purge & deny the packages
# Remove any installed copy and scrub lockfiles, then reinstall clean: npm uninstall paysafe-checkout paysafe-node paysafe-js skrill-payments neteller pip uninstall -y paysafe-sdk paysafe-payments paysafe-api paysafe-kyc # Registry proxy deny-list (Verdaccio example): add the names under an unmatched # package rule so they can never resolve internally. # Clear caches so a poisoned copy is not reused: npm cache clean --force pip cache purge
2 · Harden CI install steps
# package installs must NOT run arbitrary scripts: npm ci --ignore-scripts # python: pin hashes so only vetted artifacts install: pip install --require-hashes -r requirements.txt # Egress allow-list on the runner (example iptables intent): permit registry # endpoints only; drop everything else, including ngrok.
3 · Rotate & verify exposed secrets
# For each host that installed a package, for each secret in its env matching # KEY|SECRET|TOKEN|PASS|AUTH|API: # 1. Issue a replacement (prefer short-lived OIDC). # 2. Revoke the old value. # 3. Review provider logs for use since 2026-07-07: # GitHub audit log (GITHUB_TOKEN), AWS CloudTrail (access keys), # npm publish events (NPM_TOKEN).
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Scope | Grep all repos/lockfiles/registry logs for the 9 package names; run Q1–Q3 across dev + CI hosts to find installs and C2 contact | Threat Hunt / AppSec | Repo hits; query exports |
| Isolate | Block *.ngrok-free.dev egress; deny packages in registry; quarantine any host with a Q3 hash hit | SOC / Platform | Egress + registry rule changes |
| Eradicate | Uninstall packages; clear npm/pip caches; rebuild affected CI images from clean base | Platform / Dev | Uninstall + cache-clean logs |
| Rotate | Rotate ALL secrets reachable from any affected host (CI/CD, cloud, VCS, npm); prefer short-lived tokens | IAM / Platform | Rotation record |
| Verify abuse | Review GitHub/AWS/npm provider logs for use of exposed tokens since 2026-07-07 | IR / Cloud | Provider log review |
| Recover | Re-enable installs behind registry allow-list + --ignore-scripts; monitor Q1/Q2 as detections | Platform / SOC | CI config; detection status |
Detection Coverage Map
| Technique | Behavior | CQL | IOA/IOC | Coverage |
|---|---|---|---|---|
| T1195.002 | Malicious package installed | Q2, Q3 | Yes | Good |
| T1071.001 / T1572 | C2 / exfil to ngrok | Q1, Q4 | IOC | Good |
| T1041 | Secret exfiltration | Q1, Q4 | — | Partial (payload/DNS only) |
| T1552.001 | Env-var credential harvest | — | — | GAP (in-process) |
| T1497 | Sandbox evasion | — | — | GAP (in-process) |
| T1059.007/.006 | Post-install execution | Q5 | — | Partial |
Known gaps: (1) The env-var harvest and anti-sandbox checks run inside the node/python interpreter and produce no discrete EDR event — they are covered only indirectly (install + C2 + hash) and, decisively, by secret rotation. (2) Ephemeral CI runners often lack an EDR sensor, so the lockfile/registry audit (§7) is the authoritative scoping method, not CQL. (3) Q4/Q5 are FP-prone (legit ngrok / postinstall fetches) — tune before alerting.
Hunt Summary Ticket
TITLE: Fake Payment-SDK npm/PyPI Supply-Chain Campaign (PaySafe/Skrill/Neteller)
SEVERITY: High — active credential-theft supply-chain campaign (published 2026-07-07)
SCOPE: Dev workstations + CI/CD runners that installed a package since 2026-07-07
HYPOTHESIS: A host installed a malicious payment-SDK package and exfiltrated env
secrets (KEY/SECRET/TOKEN/PASS/AUTH/API) to an ngrok C2.
QUERIES: Q1 C2 DNS · Q2 package-name install · Q3 payload hash
Q4 ngrok tunnel · Q5 install-time net client · native lockfile audit (s7)
DO FIRST: 1) Grep all repos/lockfiles for the 9 package names
2) Load domain + 3 SHA-256 as Custom IOCs; run Q1-Q3
3) Rotate every secret on any host that installed one
FINDINGS: ____________________________________________________________
GAPS: Env harvest + sandbox evasion in-process; ephemeral CI runners unmonitored
ACTIONS: Deny packages · block ngrok egress · rotate secrets · --ignore-scripts in CI
OWNER: ____________________ VERSION: v0.2 (2026-07-14)
Changelog
table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.References
| Tier | Source | Used for | Accessed |
|---|---|---|---|
| 2 | GBHackers — npm and PyPI malware campaign exfiltrates CI/CD secrets | Env-var harvest, ngrok C2 domain, sandbox evasion, package names | 2026-07-14 |
| 2 | CyberPress — Fake SDKs target developers | Package names, obfuscation chain, 3 SHA-256 payload hashes | 2026-07-14 |
| 2 | Upwind — npm/PyPI supply-chain campaign | Campaign scope / CI-CD targeting context | 2026-07-14 |
Generated 2026-07-14 · HuntPack · Defensive detection & hardening content only. No victim organizations named.