Suspicious PowerShell from npm Package Install

Detects PowerShell launched with an encoded command or a download cradle whose process ancestry includes a Node.js npm package execution (npx-cli.js with a scoped or versioned package). Malicious npm packages and supply-chain compromises commonly run this from install or postinstall scripts to fetch and execute a second stage.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/09/22"
  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/22"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12Detects PowerShell launched with an encoded command or a download cradle whose process ancestry includes a Node.js
 13npm package execution (`npx-cli.js` with a scoped or versioned package). Malicious npm packages and supply-chain
 14compromises commonly run this from install or postinstall scripts to fetch and execute a second stage.
 15"""
 16from = "now-9m"
 17language = "esql"
 18license = "Elastic License v2"
 19name = "Suspicious PowerShell from npm Package Install"
 20note = """## Triage and analysis
 21
 22### Investigating Suspicious PowerShell from npm Package Install
 23
 24PowerShell with an encoded command or a download cradle (`DownloadString`, `Invoke-WebRequest`, `Invoke-Expression`,
 25BITS) ran in a process tree that includes Node.js executing an npm package via `npx-cli.js`. That pattern is common
 26in malicious npm packages and compromised dependencies.
 27
 28### Possible investigation steps
 29
 30- Review `all_pkg_cmdline` for the `npx-cli.js` invocation and identify the package name, scope, and version.
 31- Inspect `process.command_line` and whether `is_encoded_powershell` or `is_download_cradle` is true. Decode any
 32  `-EncodedCommand` payload and extract URLs, file paths, and follow-on commands.
 33- Correlate the same `host.id` and `user.name` for new files, outbound connections, and additional child processes
 34  from the Node.js or PowerShell tree.
 35- Check recent npm installs, `package.json` lifecycle scripts, and lockfile changes on the host for the package in
 36  `all_pkg_cmdline`.
 37
 38### False positive analysis
 39
 40- Some legitimate packages download helpers or build tools with PowerShell during install. Confirm the package,
 41  publisher, and destination URL before excluding.
 42- Scope exceptions to a known package command line, host, or user. Do not broadly exclude `npx` or `powershell.exe`.
 43
 44### Response and remediation
 45
 46- If malicious, isolate the host, stop the Node.js and PowerShell process tree, and remove the package and any
 47  dropped files.
 48- Rotate credentials and tokens available to that user or build environment, including npm tokens and cloud keys.
 49- Block the package and related domains, and hunt for the same `all_pkg_cmdline` and PowerShell command line on
 50  other hosts.
 51"""
 52references = ["https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise"]
 53risk_score = 73
 54rule_id = "c4e8a1d7-2b6f-4e93-8a15-7d0c3f9b6e42"
 55severity = "high"
 56tags = [
 57    "Domain: Endpoint",
 58    "OS: Windows",
 59    "Platform: Windows",
 60    "Use Case: Threat Detection",
 61    "Tactic: Execution",
 62    "Tactic: Initial Access",
 63    "Tactic: Defense Evasion",
 64    "Tactic: Command and Control",
 65    "Data Source: Elastic Defend",
 66    "Resources: Investigation Guide",
 67    "Rule Type: ES|QL",
 68    "Threat: Supply Chain",
 69    "Threat: Script-Based Execution",
 70]
 71timestamp_override = "event.ingested"
 72type = "esql"
 73
 74query = '''
 75FROM logs-endpoint.events.process-* METADATA _id, _version, _index
 76
 77| EVAL is_pkg_install = CASE(
 78    // npm
 79    process.parent.name == "node.exe" 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-cache\\_npx\\*"""
 83    ), true,
 84    false
 85  )
 86
 87| EVAL cmdline_lower = TO_LOWER(process.command_line)
 88
 89| EVAL is_encoded_powershell = CASE(
 90    TO_LOWER(process.name) == "powershell.exe" AND (
 91        cmdline_lower LIKE "* -enc*" OR
 92        cmdline_lower LIKE "* -en *" OR
 93        cmdline_lower LIKE "* -ec *" OR
 94        cmdline_lower LIKE "* -ec:*" OR
 95        cmdline_lower LIKE "*frombase64*"
 96     ), true, false
 97  )
 98
 99// plain -Command / dot-sourced / no-flag download cradles that skip -EncodedCommand entirely
