Compromised Legitimate npm Release: axios 1.14.1 and 0.30.4
WindowsmacOSLinux
IOC Hunt high Supply ChainSapphire Sleet
A routine dependency bump that resolved to axios 1.14.1 or 0.30.4, whose added dependency plain-crypto-js dropped a cross-platform remote-access tool from sfrclak.com:8000, as OpenSourceMalware documents and reports Google and Microsoft attributing to a North Korea-nexus actor.
What this hunt looks for
On 31 March 2026 two releases of axios, 1.14.1 and 0.30.4, were published from its lead maintainer's hijacked account, without OIDC provenance. Each added one dependency, plain-crypto-js, whose postinstall hook ran node setup.js, decoded strings with the key OrDeR_7077, contacted sfrclak.com:8000, fetched a platform-specific remote-access tool and deleted itself. The releases were live for about three hours.
OpenSourceMalware's May 2026 follow-up reports that Google Threat Intelligence Group attributed the compromise to UNC1069, a financially motivated North Korea-nexus actor that Microsoft tracks as Sapphire Sleet. Three further packages, redeem-onchain-sdk@1.0.7, nicegui@0.1.4 and period-newline@0.1.0, share the OrDeR_7077 key and are assessed as the same operator's second wave.
Nobody chose a suspicious package here: an unpinned range or lockfile refresh resolved to a malicious release of a package the project already trusted. The hunt therefore keys on the exact versions, on the command lines that name the dropped payloads, and on the files, Run value and connections it left behind.
- Technique
- Compromise of a software dependency through a hijacked maintainer account (MITRE ATT&CK T1195.001), executing a postinstall JavaScript dropper (T1059.007) that installs a remote-access tool persisting through a Registry Run key (T1547.001) and deletes its own installer (T1070.004).
- Data sources
- Package inventory, EDR process telemetry, File-write telemetry, Registry telemetry, EDR network telemetry, Proxy logs
- Last updated
- 2026-10-04
Indicators
The atomic values this hunt matches on, listed separately from the queries so they can be lifted into tooling that speaks none of the query languages below. Every value here is reported by one of the sources cited at the foot of this page.
| Type | Value | Note |
|---|---|---|
| npm package | axios@1.14.1, axios@0.30.4 |
Malicious releases of the legitimate package, published from the npm CLI without provenance on 2026-03-31 (OpenSourceMalware). The package name identifies the victim; only these versions are indicators. |
| npm package | plain-crypto-js@4.2.0, plain-crypto-js@4.2.1 |
Attacker-created dependency; 4.2.1 is the version the malicious axios releases required (OpenSourceMalware). |
| Command | node setup.js |
plain-crypto-js postinstall hook (OpenSourceMalware). |
| String | OrDeR_7077 |
XOR key used to decode the dropper's strings, and shared by the second-wave packages (OpenSourceMalware). |
| Domain | sfrclak.com |
Command-and-control domain (OpenSourceMalware). |
| IP | 142.11.206.73:8000 |
Command-and-control server over plain HTTP, request path /6202033 (OpenSourceMalware). |
| User agent | mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) |
User agent on the C2 requests (OpenSourceMalware). |
| File path (Windows) | %PROGRAMDATA%\system.bat, %PROGRAMDATA%\wt.exe, %TEMP%\6202033.vbs, %TEMP%\6202033.ps1 |
Windows artefacts (OpenSourceMalware). |
| Registry value | HKCU\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftUpdate |
Run value pointing at the hidden system.bat (OpenSourceMalware). |
| File path (macOS) | /Library/Caches/com.apple.act.mond |
macOS remote-access tool (OpenSourceMalware). |
| File path (Linux) | /tmp/ld.py |
Linux remote-access tool (OpenSourceMalware). |
| npm package | redeem-onchain-sdk@1.0.7, nicegui@0.1.4, period-newline@0.1.0 |
Second-wave packages OpenSourceMalware attributes to the same operator on the shared XOR key. |
| IP | 18.208.244.120:9999 |
Second-wave C2 over a custom TCP protocol (OpenSourceMalware). |
| File path | dist/index5_test.js, /tmp/.redeem_err.log |
Second-wave payload and error log (OpenSourceMalware). |
Logic
Every query below implements these steps, so the same hunt returns the same result in each product.
- Collect: process starts, file and registry writes and network connections on Windows, macOS and Linux; npm packages via osquery
- Match: a command line names a payload or the C2 (6202033, ProgramData system.bat, com.apple.act.mond, /tmp/ld.py, sfrclak)
- Match: or a payload file or the plain-crypto-js folder is written, or a Run value MicrosoftUpdate or naming system.bat is set
- Match: or a connection to sfrclak.com or to a published server address, 142.11.206.73 or 18.208.244.120 (addresses rotate)
- Match: or an installed package is axios 1.14.1 or 0.30.4, plain-crypto-js or a second-wave package version (osquery)
- Report: devices with the signal kinds seen, first and last time, newest first
Queries
Defender / Sentinel KQL
Written for this site, from indicators reported by OpenSourceMalwareOne row per device across process, file, registry and network telemetry from 2026-03-25, where a Run value, the hidden batch file or the command server is the strongest hit.
// Compromised axios 1.14.1 and 0.30.4 and the second wave: process, file, registry and network signals, reported per device
// The window starts at 2026-03-25 so it still reaches the 2026-03-31 incident
union
(DeviceProcessEvents
| where Timestamp >= datetime(2026-03-25)
| where ProcessCommandLine contains "6202033"
or ProcessCommandLine contains "com.apple.act.mond"
or ProcessCommandLine contains "/tmp/ld.py"
or ProcessCommandLine contains "sfrclak"
or ProcessCommandLine contains ".redeem_err.log"
or (ProcessCommandLine contains "ProgramData" and ProcessCommandLine contains "system.bat")
// optional: setup.js is a common name and some legitimate packages spawn PowerShell during install
or (InitiatingProcessFileName in~ ("node.exe", "node") and InitiatingProcessCommandLine contains "setup.js" and FileName in~ ("cscript.exe", "wscript.exe", "powershell.exe", "cmd.exe", "curl.exe", "osascript", "curl", "python3", "sh", "bash"))
| project Timestamp, DeviceName, Signal = "process", Detail = ProcessCommandLine),
(DeviceFileEvents
| where Timestamp >= datetime(2026-03-25)
| where (FolderPath contains "ProgramData" and FileName in~ ("system.bat", "wt.exe"))
or FileName in~ ("6202033.vbs", "6202033.ps1", "index5_test.js", ".redeem_err.log")
or (FolderPath =~ "/Library/Caches/com.apple.act.mond" or FolderPath =~ "/tmp/ld.py")
or FolderPath matches regex @"(?i)[/\\]node_modules[/\\]plain-crypto-js[/\\]"
| project Timestamp, DeviceName, Signal = "file", Detail = FolderPath),
(DeviceRegistryEvents
| where Timestamp >= datetime(2026-03-25)
| where RegistryKey contains @"\CurrentVersion\Run"
| where RegistryValueName =~ "MicrosoftUpdate" or RegistryValueData contains "system.bat"
| project Timestamp, DeviceName, Signal = "registry", Detail = strcat(RegistryValueName, " = ", RegistryValueData)),
(DeviceNetworkEvents
| where Timestamp >= datetime(2026-03-25)
| where RemoteUrl has "sfrclak.com"
// optional: server addresses, they rotate
or RemoteIP in ("142.11.206.73", "18.208.244.120")
| project Timestamp, DeviceName, Signal = "network", Detail = strcat(RemoteUrl, " ", RemoteIP, ":", tostring(RemotePort)))
| summarize Signals = make_set(Signal), Events = count(), Examples = make_set(Detail, 5), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName
| sort by LastSeen desc
Lower-fidelity parts (2)
- the node setup.js spawn clause — setup.js is a common name and some legitimate packages spawn PowerShell during install. Remove: delete the `// optional: setup.js` line and the `or (InitiatingProcessFileName in~ ... "setup.js" ...` line below it.
- the two server addresses 142.11.206.73 and 18.208.244.120 — they are specific to this operator's servers and rotate. Remove: delete the `// optional: server addresses` line and the `or RemoteIP in (...)` line below it.
Splunk SPL
Written for this site, from indicators reported by OpenSourceMalwareRaw EDR and proxy events from 2026-03-25 with the Endpoint fields, one row per device, labelled by signal.
```index=endpoint holds EDR process, file, registry and network events, index=proxy holds proxy events carrying http_user_agent```
(index=endpoint OR index=proxy) earliest="03/25/2026:00:00:00"
| eval cmd=replace(lower(process), "\\\\", "/"), fname=lower(file_name), path=replace(lower(file_path), "\\\\", "/"), reg=replace(lower(registry_path), "\\\\", "/"), web=lower(coalesce(url, query))
| eval signal=case(
match(cmd, "6202033|com\\.apple\\.act\\.mond|/tmp/ld\\.py|sfrclak|\\.redeem_err\\.log") OR (match(cmd, "programdata") AND match(cmd, "system\\.bat")), "process",
match(path, "programdata") AND in(fname, "system.bat", "wt.exe"), "file",
in(fname, "6202033.vbs", "6202033.ps1", "index5_test.js", ".redeem_err.log") OR in(path, "/library/caches/com.apple.act.mond", "/tmp/ld.py") OR match(path, "/node_modules/plain-crypto-js/"), "file",
match(reg, "/currentversion/run") AND (lower(registry_value_name)="microsoftupdate" OR match(lower(registry_value_data), "system\\.bat")), "registry",
match(web, "sfrclak\\.com"), "network",
```optional: server addresses, they rotate```
in(dest_ip, "142.11.206.73", "18.208.244.120"), "network",
```optional: the old Internet Explorer 8 user agent is shared with other tools```
lower(http_user_agent)="mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)", "user-agent",
true(), null())
| where isnotnull(signal)
| eval detail=coalesce(path, process, registry_value_data, web, http_user_agent), device=if(signal="user-agent", src_ip, host)
| stats count AS events values(signal) AS signals values(detail) AS examples min(_time) AS first_seen max(_time) AS last_seen BY device
| convert ctime(first_seen) ctime(last_seen)
| sort - last_seen
Lower-fidelity parts (2)
- the two server addresses 142.11.206.73 and 18.208.244.120 — they are specific to this operator's servers and rotate. Remove: delete the ```optional: server addresses``` comment and the `in(dest_ip, ...)` line below it.
- the Internet Explorer 8 user agent — an old user agent that other tools also send. Remove: delete the ```optional: the old Internet Explorer 8``` comment and the `lower(http_user_agent)=...` line below it.
Sigma
Written for this site, from indicators reported by OpenSourceMalwareOne rule per telemetry type because a Sigma rule binds to one logsource, with the Run-value rule written for the registry_set category.
title: Compromised axios Release Payload Command Line
id: cb47b078-b8ce-40bd-b63b-c193ec2e847e
status: experimental
description: >
A command line that names a payload or command server of the compromised
axios releases and their second wave, and node running setup.js to start a
script host, PowerShell or downloader as the plain-crypto-js postinstall hook
does, as OpenSourceMalware documents.
references:
- https://opensourcemalware.com/blog/axios-compromise-transitive-dependency
- https://opensourcemalware.com/blog/axios-attacker-strikes-again
logsource:
category: process_creation
detection:
selection_cmd:
CommandLine|contains:
- '6202033'
- 'com.apple.act.mond'
- '/tmp/ld.py'
- 'sfrclak'
- '.redeem_err.log'
selection_batch:
CommandLine|contains|all:
- 'ProgramData'
- 'system.bat'
# optional: setup.js is a common name and some legitimate packages spawn PowerShell during install
selection_setup:
ParentImage|endswith:
- '\node.exe'
- '/node'
ParentCommandLine|contains: 'setup.js'
Image|endswith:
- '\cscript.exe'
- '\wscript.exe'
- '\powershell.exe'
- '\cmd.exe'
- '\curl.exe'
- '/osascript'
- '/curl'
- '/python3'
- '/sh'
- '/bash'
condition: 1 of selection_*
falsepositives:
- Packages whose legitimate setup script shells out during install
level: high
---
title: Compromised axios Release Files Dropped
id: 8d2f6a14-3b97-5c0e-a1d8-5e7f9b2c4a60
status: experimental
description: >
Files the compromised axios releases and the second-wave packages drop: the
hidden batch file and wt.exe in ProgramData, the 6202033 script files, the
macOS and Linux payloads, the second wave's payload and error log, and any file
inside the plain-crypto-js dependency.
references:
- https://opensourcemalware.com/blog/axios-compromise-transitive-dependency
- https://opensourcemalware.com/blog/axios-attacker-strikes-again
logsource:
category: file_event
detection:
selection_programdata:
TargetFilename|endswith:
- '\ProgramData\system.bat'
- '\ProgramData\wt.exe'
selection_scripts:
TargetFilename|re: '(?i)[\\/](6202033\.(vbs|ps1)|index5_test\.js|\.redeem_err\.log)$'
selection_unix:
TargetFilename:
- '/Library/Caches/com.apple.act.mond'
- '/tmp/ld.py'
selection_package:
TargetFilename|re: '(?i)[\\/]node_modules[\\/]plain-crypto-js[\\/]'
condition: 1 of selection_*
falsepositives:
- None known; wt.exe is also Windows Terminal but does not live in ProgramData
level: high
---
title: MicrosoftUpdate Run Value Launching a Batch File
id: 5b441188-9fee-4636-9e72-49c49cf9c25a
status: experimental
description: >
The per-user Run value the Windows payload of the compromised axios releases
writes, named MicrosoftUpdate or pointing at the hidden system.bat.
references:
- https://opensourcemalware.com/blog/axios-compromise-transitive-dependency
logsource:
category: registry_set
detection:
selection_run:
TargetObject|contains: '\CurrentVersion\Run'
selection_name:
TargetObject|endswith: '\MicrosoftUpdate'
selection_data:
Details|contains: 'system.bat'
condition: selection_run and (selection_name or selection_data)
falsepositives:
- None known
level: high
---
title: Connection to the Compromised axios Command Server
id: e6a1b3d7-4c29-5f08-9a35-2d8c7e1b6f94
status: experimental
description: >
A connection to the command-and-control domain of the compromised axios
releases, or to the two server addresses OpenSourceMalware reports for the
first and second wave.
references:
- https://opensourcemalware.com/blog/axios-compromise-transitive-dependency
- https://opensourcemalware.com/blog/axios-attacker-strikes-again
logsource:
category: network_connection
detection:
selection_domain:
DestinationHostname|endswith: 'sfrclak.com'
# optional: server addresses, they rotate
selection_ip:
DestinationIp:
- '142.11.206.73'
- '18.208.244.120'
condition: 1 of selection_*
falsepositives:
- Another tenant of a recycled hosting address
level: high
Lower-fidelity parts (2)
- the node setup.js spawn clause — setup.js is a common name and some legitimate packages spawn PowerShell during install. Remove: delete the `# optional: setup.js` comment and the `selection_setup` block in the first rule; `1 of selection_*` needs no change.
- the two server addresses 142.11.206.73 and 18.208.244.120 — they are specific to this operator's servers and rotate. Remove: delete the `# optional: server addresses` comment and the `selection_ip` block in the fourth rule; `1 of selection_*` needs no change.
CrowdStrike LogScale
Written for this site, from indicators reported by OpenSourceMalwareCovers the process and network lines only, because Falcon logs no event for these file or Run-value writes in this field set and no parent command line.
// Run with a time range that starts on 2026-03-25; Falcon has no event for these file or Run-value writes in this field set, and no parent command line
(
#event_simpleName=/^(Synthetic)?ProcessRollup2$/
and CommandLine=/6202033|com\.apple\.act\.mond|\/tmp\/ld\.py|sfrclak|\.redeem_err\.log|programdata.*system\.bat|system\.bat.*programdata/i
)
or (#event_simpleName=DnsRequest and DomainName=/sfrclak\.com$/i)
// optional: server addresses, they rotate
or (#event_simpleName=NetworkConnectIP4 and in(RemoteAddressIP4, values=["142.11.206.73", "18.208.244.120"]))
| groupBy([ComputerName, aid], function=[count(as=events), collect([CommandLine, DomainName, RemoteAddressIP4]), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen)])
| sort(last_seen, order=desc)
Lower-fidelity parts (1)
- the two server addresses 142.11.206.73 and 18.208.244.120 — they are specific to this operator's servers and rotate. Remove: delete the `// optional: server addresses` line and the `or (#event_simpleName=NetworkConnectIP4 ...)` line below it.
Elastic
Written for this site, from indicators reported by OpenSourceMalwareEach signal is a boolean column and the address match is labelled low fidelity in the result, so it can be dropped without touching the core conditions.
FROM logs-*
| WHERE @timestamp >= TO_DATETIME("2026-03-25T00:00:00Z")
| EVAL cmd = REPLACE(TO_LOWER(process.command_line), "\\\\", "/"), fname = TO_LOWER(file.name), path = REPLACE(TO_LOWER(file.path), "\\\\", "/"), reg = REPLACE(TO_LOWER(registry.path), "\\\\", "/"), regdata = TO_LOWER(MV_CONCAT(registry.data.strings, " ")), web = TO_LOWER(COALESCE(url.original, destination.domain, dns.question.name))
| EVAL f_proc = cmd LIKE "*6202033*" OR cmd LIKE "*com.apple.act.mond*" OR cmd LIKE "*/tmp/ld.py*" OR cmd LIKE "*sfrclak*" OR cmd LIKE "*.redeem_err.log*" OR (cmd LIKE "*programdata*" AND cmd LIKE "*system.bat*")
| EVAL f_file = (path LIKE "*programdata*" AND fname IN ("system.bat", "wt.exe")) OR fname IN ("6202033.vbs", "6202033.ps1", "index5_test.js", ".redeem_err.log") OR path IN ("/library/caches/com.apple.act.mond", "/tmp/ld.py") OR path LIKE "*/node_modules/plain-crypto-js/*"
| EVAL f_reg = reg LIKE "*/currentversion/run*" AND (reg LIKE "*/microsoftupdate" OR regdata LIKE "*system.bat*")
| EVAL f_net = web LIKE "*sfrclak.com*"
| EVAL f_ip = TO_STRING(destination.ip) IN ("142.11.206.73", "18.208.244.120")
| WHERE f_proc OR f_file OR f_reg OR f_net OR f_ip
| EVAL signal = CASE(f_proc, "process", f_file, "file", f_reg, "registry", f_net, "network", "low fidelity")
| STATS events = COUNT(*), signals = VALUES(signal), paths = VALUES(path), commands = VALUES(cmd), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp) BY host.name
| SORT last_seen DESC
| LIMIT 100
Lower-fidelity parts (1)
- the two server addresses 142.11.206.73 and 18.208.244.120 — they are specific to this operator's servers and rotate. Remove: delete the `| EVAL f_ip` line and ` OR f_ip` from the `| WHERE` line.
Cortex XQL
Written for this site, from indicators reported by OpenSourceMalwareCounts matching events per host and telemetry type, over a time frame that must start on 2026-03-25.
// Run with a time frame that starts on 2026-03-25 so it reaches the 2026-03-31 incident
config case_sensitive = false
| dataset = xdr_data
| filter (event_type = ENUM.PROCESS and (
action_process_image_command_line ~= "6202033|com[.]apple[.]act[.]mond|/tmp/ld[.]py|sfrclak|[.]redeem_err[.]log"
or (action_process_image_command_line contains "ProgramData" and action_process_image_command_line contains "system.bat")
// optional: setup.js is a common name and some legitimate packages spawn PowerShell during install
or (actor_process_image_name in ("node.exe", "node") and actor_process_command_line contains "setup.js" and action_process_image_name in ("cscript.exe", "wscript.exe", "powershell.exe", "cmd.exe", "curl.exe", "osascript", "curl", "python3", "sh", "bash"))
))
or (event_type = ENUM.FILE and (
(action_file_path contains "ProgramData" and action_file_name in ("system.bat", "wt.exe"))
or action_file_name in ("6202033.vbs", "6202033.ps1", "index5_test.js", ".redeem_err.log")
or action_file_path in ("/Library/Caches/com.apple.act.mond", "/tmp/ld.py")
or action_file_path ~= "node_modules[^a-z0-9_.-]plain-crypto-js[^a-z0-9_.-]"
))
or (event_type = ENUM.REGISTRY and action_registry_key_name contains "CurrentVersion\\Run" and (action_registry_value_name = "MicrosoftUpdate" or action_registry_data contains "system.bat"))
or (event_type = ENUM.NETWORK and (
action_external_hostname ~= "sfrclak[.]com$"
or dns_query_name ~= "sfrclak[.]com$"
// optional: server addresses, they rotate
or action_remote_ip in ("142.11.206.73", "18.208.244.120")
))
| alter signal = if(event_type = ENUM.FILE, "file", if(event_type = ENUM.PROCESS, "process", if(event_type = ENUM.REGISTRY, "registry", "network")))
| comp count() as events, min(_time) as first_seen, max(_time) as last_seen by agent_hostname, signal
| sort desc last_seen
Lower-fidelity parts (2)
- the node setup.js spawn clause — setup.js is a common name and some legitimate packages spawn PowerShell during install. Remove: delete the `// optional: setup.js` line and the `or (actor_process_image_name in ("node.exe", "node") and ... "setup.js" ...` line below it.
- the two server addresses 142.11.206.73 and 18.208.244.120 — they are specific to this operator's servers and rotate. Remove: delete the `// optional: server addresses` line and the `or action_remote_ip in (...)` line below it.
osquery
Written for this site, from indicators reported by OpenSourceMalwareLists the malicious axios versions, plain-crypto-js under any version, the second-wave packages and the fixed payload paths on all three systems, with AND directory = '<project path>' added on osquery versions that require it.
SELECT 'package' AS signal, path, name || '@' || version AS detail
FROM npm_packages
WHERE (name = 'axios' AND version IN ('1.14.1', '0.30.4'))
OR name = 'plain-crypto-js'
OR (name = 'redeem-onchain-sdk' AND version = '1.0.7')
OR (name = 'nicegui' AND version = '0.1.4')
OR (name = 'period-newline' AND version = '0.1.0')
UNION ALL
SELECT 'file' AS signal, path, CAST(size AS TEXT) AS detail
FROM file
WHERE path IN ('C:\ProgramData\system.bat', 'C:\ProgramData\wt.exe', '/Library/Caches/com.apple.act.mond', '/tmp/ld.py', '/tmp/.redeem_err.log')
YARA
Written for this site, from indicators reported by OpenSourceMalwareThe XOR key is shared by the axios dropper and the second-wave packages, so the rule keys on it together with either the dropper's decoding helpers or its campaign strings.
rule Axios_Compromise_OrDeR_7077_Dropper
{
meta:
description = "JavaScript dropper using the OrDeR_7077 key: plain-crypto-js setup.js and the later packages sharing it"
reference = "https://opensourcemalware.com/blog/axios-compromise-transitive-dependency"
strings:
$key = "OrDeR_7077"
$trans1 = "_trans_1"
$trans2 = "_trans_2"
$c2 = "sfrclak" nocase
$campaign = "6202033"
$product = "packages.npm.org/product"
condition:
filesize < 1MB and $key and (any of ($trans*) or any of ($c2, $campaign, $product))
}
False positives and tuning
The versions are exact: npm removed both axios releases within hours, and any lockfile or installed tree naming them was exposed.
Hold the attribution loosely: it is Google's and Microsoft's as OpenSourceMalware reports it, and the second-wave link rests on a shared key.
setup.js is a common name and some legitimate packages spawn PowerShell during install, so the optional node setup.js clause is noisy; the Run value, batch file and fixed paths are specific.
wt.exe is the name of Windows Terminal and matters only in ProgramData.
Sources
- Malicious Transitive Dependency in Axios Affects Millions of Users — OpenSourceMalware, 2026-03-31
- Axios Attacker Behind Three More Malicious NPM Packages — OpenSourceMalware, 2026-05-19