From package to postinstall payload: Inside the Mastra npm supply chain compromise
Microsoft SecurityArchived Jun 18, 2026✓ Full text saved
A poisoned npm package infected 140+ projects with a hidden payload. This report highlights how to detect, hunt, and defend against supply chain attacks using Microsoft Defender and actionable threat intelligence. The post From package to postinstall payload: Inside the Mastra npm supply chain compromise appeared first on Microsoft Security Blog .
Full text archived locally
✦ AI Summary· Claude Sonnet
Share
Link copied to clipboard!
TAGS
Malware
npm
CONTENT TYPES
Research
PRODUCTS AND SERVICES
Microsoft Defender
TOPICS
Actionable threat insights
Microsoft Threat Intelligence observed a large-scale npm supply chain attack affecting 140+ packages across the mastra and @mastra scopes on the npm registry. Microsoft shared its findings with the npm security team, and the compromised packages have been removed and the attacker’s publish access to the @mastra scope has been revoked. The compromise originated from the takeover of the ehindero npm maintainer account, which had publish rights across the Mastra ecosystem and was used to publish poisoned package versions that introduced easy-day-js, a malicious typosquat of the popular dayjs library.
Once installed, easy-day-js triggered a postinstall hook that executed an obfuscated dropper script, disabled Transport Layer Security (TLS) certificate verification, contacted attacker-controlled command-and-control (C2) infrastructure, downloaded a second-stage payload, and executed the payload as a detached hidden process. The activity followed a coordinated staged delivery pattern, with a clean bait version published first, followed by a weaponized version and rapid publication of the compromised Mastra packages.
Because the payload executes during installation, any developer workstation or continuous integration and continuous delivery (CI/CD) pipeline that ran npm install or npm update after the compromised versions were published was potentially exposed, regardless of whether the package was imported in application code. This created risk to credentials, tokens, build environments, and downstream software integrity. Microsoft Defender Antivirus, Microsoft Defender for Endpoint, and Microsoft Defender XDR provide detections and hunting coverage for suspicious Node.js execution, malicious package behavior, reflective code loading, persistence activity and command-and-control communication.
Attack chain overview
Figure 1. End-to-end attack chain from npm account takeover through mass dependency injection to second-stage payload execution.
At a high level, the attack progressed through six phases:
Account compromise: The attacker gained control of the ehindero npm account , a listed maintainer with publish rights across the entire @mastra scope.
Typosquat creation: The attacker published easy-day-js, a package impersonating the legitimate dayjs library (57M+ weekly downloads), using a coordinating anonymous email account ).
Mass poisoning: Using the compromised account, the attacker published new versions of 140+packages across the @mastra scope, each injected with easy-day-js@^1.11.21 as a new dependency. All poisoned versions were tagged as latest.
Delivery: Developers and CI/CD pipelines running npm install automatically resolved to the compromised versions. The semantic versioning (SemVer) range ^1.11.21 resolved to 1.11.22, the version containing the malicious postinstall hook.
Execution: The postinstall hook executed an obfuscated 4,572-byte dropper that disabled TLS verification, dropped tracking markers, and contacted the C2 server.
Second-stage payload: The dropper fetched executable code from the C2 server, wrote it as a randomly named .js file, and spawned it as a fully detached, window-hidden Node.js process.
Discovery and initial indicators
Microsoft Threat Intelligence identified the compromise through anomalous publishing patterns on the mastra package. All previous versions of mastra (through v1.13.0) were published through GitHub Actions OpenID Connect (OIDC), the legitimate CI/CD pipeline. Version 1.13.1 was manually published by ehindero using a Tutamail address, an anonymous email service.
Figure 2. Publisher comparison across mastra versions showing the anomalous manual publish on v1.13.1.
The only change between mastra@1.13.0 and mastra@1.13.1 was the addition of easy-day-js@^1.11.21 as a dependency. No corresponding code changes were present in the Mastra GitHub repository. Both the compromised publisher (ehindero2016@tutamail.com) and the typosquat publisher (sergey2016@tutamail.com) used the same anonymous email provider, Tutamail.
Dependency injection: the poisoned package.json
The compromised mastra@1.13.1 package.json reveals the injected dependency alongside the anomalous publisher metadata:
Figure 3. The compromised mastra@1.13.1 package.json with the injected easy-day-js dependency and the anomalous npm publisher.
The easy-day-js dependency was not present in any prior versions of mastra npm packages. Its addition, paired with the SemVer range ^1.11.21, ensures that the npm resolves to the weaponized 1.11.22 release.
Typosquat analysis: easy-day-js
The easy-day-js package is a deliberate impersonation of the legitimate dayjs library:
Attribute Legitimate dayjs Malicious easy-day-js
Maintainer iamkun <kunhello@outlook[.]com> sergey2016 <sergey2016@tutamail[.]com>
Claimed author iamkun iamkun (impersonated)
Repository URL github.com/iamkun/dayjs github.com/iamkun/dayjs (copied)
Weekly downloads 57,251,792 newly created
Version count 89+ versions since 2018 2 versions (both June 16, 2026)
postinstall script None node setup.cjs –no-warnings (v1.11.22)
Staged delivery pattern
The typosquat used a two-phase delivery strategy:
Phase 1 (clean bait): easy-day-js@1.11.21 was published at 07:05 UTC on June 16, 2026. This version contained only legitimate dayjs code with no postinstall hook.
Phase 2 (weaponization): easy-day-js@1.11.22 was published at 01:01 UTC on June 17, 2026, adding the setup.cjs payload and the postinstall hook. The dayjs.min.js file is byte-identical between both versions, confirming only the dropper was added.
The weaponized package.json in version 1.11.22 exposes the postinstall hook:
Figure 4. The weaponized easy-day-js@1.11.22 package.json. The postinstall hook runs setup.cjs automatically on npm install.
Obfuscation and payload analysis
Stage 0: Obfuscated dropper (setup.cjs)
The setup.cjs payload is protected with JavaScript obfuscation using rotated string arrays and a custom base64 decoder function:
Figure 5. The obfuscated setup.cjs dropper with rotated string array and base64 encoded string lookups.
The obfuscation technique uses a common pattern: an array of 40 Base64-encoded strings is shuffled at initialization using a numeric seed (0x4c11d), then accessed through a decoder function that performs Base64 decoding with character substitution. This prevents static analysis tools from extracting meaningful strings.
Stage 1: String table decryption
Decoding the rotated string array reveals the payload’s true capabilities:
Figure 6. The decoded string table revealing C2 addresses, file system operations, and process spawning functionality.
Key decoded strings include the secondary C2 address (23.254.164[.]123:443), Node.js built-in module references (node:child_process, node:os), and file system operations (writeFileSync, rmSync).
Stage 2: Deobfuscated payload logic
After resolving all string references and control flow, the full payload logic emerges as a five-step attack sequence:
Figure 7. The fully deobfuscated setup.cjs payload showing the five-step attack sequence from.
TLS bypass to self-deletion
Step 1: Disable TLS verification. The payload sets NODE_TLS_REJECT_UNAUTHORIZED to ‘0’, disabling certificate validation for all HTTPS requests in the Node.js process. This enables communication with the C2 server without valid certificates.
Step 2: Drop filesystem markers. Two tracking files are written to the OS temp directory: $TMPDIR/.pkg_history contains the install path of the compromised package, and $TMPDIR/.pkg_logs contains the package name encoded with XOR 0x80:
Figure 8. XOR 0x80 decoding of the .pkg_logs marker reveals the string easy-day-js.
Step 3: Fetch second-stage payload. The dropper issues a GET request to hxxps://23.254.164[.]92:8000/update/49890878 and reads the response body as text.
The second-stage payload is a ~41 KB cross-platform Node.js tasking client. Unlike a fire-and-forget stealer, the implant installs sign-in persistence, sends a Start beacon to the C2, then enters a repeated Check poll loop. Tasks returned by the server are dispatched to built-in runners (a Node runner and a Shell runner), and it honors configuration update and exit commands, meaning the operator can push and execute arbitrary follow-on code on the host at any time. On Windows, the payload additionally executes reflective .NET assembly injection for in-memory code execution.
Step 3.A: Windows execution chain. On Windows, the payload performs host reconnaissance and reflective in-memory code execution before establishing persistence.
The payload enumerates all installed applications across three sources—Start Menu entries (Get-StartApps), registry Uninstall keys, and UWP packages (Get-AppxPackage)—to fingerprint the compromised host:
Each enumeration is wrapped in try/catch with silent error handling. The deduplicated results are exfiltrated back to the C2 for victim profiling, enabling the attacker to identify installed security products and high-value targets.
A second PowerShell script receives two C2 endpoint URLs through the SCRIPT_ARGS environment variable. It disables SSL certificate validation and defines an HTTP POST function that Base64-encodes request bodies using a legacy IE8 User-Agent string:
The first C2 request downloads a .NET DLL that is loaded directly into memory via reflection, completely bypassing disk-based detection. The script resolves the Extension.SubRoutine class and invokes its Run2 method with a second downloaded payload, the path to cmd.exe, and the C2 callback address:
This pattern is consistent with process injection, where the payload is injected into a cmd.exe process that communicates back to the C2 over HTTPS (port 443). The entire chain is fileless—no artifacts are written to disk.
Step 3.B: Cross-platform persistence. The implant installs login persistence on all three major operating systems, using a consistent NVM/Node masquerade theme across platforms:
OS Persistence mechanism Drop location Artifact name
Windows Registry Run key
(HKCU\…\CurrentVersion\Run) C:\ProgramData\NodePackages\ NvmProtocal
macOS LaunchAgent
(RunAtLoad) ~/Library/NodePackages/ com.nvm.protocal.plist
Linux systemd user unit
(WantedBy=default.target) ~/.config/systemd/nvmconf/ nvmconf.service
On Windows, the Run key launches a hidden PowerShell process that invokes Node.js:
On Linux, the systemd user unit restarts the implant on failure with a 5-second delay:
All three persistence paths drop the implant as protocal.cjs (a deliberate misspelling) into directories named to mimic legitimate Node.js installations. The value name NvmProtocal, the macOS label com.nvm.protocal, and the Linux unit nvmconf.service are deliberately designed to blend into a developer workstation.
Step 3.C: Collection and exfiltration. The implant performs the following collection before exfiltrating to the C2:
Cryptocurrency wallet inventory: A hardcoded list of 166 wallet browser-extension IDs (MetaMask, Phantom, Coinbase Wallet, Binance Wallet, TronLink, and others) is matched against installed extensions across Chrome, Edge, and Brave profiles.
Browser history: Each profile’s History SQLite database is copied to a temp directory prefixed with browser-hist- and queried through node:sqlite.
Host reconnaissance: Gather hostname, architecture, platform, user ID, installed applications, and running processes.
Collected data is exfiltrated using a custom ICAP-style protocol over HTTPS POST (reqmod, PrimaryUrl, SecondaryUrl headers), with hostnames resolved through node:dns and traffic carrying a spoofed legacy IE8 User-Agent string.
Step 4: Writing and executing the payload. The downloaded code is written to a file with a cryptographically random name (<12 random hex bytes>.js) in the OS temp directory, then spawned as a detached, window-hidden Node.js process using child_process.spawn with unref().
Step 5: Self-deletion. The dropper removes itself (fs.rmSync(__filename)) to eliminate forensic evidence from the installed package directory.
Timeline analysis
Every package published by the ehindero account contained easy-day-js as an injected dependency. Packages last published by GitHub Actions CI/CD or other legitimate maintainers were not affected.
Attack timeline
Timestamp (UTC) Event
June 16, 07:05 easy-day-js@1.11.21 published (clean bait, no payload)
June 17, 01:01 easy-day-js@1.11.22 published (adds postinstall with setup.cjs)
June 17, 01:20 mastra@1.13.1 and 140+ other @mastra/* packages published with easy-day-js dependency
** Microsoft Threat Intelligence monitoring observed easy-day-js@1.11.22 at 01:07 UTC and mastra@1.13.1 at 01:28 UTC on June 17, 2026
Mitigation and protection guidance
Microsoft recommends the following mitigations to reduce the impact of this threat:
Review dependency trees for direct or transitive usage of affected @mastra packages at the compromised versions listed above.
Check for the presence of easy-day-js in node_modules/ or package-lock.json files across your projects and CI/CD environments.
Pin known-good package versions where possible. For mastra, version 1.13.0 and earlier are unaffected. For @mastra/core, version 1.42.0 and earlier are unaffected.
Run npm install with –ignore-scripts to prevent automatic execution of postinstall hooks during dependency installation.
Check systems for indicators of compromise (IOC) artifacts: Look for $TMPDIR/.pkg_history, $TMPDIR/.pkg_logs, and unexpected .js files in the user’s home or temp directories.
Rotate any credentials, tokens, or API keys that may have been present on systems where the compromised packages were installed.
Block the C2 IP addresses 23.254.164[.]92 and 23.254.164[.]123 at the network perimeter.
Audit CI/CD logs for unexpected outbound connections to the C2 IP addresses or suspicious postinstall script execution.
Enable cloud-delivered protection in Microsoft Defender Antivirus or equivalent antivirus protection.
Microsoft Defender XDR detections
Microsoft Defender XDR customers can refer to the list of applicable detections below. Microsoft Defender XDR coordinates detection, prevention, investigation, and response across endpoints, identities, email, and apps to provide integrated protection against attacks like the threat discussed in this blog.
Tactic Observed activity Microsoft Defender coverage
Initial access Suspicious script execution during npm install or package lifecycle activity Microsoft Defender Antivirus – Trojan:JS/NpmStealz.Z!MTB
– Trojan:JS/NpmStealz.ZA!MTB
Microsoft Defender for Endpoint
– Suspicious Node.js process behavior
– Suspicious Node.js script execution
Execution
( Stage 1 ) Postinstall hook automatically executes obfuscated setup.cjs dropper (4,572 bytes) during npm install; Microsoft Defender for Endpoint
– Suspicious Node.js process behavior
– Suspicious Node.js script execution
Execution / Defense evasion
(Stage 2) Second-stage payload: Reflective .NET assembly injection: PowerShell downloads DLL, loads via [Reflection.Assembly]::Load(), invokes Extension.SubRoutine.Run2 method to inject payload into cmd.exe process; entire chain is fileless Microsoft Defender Antivirus
Trojan:JS/NpmSteal.DB!MTB
Trojan:PowerShell/PsExec.DE!MTB
Microsoft Defender for Endpoint
-Process loaded suspicious .NET assembly
-A process was injected with potentially malicious code
-Reflective code loading (Fileless In-Memory Execution)
Microsoft Defender for Cloud
-Possible AI Tools Reconnaissance Detected
-Possible Secret Reconnaissance Detected
-Access to cloud metadata service detected
-Possible Post-Compromise Activity Detected in CICD Runner
Persistence Registry Run key created, executing hidden PowerShell that launches protocal.cjs on every user login Microsoft Defender for Endpoint
– Anomaly detected in ASEP registry
Command and control GET request to hxxps://23.254.164[.]92:8000/update/49890878 and reads the response body as text. Microsoft Defender for Endpoint
– Command-line process communicating with malicious network endpoint
Microsoft Security Copilot
Security Copilot customers can use the standalone experience to create their own prompts or run the following prebuilt promptbooks to automate incident response or investigation tasks related to this threat:
Incident investigation
Microsoft User analysis
Threat actor profile
Threat Intelligence 360 report based on MDTI article
Vulnerability impact assessment
Note that some promptbooks require access to plugins for Microsoft products such as Microsoft Defender XDR or Microsoft Sentinel.
Advanced hunting
The following KQL queries can be used in Microsoft Defender XDR Advanced Hunting to identify potential exposure to this supply chain compromise.
Detect postinstall execution of setup.cjs
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in ("node", "node.exe")
| where ProcessCommandLine has "setup.cjs"
or ProcessCommandLine has "easy-day-js"
| where ProcessCommandLine has “--no-warnings”
| project Timestamp, DeviceName, AccountName,
ProcessCommandLine, FolderPath, InitiatingProcessFileName
| sort by Timestamp desc
Outbound connections to C2 infrastructure
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("23.254.164.92", "23.254.164.123")
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc
Indicators of compromise (IOC)
Network indicators
Indicator Type Description
23.254.164.92 IP address Primary C2 server
23.254.164.123 IP address Secondary C2 address (from deobfuscated strings)
https[:]//23[.]254[.]164[.]92:8000/update/49890878 URL Payload download endpoint
File indicators
Indicator Type Description
B122A9873BEDF145AE2A7FD024B5F309007DBB025149F4DC4AC3F7E4F32A36A4 SHA256 setup.cjs (malicious postinstall dropper)
AE70DD4F6BC0D1C8C2848E4E6B51934626C4818DCB5AF99D080DDBD7DC337185 SHA256 easy-day-js-1.11.22.tgz (weaponized tarball)
4A8860240E4231C3A74C81949BE655A28E096A7D72F38FBE84E5B37636B98417 SHA256 easy-day-js-1.11.21.tgz (clean bait tarball)
B73DE25C053C3225A077738A1FCBD9CA6966D7B3CD6F5494A30F0AA0EAE55C7E SHA256 mastra-1.13.1.tgz (compromised CLI tarball)
221c45a790dec2a296af57969e1165a16f8f49733aeab64c0bbd768d9943badf SHA256 protocol.cjs
Host indicators
Indicator Type Description
$TMPDIR/.pkg_history File artifact Contains the install path of the compromised package
$TMPDIR /.pkg_logs File artifact Contains XOR 0x80 encoded string “easy-day-js”
<homedir>/<random_hex>.js File artifact Downloaded second-stage payload
Package indicators
Indicator Type Description
easy-day-js npm package Malicious typosquat of dayjs
sergey2016 npm account Publisher of easy-day-js
ehindero npm account Compromised publisher of 140+ Mastra packages
References
Security: mastra@1.13.1 is compromised — malicious postinstall payload via `easy-day-js` dependency · Issue #18046 · mastra-ai/mastra
Microsoft has identified a supply chain attack on the Mastra-AI npm ecosystem, with 80+ packages compromised through npm account takeover. The attacker introduced a phantom dependency into the… | Microsoft Threat Intelligence
This research is provided by Microsoft Defender Security Research, Suriyaraj Natarajan, Sagar Patil, Rajesh Kumar Natarajan, Mahesh Mandava, Arvind Gowda, and with contributions from members of Microsoft Threat Intelligence.
Learn more
For the latest security research from the Microsoft Threat Intelligence community, check out the Microsoft Threat Intelligence Blog.
To get notified about new publications and to join discussions on social media, follow us on LinkedIn, X (formerly Twitter), and Bluesky.
To hear stories and insights from the Microsoft Threat Intelligence community about the ever-evolving threat landscape, listen to the Microsoft Threat Intelligence podcast.
Review our documentation to learn more about our real-time protection capabilities and see how to enable them within your organization.
Explore how to build and customize agents with Copilot Studio Agent Builder
Microsoft 365 Copilot AI security documentation
How Microsoft discovers and mitigates evolving attacks against AI guardrails
Learn more about securing Copilot Studio agents with Microsoft Defender
Evaluate your AI readiness with our latest Zero Trust for AI workshop.
Learn more about Protect your agents in real-time during runtime (Preview)
Microsoft Defender Security Research Team
See Microsoft Defender Security Research Team posts
Related posts
June 17
Crypto Clipper uses Tor and worm-like propagation for persistence and control
Microsoft Threat Intelligence analyzed a cryptocurrency clipper campaign that combines clipboard theft, wallet replacement, Tor-based communications, and worm-like propagation.
June 9
Reconstructing AI activity in investigations
Learn how to investigate AI activity in Microsoft 365 Copilot and Azure AI services using a structured, telemetry-driven approach.
June 8
AI brands as bait: How threat actors are using the AI hype in social engineering
As threat actors operationalize AI to accelerate attacks, they are also leveraging the wider global interest around AI itself as a social engineering lure.