﻿---
title: Suspicious PowerShell from npm Package Install
description: 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...
url: https://www.elastic.co/docs/reference/security/prebuilt-rules/rules/windows/execution_suspicious_powershell_from_npm_install
products:
  - Elastic Security
---

# 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.
**Rule type**: esql
**Rule indices**:
**Rule Severity**: high
**Risk Score**: 73
**Runs every**: 
**Searches indices from**: `now-9m`
**Maximum alerts per execution**: 100
**References**:
- [[https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise](https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise)](https://www.elastic.co/blog/shai-hulud-worm-npm-supply-chain-compromise)

**Tags**:
- Domain: Endpoint
- OS: Windows
- Platform: Windows
- Use Case: Threat Detection
- Tactic: Execution
- Tactic: Initial Access
- Tactic: Defense Evasion
- Tactic: Command and Control
- Data Source: Elastic Defend
- Resources: Investigation Guide
- Rule Type: ES|QL
- Threat: Supply Chain
- Threat: Script-Based Execution

**Version**: 1
**Rule authors**:
- Elastic

**Rule license**: Elastic License v2

## Setup

This 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.
Setup instructions: [https://ela.st/install-elastic-defend](https://ela.st/install-elastic-defend)

## Investigation guide


## 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.


## Rule Query

```esql
FROM logs-endpoint.events.process-* METADATA _id, _version, _index

| EVAL is_pkg_install = CASE(
    // npm
    process.parent.name == "node.exe" AND (
      process.parent.command_line LIKE "*npx-cli.js*" OR
      process.parent.command_line LIKE "*npm-cli.js*" OR
      process.parent.command_line LIKE """*\\npm-cache\\_npx\\*"""
    ), true,
    false
  )

| EVAL cmdline_lower = TO_LOWER(process.command_line)

| EVAL is_encoded_powershell = CASE(
    TO_LOWER(process.name) == "powershell.exe" AND (
        cmdline_lower LIKE "* -enc*" OR
        cmdline_lower LIKE "* -en *" OR
        cmdline_lower LIKE "* -ec *" OR
        cmdline_lower LIKE "* -ec:*" OR
        cmdline_lower LIKE "*frombase64*"
     ), true, false
  )

// plain -Command / dot-sourced / no-flag download cradles that skip -EncodedCommand entirely
| EVAL is_download_cradle = CASE(
     process.name == "powershell.exe" AND (
        cmdline_lower LIKE "*downloadstring*" OR
        cmdline_lower LIKE "*downloadfile*" OR
        cmdline_lower LIKE "*downloaddata*" OR
        cmdline_lower LIKE "*net.webclient*" OR
        cmdline_lower LIKE "*new-object*webclient*" OR
        cmdline_lower LIKE "*invoke-webrequest*" OR
        cmdline_lower LIKE "* iwr *" OR
        cmdline_lower LIKE "* iwr(*" OR
        cmdline_lower LIKE "*invoke-restmethod*" OR
        cmdline_lower LIKE "* irm *" OR
        cmdline_lower LIKE "* irm(*" OR
        cmdline_lower LIKE "*invoke-expression*" OR
        cmdline_lower LIKE "* iex *" OR
        cmdline_lower LIKE "* iex(*" OR
        cmdline_lower LIKE "*start-bitstransfer*" OR
        cmdline_lower LIKE "*bitstransfer*"
     ), true, false
  )

| EVAL is_susp_powershell = CASE(is_encoded_powershell OR is_download_cradle, true, false)

| WHERE process.Ext.ancestry IS NOT NULL AND (is_susp_powershell OR is_pkg_install)

// Capture entity_ids for package install parent processes
| EVAL all_entity_id = CASE(is_pkg_install, process.parent.entity_id, "null")

// Collect all package install entity_ids globally
| 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

// Find which package install entity_ids appear in this process's ancestry
| EVAL Esql.pkg_ancestor_ids = MV_INTERSECTION(all_pkg_entity_ids, process.Ext.ancestry)

// susp powershell (encoded or download cradle) and descended from an npm package install process
| WHERE Esql.pkg_ancestor_ids IS NOT NULL AND is_susp_powershell

| 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,
       is_encoded_powershell, is_download_cradle
```

**Framework:** MITRE ATT&CK
- Tactic:
  - Name: Execution
  - Id: TA0002
  - Reference URL: [[https://attack.mitre.org/tactics/TA0002/](https://attack.mitre.org/tactics/TA0002/)](https://attack.mitre.org/tactics/TA0002/)
- Technique:
  - Name: Command and Scripting Interpreter
  - Id: T1059
  - Reference URL: [[https://attack.mitre.org/techniques/T1059/](https://attack.mitre.org/techniques/T1059/)](https://attack.mitre.org/techniques/T1059/)
- Sub Technique:
  - Name: PowerShell
  - Id: T1059.001
  - Reference URL: [[https://attack.mitre.org/techniques/T1059/001/](https://attack.mitre.org/techniques/T1059/001/)](https://attack.mitre.org/techniques/T1059/001/)

**Framework:** MITRE ATT&CK
- Tactic:
  - Name: Initial Access
  - Id: TA0001
  - Reference URL: [[https://attack.mitre.org/tactics/TA0001/](https://attack.mitre.org/tactics/TA0001/)](https://attack.mitre.org/tactics/TA0001/)
- Technique:
  - Name: Supply Chain Compromise
  - Id: T1195
  - Reference URL: [[https://attack.mitre.org/techniques/T1195/](https://attack.mitre.org/techniques/T1195/)](https://attack.mitre.org/techniques/T1195/)
- Sub Technique:
  - Name: Compromise Software Dependencies and Development Tools
  - Id: T1195.001
  - Reference URL: [[https://attack.mitre.org/techniques/T1195/001/](https://attack.mitre.org/techniques/T1195/001/)](https://attack.mitre.org/techniques/T1195/001/)

**Framework:** MITRE ATT&CK
- Tactic:
  - Name: Defense Evasion
  - Id: TA0005
  - Reference URL: [[https://attack.mitre.org/tactics/TA0005/](https://attack.mitre.org/tactics/TA0005/)](https://attack.mitre.org/tactics/TA0005/)
- Technique:
  - Name: Obfuscated Files or Information
  - Id: T1027
  - Reference URL: [[https://attack.mitre.org/techniques/T1027/](https://attack.mitre.org/techniques/T1027/)](https://attack.mitre.org/techniques/T1027/)
- Sub Technique:
  - Name: Command Obfuscation
  - Id: T1027.010
  - Reference URL: [[https://attack.mitre.org/techniques/T1027/010/](https://attack.mitre.org/techniques/T1027/010/)](https://attack.mitre.org/techniques/T1027/010/)

**Framework:** MITRE ATT&CK
- Tactic:
  - Name: Command and Control
  - Id: TA0011
  - Reference URL: [[https://attack.mitre.org/tactics/TA0011/](https://attack.mitre.org/tactics/TA0011/)](https://attack.mitre.org/tactics/TA0011/)
- Technique:
  - Name: Ingress Tool Transfer
  - Id: T1105
  - Reference URL: [[https://attack.mitre.org/techniques/T1105/](https://attack.mitre.org/techniques/T1105/)](https://attack.mitre.org/techniques/T1105/)