Curl Download Activity from npm Package Install

Detects curl downloading over HTTP(S) with a short argument list, writing output to a file or launched via a shell, when the process ancestry includes a Node.js npm package execution (npx-cli.js or an npx cache path). Malicious npm packages and supply-chain compromises commonly fetch a second stage from install or postinstall scripts.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/09/23"
  3integration = ["endpoint"]
  4maturity = "production"
  5min_stack_comments = "ES|QL inline stats became generally available in 9.3.0 and MV_INTERSECTION is in preview since 9.3."
  6min_stack_version = "9.3.0"
  7updated_date = "2026/09/23"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12Detects curl downloading over HTTP(S) with a short argument list, writing output to a file or launched via a shell,
 13when the process ancestry includes a Node.js npm package execution (`npx-cli.js` or an npx cache path). Malicious npm
 14packages and supply-chain compromises commonly fetch a second stage from install or postinstall scripts.
 15"""
 16from = "now-9m"
 17language = "esql"
 18license = "Elastic License v2"
 19name = "Curl Download Activity from npm Package Install"
 20note = """## Triage and analysis
 21
 22### Investigating Curl Download Activity from npm Package Install
 23
 24curl retrieved a remote HTTP(S) resource with a short command line (output file `-o`, or a shell parent) in a process
 25tree that includes Node.js executing an npm package via `npx-cli.js` or the npx cache. That pattern is common in
 26malicious npm packages and compromised dependencies.
 27
 28### Possible investigation steps
 29
 30- Review `all_pkg_cmdline` for the `npx-cli.js` or npx-cache invocation and identify the package name, scope, and version.
 31- Inspect `process.command_line` for the URL, `-o`/`-O` output path, and whether the download is piped to a shell.
 32- Correlate the same `host.id` and `user.name` for newly written files, outbound connections, and additional children
 33  from the Node.js or curl tree.
 34- Check recent npm installs, `package.json` lifecycle scripts, and lockfile changes on the host for the package in
 35  `all_pkg_cmdline`.
 36
 37### False positive analysis
 38
 39- Some legitimate packages download helpers or build tools with curl during install. Confirm the package, publisher,
 40  and destination URL before excluding.
 41- Scope exceptions to a known package command line, host, or user. Do not broadly exclude `npx` or `curl`.
 42
 43### Response and remediation
 44
 45- If malicious, isolate the host, stop the Node.js and curl process tree, and remove the package and any dropped files.
 46- Rotate credentials and tokens available to that user or build environment, including npm tokens and cloud keys.
 47- Block the package and related domains, and hunt for the same `all_pkg_cmdline` and curl command line on other hosts.
 48"""
 49references = ["https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise"]
 50risk_score = 47
 51rule_id = "7a3c9e21-5d84-4b16-9f02-1e8c6a4d0b73"
 52severity = "medium"
 53tags = [
 54    "Domain: Endpoint",
 55    "OS: Linux",
 56    "OS: macOS",
 57    "Platform: Linux",
 58    "Platform: macOS",
 59    "Use Case: Threat Detection",
 60	"Tactic: Command and Control",
 61    "Tactic: Execution",
 62    "Tactic: Initial Access",
 63    "Data Source: Elastic Defend",
 64    "Resources: Investigation Guide",
 65    "Rule Type: ES|QL",
 66    "Threat: Supply Chain",
 67    "Threat: Download Tool Abuse",
 68]
 69timestamp_override = "event.ingested"
 70type = "esql"
 71
 72query = '''
 73FROM logs-endpoint.events.process-* METADATA _id, _version, _index
 74
 75| WHERE KQL(""" host.os.type:("linux" or "macos") AND event.action:exec """)
 76
 77| EVAL is_pkg_install = CASE(
 78    // npm
 79    process.parent.name == "node" AND (
 80      process.parent.command_line LIKE "*npx-cli.js*" OR
 81      process.parent.command_line LIKE "*npm-cli.js*" OR
 82      process.parent.command_line LIKE "*/.npm/_npx/*" OR
 83      process.parent.command_line LIKE "*/npm-cache/*"
 84    ), true,
 85    false
 86  )
 87
 88| EVAL cmdline_lower = TO_LOWER(process.command_line)
 89| EVAL parent_cmdline_lower = TO_LOWER(process.parent.command_line)
 90
 91| EVAL is_curl_download = CASE(
 92    process.name == "curl" AND
 93    cmdline_lower LIKE "*http*" AND
 94    process.args_count <= 5 AND
 95    (
 96      cmdline_lower LIKE "* -o*" OR 
 97	  cmdline_lower LIKE "* --output*" OR 
 98      parent_cmdline_lower LIKE "*zsh" OR
 99      parent_cmdline_lower LIKE "*sh" OR
100      parent_cmdline_lower LIKE "*bash" OR
101      parent_cmdline_lower LIKE "*dash"
102    ) AND
103    NOT (
104      cmdline_lower LIKE "*169.254.169.254*" OR
105      cmdline_lower LIKE "*localhost*" OR
106      cmdline_lower LIKE "*127.0.0.1*"
107    ),
108    true, false
109  )
110
111| WHERE process.Ext.ancestry IS NOT NULL AND (is_curl_download OR is_pkg_install)
112
113// Capture entity_ids for package install parent processes
114| EVAL all_entity_id = CASE(is_pkg_install, process.parent.entity_id, "null")
115
116// Collect all package install entity_ids globally
117| INLINE STATS all_pkg_entity_ids = VALUES(all_entity_id), all_pkg_cmdline = VALUES(process.parent.command_line) WHERE all_entity_id != "null" by host.id
118
119// Find which package install entity_ids appear in this process's ancestry
120| EVAL Esql.pkg_ancestor_ids = MV_INTERSECTION(all_pkg_entity_ids, process.Ext.ancestry)
121
122// curl download descended from an npm package install process
123| WHERE Esql.pkg_ancestor_ids IS NOT NULL AND is_curl_download
124
125| KEEP _id, _version, _index, data_stream.namespace, host.id, host.name, user.name, process.entity_id, process.executable, process.command_line, process.parent.executable, process.parent.command_line, all_pkg_cmdline
126'''
127
128setup = """## Setup
129
130This rule is designed for data generated by [Elastic Defend](https://www.elastic.co/security/endpoint-security), which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.
131
132Setup instructions: https://ela.st/install-elastic-defend
133"""
134
135[rule.investigation_fields]
136field_names = [
137    "host.id",
138    "host.name",
139    "user.name",
140    "process.executable",
141    "process.command_line",
142    "process.parent.executable",
143    "process.parent.command_line",
144]
145
146[[rule.threat]]
147framework = "MITRE ATT&CK"
148[[rule.threat.technique]]
149id = "T1105"
150name = "Ingress Tool Transfer"
151reference = "https://attack.mitre.org/techniques/T1105/"
152
153[rule.threat.tactic]
154id = "TA0011"
155name = "Command and Control"
156reference = "https://attack.mitre.org/tactics/TA0011/"
157
158[[rule.threat]]
159framework = "MITRE ATT&CK"
160[[rule.threat.technique]]
161id = "T1059"
162name = "Command and Scripting Interpreter"
163reference = "https://attack.mitre.org/techniques/T1059/"
164[[rule.threat.technique.subtechnique]]
165id = "T1059.004"
166name = "Unix Shell"
167reference = "https://attack.mitre.org/techniques/T1059/004/"
168
169[rule.threat.tactic]
170id = "TA0002"
171name = "Execution"
172reference = "https://attack.mitre.org/tactics/TA0002/"
173
174[[rule.threat]]
175framework = "MITRE ATT&CK"
176[[rule.threat.technique]]
177id = "T1195"
178name = "Supply Chain Compromise"
179reference = "https://attack.mitre.org/techniques/T1195/"
180[[rule.threat.technique.subtechnique]]
181id = "T1195.001"
182name = "Compromise Software Dependencies and Development Tools"
183reference = "https://attack.mitre.org/techniques/T1195/001/"
184
185[rule.threat.tactic]
186id = "TA0001"
187name = "Initial Access"
188reference = "https://attack.mitre.org/tactics/TA0001/"

Triage and analysis

Investigating Curl Download Activity from npm Package Install

curl retrieved a remote HTTP(S) resource with a short command line (output file -o, or a shell parent) in a process tree that includes Node.js executing an npm package via npx-cli.js or the npx cache. That pattern is common in malicious npm packages and compromised dependencies.

Possible investigation steps

  • Review all_pkg_cmdline for the npx-cli.js or npx-cache invocation and identify the package name, scope, and version.
  • Inspect process.command_line for the URL, -o/-O output path, and whether the download is piped to a shell.
  • Correlate the same host.id and user.name for newly written files, outbound connections, and additional children from the Node.js or curl tree.
  • Check recent npm installs, package.json lifecycle scripts, and lockfile changes on the host for the package in all_pkg_cmdline.

False positive analysis

  • Some legitimate packages download helpers or build tools with curl during install. Confirm the package, publisher, and destination URL before excluding.
  • Scope exceptions to a known package command line, host, or user. Do not broadly exclude npx or curl.

Response and remediation

  • If malicious, isolate the host, stop the Node.js and curl process tree, and remove the package and any dropped files.
  • Rotate credentials and tokens available to that user or build environment, including npm tokens and cloud keys.
  • Block the package and related domains, and hunt for the same all_pkg_cmdline and curl command line on other hosts.

References

Related rules

to-top