Mastra npm Supply-Chain Attack — "easy-day-js"

144 @mastra packages backdoored via a typosquat dependency that drops a detached crypto-stealing RAT through an obfuscated postinstall hook.
Threat
Mastra npm Compromise (easy-day-js)
Severity
ACTIVE SUPPLY-CHAIN
Type
Software Supply Chain · npm / Node.js
Access Vector
npm install postinstall
Version
v0.1 · 2026-06-18
Author
HuntPack
Confidence
High (multi-vendor corroborated)
01

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.

02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Fwd
1StepSecurityExact 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
1Endor LabsSix-step setup.cjs behavior; NODE_TLS_REJECT_UNAUTHORIZED=0; home-dir artifacts; missing SLSA provenance; minute-by-minute timelineyes
1StepSecurity / Aikido140+ packages, 88-minute republish window, 1.1M+ weekly downloads blast radiusyes
1The Hacker NewsCampaign naming ("easy-day-js"), 144 packages, account-hijack root cause via stale contributor tokenyes
2Orca SecurityCrypto-stealer RAT classification; credential/API-key harvesting intent of stage-2partial

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.

03

Hunt Brief & Attack Chain

Hunt Hypotheses (fidelity-ordered)

  1. H1 (high): A node process spawns setup.cjs as a child of an npm/npx/CI install — postinstall execution. low FP
  2. H2 (high): A node process spawns a second detached node child running a random-hex-named .js from the home/temp dir shortly after install. low FP
  3. H3 (high): Any process sets NODE_TLS_REJECT_UNAUTHORIZED=0 in its environment/command line. med FP
  4. H4 (high): Outbound connection to C2 23.254.164.92 (:8000) or 23.254.164.123 (:443) from a node process. low FP
  5. H5 (medium): Beacon-marker files .pkg_history / .pkg_logs written to a home/temp directory. med FP
  6. H6 (medium): A .js / .cjs dropper self-deletes (FileDeleteInfo) the same file it was just running from, shortly after a network fetch. med FP
  7. H7 (high): Presence of easy-day-js in node_modules or package-lock.json on any endpoint/build agent. low FP

Attack Chain

StepBehaviorTelemetryHunt Angle
1 · Initial AccessHijacked npm account republishes 142 @mastra pkgs with easy-day-js depn/a (registry-side)SCA / lockfile scan (H7)
2 · Executionnpm install triggers postinstall: node setup.cjs --no-warningsProcessRollup2node → setup.cjs (H1)
3 · Defense EvasionNODE_TLS_REJECT_UNAUTHORIZED=0 set; --no-warningsProcessRollup2 (CommandLine/env)TLS-off env (H3)
4 · Discovery / BeaconMarker files .pkg_history, .pkg_logs writtenFileCreateInfoMarker files (H5)
5 · Ingress Tool TransferStage-2 fetched from https://23.254.164.92:8000/update/49890878NetworkConnectIP4 / DnsRequestC2 connect (H4)
6 · Execution (stage-2)Detached hidden node <hex>.js <stage2C2>ProcessRollup2Detached node child (H2)
7 · Defense Evasionfs.rmSync(__filename) self-deletes setup.cjsFileDeleteInfoSelf-delete (H6)
8 · ImpactCrypto theft + credential/LLM/cloud key harvesting via stage-2 C2 23.254.164.123:443NetworkConnectIP4Stage-2 C2 (H4)
04

Consolidated IOC Table

