Mastra npm Supply-Chain Attack — "easy-day-js"
Executive Summary
On 2026-06-17, an attacker hijacked the npm account of a legitimate former Mastra contributor (ehindero) whose @mastra scope publishing rights were never revoked. In an 88-minute automated campaign (01:12–02:39 UTC), the attacker republished 142 packages under the @mastra scope (144 total compromised artifacts including the bait/dropper packages), each with a single injected dependency: easy-day-js — a typosquat of the legitimate dayjs date library. Mastra is a popular open-source JS/TS framework for building AI applications; the affected packages exceed 1.1M combined weekly downloads (@mastra/core alone ~918K/week).
easy-day-js is a clone of dayjs that ships an obfuscated postinstall hook (node setup.cjs --no-warnings). On install, setup.cjs: (1) sets NODE_TLS_REJECT_UNAUTHORIZED=0 to disable TLS verification, (2) writes beacon-marker files (.pkg_history, .pkg_logs) to the user home/temp directory, (3) fetches a second-stage payload from attacker C2 over unvalidated TLS, (4) writes it as a random 24-hex-char .js file, (5) spawns it as a DETACHED, hidden background process (detached:true, stdio:'ignore', windowsHide:true, then .unref()) that outlives the install, and (6) self-deletes (fs.rmSync(__filename)) to destroy the primary forensic artifact. The second stage is a remote-access trojan oriented toward cryptocurrency theft and credential/API-key harvesting (LLM and cloud keys).
This is distinct from prior Shai-Hulud, PyPI, and AwesomeMotive supply-chain incidents: it is an npm-scope-wide hijack of a single AI framework org, anchored on a dayjs typosquat as the payload carrier.
Defender priority: The highest-fidelity catch is a node process spawning a detached child node process within seconds of npm install, combined with the env var NODE_TLS_REJECT_UNAUTHORIZED=0 and outbound to 23.254.164.92:8000 / 23.254.164.123:443. Block both C2 IPs at the perimeter now and sweep package-lock.json / node_modules for easy-day-js across all build agents and developer endpoints.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Fwd |
|---|---|---|---|
| 1 | StepSecurity | Exact child_process.spawn signature; marker files .pkg_history / .pkg_logs; XOR-encoded log; C2 IPs and /update/49890878 endpoint; affected versions (@mastra/core@1.42.1 etc.) | yes |
| 1 | Endor Labs | Six-step setup.cjs behavior; NODE_TLS_REJECT_UNAUTHORIZED=0; home-dir artifacts; missing SLSA provenance; minute-by-minute timeline | yes |
| 1 | StepSecurity / Aikido | 140+ packages, 88-minute republish window, 1.1M+ weekly downloads blast radius | yes |
| 1 | The Hacker News | Campaign naming ("easy-day-js"), 144 packages, account-hijack root cause via stale contributor token | yes |
| 2 | Orca Security | Crypto-stealer RAT classification; credential/API-key harvesting intent of stage-2 | partial |
Hunter note: Reporting differs on exact package count (116 / 140 / 142 / 144) and on whether markers land in temp vs. home dir — both paths are hunted. Account attribution differs between ehindero (publisher) and sergey2016 (bait-package publisher); both are former/related contributor identities. Atomic file hashes for setup.cjs and stage-2 are not yet broadly published — behavioral detection is primary; CSV uses placeholders for hashes.
Hunt Brief & Attack Chain
Hunt Hypotheses (fidelity-ordered)
- H1 (high): A
nodeprocess spawnssetup.cjsas a child of annpm/npx/CI install — postinstall execution. low FP - H2 (high): A
nodeprocess spawns a second detachednodechild running a random-hex-named.jsfrom the home/temp dir shortly after install. low FP - H3 (high): Any process sets
NODE_TLS_REJECT_UNAUTHORIZED=0in its environment/command line. med FP - H4 (high): Outbound connection to C2
23.254.164.92(:8000) or23.254.164.123(:443) from a node process. low FP - H5 (medium): Beacon-marker files
.pkg_history/.pkg_logswritten to a home/temp directory. med FP - H6 (medium): A
.js/.cjsdropper self-deletes (FileDeleteInfo) the same file it was just running from, shortly after a network fetch. med FP - H7 (high): Presence of
easy-day-jsinnode_modulesorpackage-lock.jsonon any endpoint/build agent. low FP
Attack Chain
| Step | Behavior | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 · Initial Access | Hijacked npm account republishes 142 @mastra pkgs with easy-day-js dep | n/a (registry-side) | SCA / lockfile scan (H7) |
| 2 · Execution | npm install triggers postinstall: node setup.cjs --no-warnings | ProcessRollup2 | node → setup.cjs (H1) |
| 3 · Defense Evasion | NODE_TLS_REJECT_UNAUTHORIZED=0 set; --no-warnings | ProcessRollup2 (CommandLine/env) | TLS-off env (H3) |
| 4 · Discovery / Beacon | Marker files .pkg_history, .pkg_logs written | FileCreateInfo | Marker files (H5) |
| 5 · Ingress Tool Transfer | Stage-2 fetched from https://23.254.164.92:8000/update/49890878 | NetworkConnectIP4 / DnsRequest | C2 connect (H4) |
| 6 · Execution (stage-2) | Detached hidden node <hex>.js <stage2C2> | ProcessRollup2 | Detached node child (H2) |
| 7 · Defense Evasion | fs.rmSync(__filename) self-deletes setup.cjs | FileDeleteInfo | Self-delete (H6) |
| 8 · Impact | Crypto theft + credential/LLM/cloud key harvesting via stage-2 C2 23.254.164.123:443 | NetworkConnectIP4 | Stage-2 C2 (H4) |
Consolidated IOC Table
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| IPv4 | 23.254.164.92 (port 8000) | high | detect | Stage-1 C2; serves stage-2 payload |
| URL | https://23.254.164.92:8000/update/49890878 | high | detect | Payload download endpoint |
| IPv4 | 23.254.164.123 (port 443) | high | detect | Stage-2 C2; beacon + exfil sink |
| npm package | easy-day-js@1.11.22 | high | detect | Weaponized dropper (typosquat of dayjs) |
| npm package | easy-day-js@1.11.21 | medium | hunt | Clean decoy; attacker-controlled |
| npm scope | @mastra/* versions published 2026-06-17 01:12–02:39 UTC | high | hunt | e.g. @mastra/core@1.42.1, @mastra/memory@1.20.4, mastra@1.13.1 |
| Filename | setup.cjs (~4.5 KB, self-deleting) | high | hunt | Obfuscated postinstall dropper |
| File marker | .pkg_history, .pkg_logs | medium | hunt | Beacon markers in home/temp dir |
| File pattern | 24-hex-char .js in home/temp dir | medium | hunt | Stage-2 payload filename |
| Env var | NODE_TLS_REJECT_UNAUTHORIZED=0 | medium | hunt | TLS-validation disable |
| npm account | ehindero / sergey2016 | medium | pivot | Hijacked / attacker publisher identities |
| SHA256 | REPLACE_WITH_SHA256 (setup.cjs / stage-2) | medium | detect | Populate when vendor hashes publish |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Developer endpoints (mac/Win/Linux) | ProcessRollup2, FileCreate/DeleteInfo, NetworkConnectIP4 | critical | Low — Falcon sensor coverage typical |
| CI/CD build agents | ProcessRollup2, NetworkConnectIP4 | critical | High — ephemeral runners may lack sensor |
| Container build hosts | ProcessRollup2 (container-aware), NetworkConnectIP4 | high | High — short-lived containers |
| Network egress | NetworkConnectIP4, DnsRequest, proxy/firewall logs | high | Medium — direct-IP C2 bypasses DNS logging |
| Artifact / registry | SCA / SBOM, lockfile scan | high | Medium — needs out-of-band tooling |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Query / Control |
|---|---|---|---|
| Initial Access | T1195.002 — Supply Chain Compromise: Software Supply Chain | Hijacked @mastra scope republishes with malicious dep | H7 / SCA + Q7 |
| Execution | T1059.007 — JavaScript | node setup.cjs via npm postinstall | Q1 |
| Execution | T1554 — Compromise Host Software Binary | Trojanized npm packages run on install | Q1 / Q7 |
| Command & Control | T1105 — Ingress Tool Transfer | Stage-2 fetched from C2 over unvalidated TLS | Q4 / Q5 |
| Defense Evasion | T1027 — Obfuscated Files or Information | Obfuscated setup.cjs; XOR-encoded marker | Q1 / Q6 |
| Defense Evasion | T1070.004 — Indicator Removal: File Deletion | fs.rmSync(__filename) self-delete | Q6 |
| Defense Evasion | T1562.001 — Impair Defenses (TLS validation) | NODE_TLS_REJECT_UNAUTHORIZED=0 | Q3 |
| Impact | T1496 — Resource Hijacking / crypto theft | Stage-2 RAT steals crypto + credentials | Q4 |
Native Audit-Log Hunts (non-CQL)
Lockfile / dependency tree (run on every endpoint & build agent)
// Find easy-day-js anywhere in the tree grep -rln "easy-day-js" --include=package-lock.json --include=package.json / npm ls easy-day-js // per-project // Pinpoint vulnerable @mastra versions npm ls @mastra/core | grep -E "1\.42\.1"
Home / temp directory marker sweep
// macOS / Linux
ls -la ~/.pkg_history ~/.pkg_logs 2>/dev/null
find /tmp "$HOME" -maxdepth 1 -name ".pkg_*" -o -regextype posix-extended -regex '.*/[0-9a-f]{24}\.js'
// Windows (PowerShell)
Get-ChildItem -Path $env:USERPROFILE,$env:TEMP -Filter ".pkg_*" -Force -EA SilentlyContinue
npm audit / SCA
npm audit --json | grep -i easy-day-js // SBOM: assert no resolved url points to easy-day-js or @mastra rev'd after 2026-06-17 01:00 UTC
Note: Because setup.cjs self-deletes, absence of the file does not clear a host — pivot on process/network telemetry (Q1–Q6) and the persistent stage-2 process / markers.
CrowdStrike LogScale CQL Hunt Queries
Looks for: a node process running setup.cjs with a package-manager ancestor — the postinstall dropper trigger.
// HUNT: npm postinstall executing setup.cjs // MITRE: T1059.007, T1554, T1195.002 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: legitimate packages with a benign setup.cjs (rare; verify path/parent) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ImageFileName=/\\node(\.exe)?$/i | CommandLine=/setup\.cjs/i | CommandLine=/--no-warnings/i OR ParentBaseFileName=/^(npm|npx|node|yarn|pnpm)/i | table([ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, SHA256HashData, aid], limit=200)
Looks for: a node process launching a second node child whose script is a 24-hex-char .js in a home/temp path — the detached stage-2 spawn.
// HUNT: Detached node child running random-hex .js (stage-2) // MITRE: T1059.007, T1105 // CONF: high FP: low COST: medium | REQUIRES: ProcessRollup2 // FALSE POSITIVES: build tools writing hashed temp scripts (verify parent is node/npm) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ImageFileName=/\\node(\.exe)?$/i | ParentBaseFileName=/^node/i | CommandLine=/[\/\\][0-9a-f]{24}\.js/i | CommandLine=/(Temp|tmp|Users|home|AppData)/i | table([ComputerName, UserName, ParentBaseFileName, CommandLine, SHA256HashData, aid], limit=200)
Looks for: NODE_TLS_REJECT_UNAUTHORIZED=0 appearing in a process command line / environment. FP: some legacy internal tooling sets this against self-signed corp certs — baseline and exclude known dev hosts/parents.
// HUNT: Process disabling Node TLS verification // MITRE: T1562.001, T1027 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2 (CommandLine) // FALSE POSITIVES: legacy node apps targeting self-signed internal certs // TUNING: exclude known-good ComputerName/ParentBaseFileName after baselining #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | CommandLine=/NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*0/i | ParentBaseFileName!=/^(corp-build-tool)\.exe$/i | table([ComputerName, UserName, ImageFileName, ParentBaseFileName, CommandLine, aid], limit=200)
Looks for: any host connecting to the published stage-1 / stage-2 C2 IPs.
// HUNT: Outbound to known easy-day-js C2 IPs // MITRE: T1105, T1496 // CONF: high FP: low COST: low | REQUIRES: NetworkConnectIP4 // FALSE POSITIVES: none expected; these IPs are attacker-attributed #event_simpleName=NetworkConnectIP4 | RemoteAddressIP4=/^23\.254\.164\.(92|123)$/ | table([ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort, aid], limit=200)
Looks for: a node-context process making an outbound connection to a raw IP on non-standard ports (e.g. 8000) — generalizes beyond the known IPs. FP: legitimate node apps that beacon to IP-based services; scope to install windows and exclude known SaaS ranges.
// HUNT: node outbound to raw-IP C2 on uncommon port // MITRE: T1105 // CONF: medium FP: medium COST: medium | REQUIRES: NetworkConnectIP4 // FALSE POSITIVES: node apps talking to IP-based internal services // TUNING: exclude RFC1918 and known SaaS egress ranges; focus RemotePort 8000/8080/8443 #event_simpleName=NetworkConnectIP4 | ContextBaseFileName=/^node(\.exe)?$/i | in(RemotePort, values=[8000, 8080, 8443]) | RemoteAddressIP4!=/^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)/ | table([ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort, aid], limit=200)
Looks for: deletion of setup.cjs or creation of .pkg_history / .pkg_logs beacon markers. FP: build caches churn temp files; pair with a node parent and proximity to a network fetch.
// HUNT: self-deleting setup.cjs + beacon-marker files // MITRE: T1070.004, T1027 // CONF: medium FP: medium COST: medium | REQUIRES: FileDeleteInfo, FileCreateInfo // FALSE POSITIVES: package managers churning temp/cache files // TUNING: require a node-family process context; correlate with Q1/Q4 on same aid #event_simpleName=/FileDeleteInfo/ | TargetFileName=/(\\|\/)(setup\.cjs|\.pkg_history|\.pkg_logs)$/i | table([ComputerName, UserName, #event_simpleName, TargetFileName, ContextBaseFileName, aid], limit=200)
Looks for: file-system evidence of the typosquat dependency on disk. FP: security scanners/quarantine copies may reference the name; verify it is a real install path.
// HUNT: easy-day-js in node_modules / package-lock // MITRE: T1195.002, T1554 // CONF: high FP: medium COST: medium | REQUIRES: FileCreateInfo // FALSE POSITIVES: SCA tools / quarantine snapshots referencing the name // TUNING: exclude scanner ContextBaseFileName; confirm path under node_modules #event_simpleName=/NewExecutableWritten|PeFileWritten/ | TargetFileName=/node_modules[\/\\]easy-day-js[\/\\]/i OR TargetFileName=/easy-day-js/i AND TargetFileName=/package-lock\.json$/i | table([ComputerName, UserName, TargetFileName, ContextBaseFileName, aid], limit=200)
CrowdStrike Custom IOA Recommendations
| IOA Name | Pattern | Action | Exclusions |
|---|---|---|---|
| npm-postinstall-setup-cjs | Process node, CommandLine contains setup.cjs + --no-warnings, parent npm/npx/yarn/pnpm | Detect (High) → Prevent after tuning | Approved internal packages with verified setup.cjs path |
| node-detached-hex-child | Parent node spawning child node with a 24-hex .js in temp/home | Detect (Critical) | Known build toolchains writing hashed temp scripts |
| node-tls-verify-disabled | CommandLine/env contains NODE_TLS_REJECT_UNAUTHORIZED=0 | Detect (Medium) | Baselined legacy internal-cert hosts/parents |
| easy-day-js-c2 | NetworkConnect to 23.254.164.92 / 23.254.164.123 | Prevent (Critical) | None |
Promotion path: Q1, Q2, Q4 are strong IOA/Prevent candidates (low FP, attacker-specific). Q3, Q5, Q6, Q7 stay Investigate-only until baselined per environment.
Machine-Readable IOC Appendix
type,value,action,severity,expiration,description,tags ipv4,23.254.164.92,prevent,critical,2026-12-18,easy-day-js stage-1 C2,campaign:MastraNpm ipv4,23.254.164.123,prevent,critical,2026-12-18,easy-day-js stage-2 C2,campaign:MastraNpm domain,REPLACE_WITH_C2_DOMAIN,detect,high,2026-09-18,easy-day-js C2 (if domain surfaces),campaign:MastraNpm sha256,REPLACE_WITH_SETUP_CJS_SHA256,prevent,critical,2026-12-18,setup.cjs dropper,campaign:MastraNpm sha256,REPLACE_WITH_STAGE2_SHA256,prevent,critical,2026-12-18,stage-2 RAT payload,campaign:MastraNpm
postinstall := node setup.cjs --no-warnings (parent: npm|npx|yarn|pnpm) detached_child := node <24-hex>.js <stage2-c2> (detached, stdio ignore, windowsHide) tls_off := env NODE_TLS_REJECT_UNAUTHORIZED=0 self_delete := fs.rmSync(setup.cjs) markers := ~/.pkg_history, ~/.pkg_logs (XOR-encoded pkg name in .pkg_logs)
easy-day-js@1.11.22 (weaponized dropper)
easy-day-js@1.11.21 (clean decoy, attacker-controlled)
@mastra/* versions published 2026-06-17 01:12-02:39 UTC
e.g. @mastra/core@1.42.1, @mastra/memory@1.20.4,
@mastra/schema-compat@1.2.12, mastra@1.13.1
Publisher accounts: ehindero, sergey2016 (tutamail.com)
# 1. Block both C2 IPs at perimeter (23.254.164.92, 23.254.164.123) # 2. grep -rln "easy-day-js" across all repos + build agents # 3. Pin/rollback @mastra to versions published before 2026-06-17 01:00 UTC # 4. Rotate all secrets exposed to affected build agents: # npm tokens, cloud keys, LLM API keys, crypto wallet keys # 5. Wipe/rebuild any host that ran an affected install # 6. Enforce --ignore-scripts in CI; require SLSA provenance
Detection Validation Gates
| Gate | Check | Pass Criteria |
|---|---|---|
| Telemetry ready | ProcessRollup2, FileCreate/DeleteInfo, NetworkConnectIP4 flowing from dev + CI hosts | All four event types present in last 24h |
| Benign baseline | Run Q3 & Q5 over 7d; catalog legitimate NODE_TLS_REJECT_UNAUTHORIZED=0 and node-IP egress | Known-good list built; exclusions added |
| Positive test | In a sandbox, create a benign setup.cjs + a temp node-spawn-detached chain | Q1 & Q2 fire on the test artifacts |
| C2 test | Sinkhole-route 23.254.164.92 in lab; confirm Q4 fires | Q4 returns the test host |
| Promotion | Q1/Q2/Q4 FP-clean for 7d | Promote to Custom IOA / Prevent |
Hardening — Tiered
- Block C2: deny
23.254.164.92and23.254.164.123at firewall/proxy egress. (M1037 Filter Network Traffic) - Disable install scripts in CI: set
npm config set ignore-scripts trueon build agents; pass--ignore-scripts. Stops postinstall droppers cold. (M1042 Disable or Remove Feature) - Lockfile sweep + rollback: remove
easy-day-js; pin@mastra/*to pre-2026-06-17 versions. (M1051 Update Software) - Rotate secrets exposed to any affected build agent: npm tokens, cloud keys, LLM API keys, wallet keys. (M1027 / M1032)
- Revoke stale publish tokens: audit
@mastra-style org membership and remove former-contributor publish rights; require 2FA + automation tokens with least scope. (M1018 User Account Management) - Require provenance: enforce
npm install --foreground-scriptsvisibility and SLSA/provenance attestation gating in CI. (M1016 Vulnerability Scanning / supply-chain policy) - Egress allowlist for build agents: permit only registry + known mirrors; deny raw-IP egress on 8000/8443 from runners. (M1037)
- Private registry / pull-through proxy (Verdaccio, Artifactory, GitHub Packages) with cooldown/quarantine on newly published versions. (M1016)
- SBOM + continuous SCA wired into CI to fail builds on known-bad coordinates. (M1016)
- Ephemeral, network-segmented build runners with no standing credentials and per-job secret injection. (M1030 Network Segmentation)
Deployable Playbooks
1 · Disable npm install scripts fleet-wide (CI + dev)
# Per-runner / per-developer npm config set ignore-scripts true # Or enforce per-invocation in pipeline YAML npm ci --ignore-scripts # pnpm / yarn equivalents pnpm config set enable-pre-post-scripts false yarn config set enableScripts false
2 · Detect & purge easy-day-js across all repos (bash)
#!/usr/bin/env bash set -euo pipefail HITS=$(grep -rln "easy-day-js" --include=package-lock.json --include=package.json . || true) if [ -n "$HITS" ]; then echo "[!] easy-day-js referenced in:"; echo "$HITS" echo "[*] Remove the dep, rollback @mastra to pre-2026-06-17, then:" echo " rm -rf node_modules package-lock.json && npm ci --ignore-scripts" fi # Marker sweep (compromise evidence) find "$HOME" /tmp -maxdepth 1 \( -name ".pkg_history" -o -name ".pkg_logs" \) 2>/dev/null
3 · Egress block (iptables example)
iptables -A OUTPUT -d 23.254.164.92 -j DROP iptables -A OUTPUT -d 23.254.164.123 -j DROP # Windows firewall New-NetFirewallRule -DisplayName "Block easy-day-js C2" -Direction Outbound \ -RemoteAddress 23.254.164.92,23.254.164.123 -Action Block
4 · Falcon RTR triage one-liner (Windows)
Get-ChildItem $env:USERPROFILE,$env:TEMP -Force -EA SilentlyContinue |
Where-Object { $_.Name -match '^\.pkg_(history|logs)$' -or $_.Name -match '^[0-9a-f]{24}\.js$' } |
Select-Object FullName,LastWriteTime
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain (Falcon RTR contain) any host with Q1/Q2/Q4 hits; freeze affected CI runners | SOC / IR | aid list, contain timestamps |
| Eradicate | Kill detached stage-2 node process; delete markers + hex .js; remove easy-day-js; rebuild from clean lockfile with --ignore-scripts | IR / DevOps | Process tree, file list |
| Eradicate | Rotate ALL secrets reachable from affected hosts/runners (npm, cloud, LLM keys, wallets) | IR / Platform | Rotation tickets |
| Recover | Rebuild/reimage hosts that ran the malicious install; restore from known-good images | DevOps / IT | Reimage records |
| Recover | Re-enable runners only after egress allowlist + ignore-scripts enforced; monitor Q4/Q5 for re-beacon | SOC | 7-day clean window |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1059.007 / T1554 | postinstall node setup.cjs | Q1 | Yes | Good |
| T1059.007 / T1105 | Detached hex .js stage-2 spawn | Q2 | Yes | Good |
| T1562.001 | NODE_TLS_REJECT_UNAUTHORIZED=0 | Q3 | Yes | Partial |
| T1105 / T1496 | C2 connect (known IPs) | Q4 | Yes | Good |
| T1105 | C2 connect (generalized) | Q5 | No | Partial |
| T1070.004 / T1027 | self-delete + markers | Q6 | No | Partial |
| T1195.002 | easy-day-js on disk | Q7 | No | Partial |
| Registry-side publish | Account hijack / republish | — | No | GAP — SCA/registry tooling only |
| Stage-2 RAT internals | Crypto/credential theft logic | — | No | GAP — no atomic hashes yet |
Behavioral coverage of the install→spawn→C2 chain is strong (Q1/Q2/Q4). Gaps are registry-side publish abuse (needs SCA/SBOM out-of-band) and stage-2 RAT atomic indicators (await vendor hashes — see CSV placeholders). Validate gates in §11 before promoting Q1/Q2/Q4 to Prevent.
Hunt Summary Ticket
TITLE: Mastra npm Supply-Chain (easy-day-js) — install→detached-RAT hunt
SEVERITY: Critical (active supply-chain; 1.1M+ weekly downloads exposed)
SCOPE: Dev endpoints, CI/CD build agents, container build hosts
HYPOTHESIS: A node postinstall (setup.cjs) drops a detached stage-2 node RAT
that beacons to 23.254.164.92/.123 and steals crypto + credentials.
QUERIES: Q1 postinstall setup.cjs | Q2 detached hex .js | Q3 TLS-off
Q4 C2 IPs | Q5 node raw-IP egress | Q6 self-delete/markers
Q7 easy-day-js on disk
DO FIRST: 1) Block 23.254.164.92 + 23.254.164.123 at egress
2) Run Q1/Q2/Q4 fleet-wide; contain any hits
3) grep repos/agents for easy-day-js; ignore-scripts in CI
FINDINGS: <fill: hosts, aids, package versions, secrets exposed>
GAPS: Registry-side publish abuse; stage-2 atomic hashes pending
ACTIONS: Rotate secrets; rollback @mastra pre-2026-06-17; reimage runners
OWNER: <assign SOC/IR lead>
VERSION: v0.1 · 2026-06-18 · HuntPack
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 | Access Date |
|---|---|---|---|
| 1 | The Hacker News — 144 Mastra npm packages compromised | Campaign naming, count, root cause | 2026-06-18 |
| 1 | Endor Labs — Mastra npm org compromised | setup.cjs steps, env var, artifacts, timeline | 2026-06-18 |
| 1 | StepSecurity — easy-day-js technical analysis | spawn signature, C2 IPs/URL, versions, markers | 2026-06-18 |
| 2 | Orca Security — Mastra npm supply-chain attack | RAT/crypto-stealer classification | 2026-06-18 |
| 2 | Aikido — 140+ Mastra packages hit | Blast radius, download counts | 2026-06-18 |