Back to all blogs

|
|
9 min read


TL;DR: On March 31, 2026, attackers compromised the npm account of a core axios maintainer and published two malicious versions containing a cross-platform remote access trojan. Within 89 seconds of publication, the first endpoint was infected. The payload self-deleted after execution, swapped the package manifest to hide its presence, and established persistent backdoors on macOS and Windows. Linux variants left no persistence because they were targeting CI/CD runners — ephemeral machines that already had access to everything the attacker needed. If your pipelines ran a build on March 31 and you haven't audited your secrets and credentials from that window, you have an open question.
The attack in 90 seconds
axios is the most widely used HTTP client in JavaScript. It has approximately 100 million weekly downloads and is a direct or transitive dependency in the majority of production Node.js applications, including most AI-powered apps built on top of OpenAI, Anthropic, and other API providers.
On March 31, attackers gained access to the npm account of jasonsaayman, one of the project's core maintainers. They published two new versions: axios@1.14.1 and axios@0.30.4. Neither showed obvious signs of tampering on first inspection. The malicious payload was not in axios itself — it was in a phantom dependency called plain-crypto-js@4.2.1, which was injected into the package manifest of both versions and did not exist before this incident.
Within 89 seconds of publication, the first Huntress-monitored endpoint was infected. That number reflects automated CI/CD pipelines pulling and installing packages immediately on publish. It is not an edge case — it is the default behavior of most modern build systems.
Both malicious versions were removed from the npm registry within roughly 74 minutes. That window was enough.
What was actually in the package
The setup.js installer used a two-layer obfuscation scheme: the outer layer reversed a Base64-encoded string, and the inner layer applied an XOR cipher with the key "OrDeR_7077". Once decoded, the payload determined the operating system and deployed a platform-specific remote access trojan.
The three variants were distinct codebases, not ports:
On macOS, the dropper installed a compiled C++ binary disguised using Apple-style naming conventions (mimicking com.apple.act.mond) and registered it as a LaunchDaemon to survive reboots. The binary established a persistent connection to the command-and-control server at sfrclak[.]com:8000.
On Windows, the dropper wrote a PowerShell-based RAT with three components: a renamed PowerShell executable (wt.exe) placed in AppData, a persistence entry written to HKCU\Software\Microsoft\Windows\CurrentVersion\Run under the key MicrosoftUpdate, and a startup script (system.bat) for redundant persistence. The C2 beacon used an IE8 User-Agent string as a detection indicator — a deliberate anachronism that no legitimate software on a modern Windows machine would produce.
On Linux, the dropper installed a Python-based RAT but deliberately wrote no persistence mechanism. This was not an oversight. Linux targets in CI/CD pipelines are typically ephemeral runners that spin up, execute a build, and terminate. There is nothing to persist to. The value is in the secrets, tokens, and API credentials that flow through the build during execution — and those were harvested in real time.
After execution, the dropper deleted itself and replaced the package.json with a clean version. Post-mortem forensic inspection of the dependency would show nothing wrong.
This was pre-staged
The attackers did not rush. The compromised maintainer account was used to publish the malicious packages approximately 18 hours after the attack infrastructure was set up. The C2 domain, the platform-specific binaries, and the obfuscated installer were all prepared in advance. The clean-looking version was published first, with the phantom dependency introduced in a subsequent update — reducing the novelty signal that automated scanners use to flag new packages.
The result was that standard supply chain security tools that check for known-malicious packages or scan existing dependencies would have seen nothing on initial install.
Why AI applications were the highest-value targets
axios is a foundational HTTP client. In the context of AI application development, it appears in virtually every stack that communicates with model APIs. An application calling the OpenAI API, the Anthropic API, or any other HTTP-based AI service almost certainly has axios somewhere in its dependency tree.
That dependency structure means the blast radius of this attack skewed heavily toward AI engineering teams: developers building LLM-powered features, data scientists running fine-tuning pipelines, and ML platform teams managing model APIs in production. These are exactly the environments where short-lived CI/CD credentials, API keys, and model access tokens flow through the build process and are available for harvest.
The Linux variant's design choice makes this explicit. No persistence was needed. The goal was secrets exfiltration during the build, not long-term access to the runner. In an AI development pipeline, a single successful harvest during a build that runs with access to production API keys is sufficient.
What the attackers got
Huntress confirmed at least 135 endpoints within their monitored customer base were compromised. That is a floor, not a ceiling — it reflects only the endpoints within Huntress's telemetry window.
Across those environments, the RAT had full remote command execution capability from the moment it established its C2 connection. On Windows and macOS machines, that access persisted after the infection window closed. On CI/CD runners, the value extraction happened during the build.
The specific data harvested varies by environment, but the attack surface on an infected machine included: environment variables (which commonly contain API keys, database credentials, and cloud provider tokens), local credential stores, SSH keys, configuration files, and any secrets available to the build process.
Three places this should have been caught
Most organizations running standard dependency scanning would have missed this attack during the 74-minute window. That is not a failure of vendor execution — it is a structural gap in how supply chain security is typically architected.
The first gap is version monitoring. Legitimate packages do not publish new versions unannounced on a Sunday. An inventory system that tracks the version state of every dependency across your AI applications would have flagged both axios@1.14.1 and axios@0.30.4 as unexpected version changes before installation. Repello AI's AI Inventory continuously monitors your AI application dependencies and generates alerts on version drift — catching exactly this class of attack at the detection layer rather than the remediation layer.
The second gap is pre-production behavioral testing. The phantom dependency plain-crypto-js@4.2.1 had never existed before March 31. Running the dependency in an isolated sandbox environment before CI/CD deployment would have revealed the C2 beacon and the dropper behavior. ARTEMIS includes supply chain testing as part of its attack simulation coverage, executing dependencies in instrumented environments to detect network callouts, filesystem modifications, and persistence attempts before they reach production.
The third gap is runtime credential monitoring. If the attack reached a production or staging environment, the C2 connection and the credential harvest represent runtime events that a behavioral security layer would catch. ARGUS monitors for anomalous outbound connections and credential access patterns in your AI application runtime, providing a detection layer after the dependency boundary has already been crossed.
Supply chain attacks succeed because they exploit the implicit trust organizations extend to installed dependencies. That trust needs to be replaced with continuous verification at three points: inventory (what changed), pre-production testing (what does it do), and runtime monitoring (what is it doing now).
What to do if you ran a build on March 31
If your pipelines ran any Node.js builds between approximately 12:00 and 14:00 UTC on March 31, 2026, treat the affected machines as potentially compromised until you can rule it out.
Check your package-lock.json and npm audit logs for axios@1.14.1 or axios@0.30.4. Check for plain-crypto-js in your installed packages — its presence is conclusive evidence of infection. On Windows, check for wt.exe in AppData and for the MicrosoftUpdate registry key. On macOS, check for com.apple.act.mond in your LaunchDaemons. Inspect network logs for outbound connections to sfrclak[.]com or any beacon using an IE8 User-Agent string.
Rotate all credentials and API keys that were available in the environment during the affected build window. This includes cloud provider tokens, model API keys, database credentials, and any secrets stored in environment variables. If you cannot confirm a machine was not infected, assume it was.
Contact Repello AI if you need help auditing the scope of exposure across your AI application infrastructure — particularly if you need to trace which API credentials and model access tokens were available to affected pipelines.
FAQ
What versions of axios were affected?
The two malicious versions were axios@1.14.1 and axios@0.30.4. Both have been removed from the npm registry. If you are running either of these versions, update immediately and audit the environments where they ran.
How do I know if my build was infected?
Check for plain-crypto-js@4.2.1 in your installed packages — this phantom dependency is the definitive indicator. On Windows, look for wt.exe in AppData\Roaming and a MicrosoftUpdate key in HKCU\Software\Microsoft\Windows\CurrentVersion\Run. On macOS, check for com.apple.act.mond in LaunchDaemons. Review outbound network connections for sfrclak[.]com:8000 or any traffic with an IE8 User-Agent header.
Why did the Linux version have no persistence?
The Linux variant was designed for CI/CD runner environments, which are ephemeral and terminate after each build. Persistence on an ephemeral machine provides no value. The objective on Linux was real-time credential harvest during the build window, not long-term access to the runner itself.
What credentials were at risk?
Any environment variables, API keys, tokens, SSH keys, or secrets accessible to the build process during the infection window. For AI application pipelines, this typically includes model API keys, cloud provider credentials, and database connection strings. Treat all of these as compromised if you cannot rule out infection.
Could standard dependency scanners have caught this?
Not during the 74-minute window. The phantom dependency plain-crypto-js@4.2.1 had no prior existence, so it had no known-malicious signature. Tools that rely on CVE databases or known-bad package lists would have seen nothing. Detection required either version drift monitoring (flagging unexpected new axios versions), behavioral sandbox testing, or runtime anomaly detection.
How was the axios maintainer account compromised?
Attribution points to UNC1069, a North Korean-linked threat actor. The account compromise method has not been publicly confirmed in full technical detail, but credential phishing and session token theft are the most common vectors in npm maintainer account takeovers.
Share this blog
Subscribe to our newsletter