100| EVAL is_download_cradle = CASE(
101     process.name == "powershell.exe" AND (
102        cmdline_lower LIKE "*downloadstring*" OR
103        cmdline_lower LIKE "*downloadfile*" OR
104        cmdline_lower LIKE "*downloaddata*" OR
105        cmdline_lower LIKE "*net.webclient*" OR
106        cmdline_lower LIKE "*new-object*webclient*" OR
107        cmdline_lower LIKE "*invoke-webrequest*" OR
108        cmdline_lower LIKE "* iwr *" OR
109        cmdline_lower LIKE "* iwr(*" OR
110        cmdline_lower LIKE "*invoke-restmethod*" OR
111        cmdline_lower LIKE "* irm *" OR
112        cmdline_lower LIKE "* irm(*" OR
113        cmdline_lower LIKE "*invoke-expression*" OR
114        cmdline_lower LIKE "* iex *" OR
115        cmdline_lower LIKE "* iex(*" OR
116        cmdline_lower LIKE "*start-bitstransfer*" OR
117        cmdline_lower LIKE "*bitstransfer*"
118     ), true, false
119  )
120
121| EVAL is_susp_powershell = CASE(is_encoded_powershell OR is_download_cradle, true, false)
122
123| WHERE process.Ext.ancestry IS NOT NULL AND (is_susp_powershell OR is_pkg_install)
124
125// Capture entity_ids for package install parent processes
126| EVAL all_entity_id = CASE(is_pkg_install, process.parent.entity_id, "null")
127
128// Collect all package install entity_ids globally
129| 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
130
131// Find which package install entity_ids appear in this process's ancestry
132| EVAL Esql.pkg_ancestor_ids = MV_INTERSECTION(all_pkg_entity_ids, process.Ext.ancestry)
133
134// susp powershell (encoded or download cradle) and descended from an npm package install process
135| WHERE Esql.pkg_ancestor_ids IS NOT NULL AND is_susp_powershell
136
137| KEEP _id, _version, _index, data_stream.namespace, host.id, host.name, user.name, process.entity_id, process.executable, process.command_line, process.parent.executable, all_pkg_cmdline,
138       is_encoded_powershell, is_download_cradle
139'''
140
141setup = """## Setup
142
143This 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.
144
145Setup instructions: https://ela.st/install-elastic-defend
146"""
147
148[rule.investigation_fields]
149field_names = [
150    "host.id",
151    "host.name",
152    "user.name",
153    "process.executable",
154    "process.command_line",
155    "process.parent.executable",
156]
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.001"
166name = "PowerShell"
167reference = "https://attack.mitre.org/techniques/T1059/001/"
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
186[rule.threat.tactic]
187id = "TA0001"
188name = "Initial Access"
189reference = "https://attack.mitre.org/tactics/TA0001/"
190
191[[rule.threat]]
192framework = "MITRE ATT&CK"
193[[rule.threat.technique]]
194id = "T1027"
195name = "Obfuscated Files or Information"
196reference = "https://attack.mitre.org/techniques/T1027/"
197[[rule.threat.technique.subtechnique]]
198id = "T1027.010"
199name = "Command Obfuscation"
200reference = "https://attack.mitre.org/techniques/T1027/010/"
201
202[rule.threat.tactic]
203id = "TA0005"
204name = "Defense Evasion"
205reference = "https://attack.mitre.org/tactics/TA0005/"
206
207[[rule.threat]]
208framework = "MITRE ATT&CK"
209[[rule.threat.technique]]
210id = "T1105"
211name = "Ingress Tool Transfer"
212reference = "https://attack.mitre.org/techniques/T1105/"
213
214[rule.threat.tactic]
215id = "TA0011"
216name = "Command and Control"
217reference = "https://attack.mitre.org/tactics/TA0011/"

Triage and analysis

Investigating Suspicious PowerShell from npm Package Install

PowerShell with an encoded command or a download cradle (DownloadString, Invoke-WebRequest, Invoke-Expression, BITS) ran in a process tree that includes Node.js executing an npm package via npx-cli.js. That pattern is common in malicious npm packages and compromised dependencies.

Possible investigation steps

  • Review all_pkg_cmdline for the npx-cli.js invocation and identify the package name, scope, and version.
  • Inspect process.command_line and whether is_encoded_powershell or is_download_cradle is true. Decode any -EncodedCommand payload and extract URLs, file paths, and follow-on commands.
  • Correlate the same host.id and user.name for new files, outbound connections, and additional child processes from the Node.js or PowerShell 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 PowerShell 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 powershell.exe.

Response and remediation

  • If malicious, isolate the host, stop the Node.js and PowerShell 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 PowerShell command line on other hosts.

References

Related rules

to-top