TypeValueConfActionContext
IPv423.254.164.92 (port 8000)highdetectStage-1 C2; serves stage-2 payload
URLhttps://23.254.164.92:8000/update/49890878highdetectPayload download endpoint
IPv423.254.164.123 (port 443)highdetectStage-2 C2; beacon + exfil sink
npm packageeasy-day-js@1.11.22highdetectWeaponized dropper (typosquat of dayjs)
npm packageeasy-day-js@1.11.21mediumhuntClean decoy; attacker-controlled
npm scope@mastra/* versions published 2026-06-17 01:12–02:39 UTChighhunte.g. @mastra/core@1.42.1, @mastra/memory@1.20.4, mastra@1.13.1
Filenamesetup.cjs (~4.5 KB, self-deleting)highhuntObfuscated postinstall dropper
File marker.pkg_history, .pkg_logsmediumhuntBeacon markers in home/temp dir
File pattern24-hex-char .js in home/temp dirmediumhuntStage-2 payload filename
Env varNODE_TLS_REJECT_UNAUTHORIZED=0mediumhuntTLS-validation disable
npm accountehindero / sergey2016mediumpivotHijacked / attacker publisher identities
SHA256REPLACE_WITH_SHA256 (setup.cjs / stage-2)mediumdetectPopulate when vendor hashes publish
05

Affected Surface & Telemetry Matrix

SurfaceRequired TelemetryPriorityGap Risk
Developer endpoints (mac/Win/Linux)ProcessRollup2, FileCreate/DeleteInfo, NetworkConnectIP4criticalLow — Falcon sensor coverage typical
CI/CD build agentsProcessRollup2, NetworkConnectIP4criticalHigh — ephemeral runners may lack sensor
Container build hostsProcessRollup2 (container-aware), NetworkConnectIP4highHigh — short-lived containers
Network egressNetworkConnectIP4, DnsRequest, proxy/firewall logshighMedium — direct-IP C2 bypasses DNS logging
Artifact / registrySCA / SBOM, lockfile scanhighMedium — needs out-of-band tooling
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
Initial AccessT1195.002 — Supply Chain Compromise: Software Supply ChainHijacked @mastra scope republishes with malicious depH7 / SCA + Q7
ExecutionT1059.007 — JavaScriptnode setup.cjs via npm postinstallQ1
ExecutionT1554 — Compromise Host Software BinaryTrojanized npm packages run on installQ1 / Q7
Command & ControlT1105 — Ingress Tool TransferStage-2 fetched from C2 over unvalidated TLSQ4 / Q5
Defense EvasionT1027 — Obfuscated Files or InformationObfuscated setup.cjs; XOR-encoded markerQ1 / Q6
Defense EvasionT1070.004 — Indicator Removal: File Deletionfs.rmSync(__filename) self-deleteQ6
Defense EvasionT1562.001 — Impair Defenses (TLS validation)NODE_TLS_REJECT_UNAUTHORIZED=0Q3
ImpactT1496 — Resource Hijacking / crypto theftStage-2 RAT steals crypto + credentialsQ4
07

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.

08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · npm postinstall executing setup.cjs
CONF HIGHFP LOWCOST LOW

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)
Q2 · Detached node child running random-hex .js after install
CONF HIGHFP LOWCOST MED

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)
Q3 · Process disabling Node TLS verification
CONF HIGHFP MEDCOST LOW

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)
Q4 · Outbound to known easy-day-js C2 IPs
CONF HIGHFP LOWCOST LOW

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)
Q5 · node connecting outbound right after install (any C2)
CONF MEDFP MEDCOST MED

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)
Q6 · Self-deleting .cjs / beacon-marker files
CONF MEDFP MEDCOST MED

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)
Q7 · easy-day-js present in node_modules / package-lock
CONF HIGHFP MEDCOST MED

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)
09

CrowdStrike Custom IOA Recommendations

IOA NamePatternActionExclusions
npm-postinstall-setup-cjsProcess node, CommandLine contains setup.cjs + --no-warnings, parent npm/npx/yarn/pnpmDetect (High) → Prevent after tuningApproved internal packages with verified setup.cjs path
node-detached-hex-childParent node spawning child node with a 24-hex .js in temp/homeDetect (Critical)Known build toolchains writing hashed temp scripts
node-tls-verify-disabledCommandLine/env contains NODE_TLS_REJECT_UNAUTHORIZED=0Detect (Medium)Baselined legacy internal-cert hosts/parents
easy-day-js-c2NetworkConnect to 23.254.164.92 / 23.254.164.123Prevent (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.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
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
Behavioral Signatureshunt logic
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)
Named Packages / ToolingSCA blocklist
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)
SCA / Egress Audit Checklistremediation
# 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
11

Detection Validation Gates

GateCheckPass Criteria
Telemetry readyProcessRollup2, FileCreate/DeleteInfo, NetworkConnectIP4 flowing from dev + CI hostsAll four event types present in last 24h
Benign baselineRun Q3 & Q5 over 7d; catalog legitimate NODE_TLS_REJECT_UNAUTHORIZED=0 and node-IP egressKnown-good list built; exclusions added
Positive testIn a sandbox, create a benign setup.cjs + a temp node-spawn-detached chainQ1 & Q2 fire on the test artifacts
C2 testSinkhole-route 23.254.164.92 in lab; confirm Q4 firesQ4 returns the test host
PromotionQ1/Q2/Q4 FP-clean for 7dPromote to Custom IOA / Prevent
12

Hardening — Tiered

Immediate (this week)
  • Block C2: deny 23.254.164.92 and 23.254.164.123 at firewall/proxy egress. (M1037 Filter Network Traffic)
  • Disable install scripts in CI: set npm config set ignore-scripts true on 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)
Near term (1–4 weeks)
  • 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-scripts visibility 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)
Strategic (1–3 months)
  • 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)
13

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
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-contain (Falcon RTR contain) any host with Q1/Q2/Q4 hits; freeze affected CI runnersSOC / IRaid list, contain timestamps
EradicateKill detached stage-2 node process; delete markers + hex .js; remove easy-day-js; rebuild from clean lockfile with --ignore-scriptsIR / DevOpsProcess tree, file list
EradicateRotate ALL secrets reachable from affected hosts/runners (npm, cloud, LLM keys, wallets)IR / PlatformRotation tickets
RecoverRebuild/reimage hosts that ran the malicious install; restore from known-good imagesDevOps / ITReimage records
RecoverRe-enable runners only after egress allowlist + ignore-scripts enforced; monitor Q4/Q5 for re-beaconSOC7-day clean window
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1059.007 / T1554postinstall node setup.cjsQ1YesGood
T1059.007 / T1105Detached hex .js stage-2 spawnQ2YesGood
T1562.001NODE_TLS_REJECT_UNAUTHORIZED=0Q3YesPartial
T1105 / T1496C2 connect (known IPs)Q4YesGood
T1105C2 connect (generalized)Q5NoPartial
T1070.004 / T1027self-delete + markersQ6NoPartial
T1195.002easy-day-js on diskQ7NoPartial
Registry-side publishAccount hijack / republishNoGAP — SCA/registry tooling only
Stage-2 RAT internalsCrypto/credential theft logicNoGAP — 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.

16

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
17

Changelog

v0.32026-07-24CQL correctness pass. All 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.
v0.22026-06-29CQL syntax review (crowdstrike-logscale-v3): regex-on-numeric RemotePort→in() (Q5), removed invalid FileCreateInfo (Q6), replaced sole FileCreateInfo with valid file-write events (Q7).
v0.12026-06-18Initial HuntPack. 7 CQL queries (3 IOA/Prevent candidates), behavioral + C2 IOC appendix, tiered hardening, deployable playbooks, containment runbook. Built from multi-vendor intel (StepSecurity, Endor Labs, Aikido, The Hacker News, Orca).
18

References

TierSourceUsed ForAccess Date
1The Hacker News — 144 Mastra npm packages compromisedCampaign naming, count, root cause2026-06-18
1Endor Labs — Mastra npm org compromisedsetup.cjs steps, env var, artifacts, timeline2026-06-18
1StepSecurity — easy-day-js technical analysisspawn signature, C2 IPs/URL, versions, markers2026-06-18
2Orca Security — Mastra npm supply-chain attackRAT/crypto-stealer classification2026-06-18
2Aikido — 140+ Mastra packages hitBlast radius, download counts2026-06-18