Detection rules › Elastic

Linux Powershell Egress Network Connection

Time window
5s
Sequence by
process.entity_id
Source
github.com/elastic/protections-artifacts

Detects when Powershell (pwsh) on Linux makes an outbound network connection attempt. Powershell usage on Linux is rare, and leveraging Powershell to connect out to the internet may indicate malicious behavior.

MITRE ATT&CK coverage

Rule body

[rule]
description = """
Detects when Powershell (pwsh) on Linux makes an outbound network connection attempt. Powershell usage on Linux is rare,
and leveraging Powershell to connect out to the internet may indicate malicious behavior.
"""
id = "1471cf36-7e5c-47cc-bf39-2234df0e676a"
license = "Elastic License v2"
name = "Linux Powershell Egress Network Connection"
os_list = ["linux"]
version = "1.0.14"

query = '''
sequence by process.entity_id with maxspan=5s
  [process where event.type == "start" and event.action == "exec" and process.parent.name == "pwsh" and not (
    process.name in ("kubectl", "helm", "pwsh", "yum", "dnf", "dotnet", "ansible-lint") or
    process.executable like (
      "/run/containerd/*python3", "/jenkins-data/docker/*python3", "/tmp/Download-References/DepotDownloader/DepotDownloader",
      "/home/*/.local/bin/az", "/usr/libexec/platform-python*"
    ) or
    process.parent.executable like ("/jenkins-data/docker*", "/var/run/docker/*", "/run/containerd/*") or
    process.command_line == "/usr/bin/gh auth status" or
    (process.name like "python*" and process.args == "azure.cli") or
    process.working_directory like "/opt/azurevstsagent/agent*" or
    process.args == "/bin/dnf" or
    process.args like "/home/*/.local/bin/az"
  )
  ]
  [network where event.type == "start" and event.action == "connection_attempted" and not (
     destination.ip == null or
     destination.ip == "0.0.0.0" or
     cidrmatch(
       destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29",
       "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24",
       "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10",
       "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1", "FE80::/10",
       "FF00::/8"
     ) or
     process.name == "ssh"
   )
  ]
'''

min_endpoint_version = "7.15.0"
optional_actions = []
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0

[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 1

[[actions]]
action = "kill_process"
field = "process.parent.entity_id"
state = 0

[[actions]]
action = "kill_process"
field = "process.parent.entity_id"
state = 1

[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[threat.technique.subtechnique]]
id = "T1059.001"
name = "PowerShell"
reference = "https://attack.mitre.org/techniques/T1059/001/"



[threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[[threat]]
framework = "MITRE ATT&CK"

[threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[[threat]]
framework = "MITRE ATT&CK"

[threat.tactic]
id = "TA0010"
name = "Exfiltration"
reference = "https://attack.mitre.org/tactics/TA0010/"
[[threat]]
framework = "MITRE ATT&CK"

[threat.tactic]
id = "TA0011"
name = "Command and Control"
reference = "https://attack.mitre.org/tactics/TA0011/"

[internal]
min_endpoint_version = "7.15.0"

Stages and Predicates

Ordered sequence: each step below must occur in order within 5s, correlated by process.entity_id.

Stage 1: process

[process where event.type == "start" and event.action == "exec" and process.parent.name == "pwsh" and not (
    process.name in ("kubectl", "helm", "pwsh", "yum", "dnf", "dotnet", "ansible-lint") or
    process.executable like (
      "/run/containerd/*python3", "/jenkins-data/docker/*python3", "/tmp/Download-References/DepotDownloader/DepotDownloader",
      "/home/*/.local/bin/az", "/usr/libexec/platform-python*"
    ) or
    process.parent.executable like ("/jenkins-data/docker*", "/var/run/docker/*", "/run/containerd/*") or
    process.command_line == "/usr/bin/gh auth status" or
    (process.name like "python*" and process.args == "azure.cli") or
    process.working_directory like "/opt/azurevstsagent/agent*" or
    process.args == "/bin/dnf" or
    process.args like "/home/*/.local/bin/az"
  )
  ]

Stage 2: network

[network where event.type == "start" and event.action == "connection_attempted" and not (
     destination.ip == null or
     destination.ip == "0.0.0.0" or
     cidrmatch(
       destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29",
       "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24",
       "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10",
       "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1", "FE80::/10",
       "FF00::/8"
     ) or
     process.name == "ssh"
   )
  ]

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
process.argseqazure.cliexcludes:process.args field:"process.args" value:"azure.cli"
process.namestarts_withpythonexcludes:process.name field:"process.name" value:"python"
process.argseq/bin/dnfexcludes:process.args field:"process.args" value:"/bin/dnf"
process.argswildcard/home/*/.local/bin/azexcludes:process.args field:"process.args" value:"/home/*/.local/bin/az"
process.command_lineeq/usr/bin/gh auth statusexcludes:process.command_line field:"process.command_line" value:"/usr/bin/gh auth status"
process.executablewildcard/run/containerd/*python3, /jenkins-data/docker/*python3, /tmp/Download-References/DepotDownloader/DepotDownloader, /home/*/.local/bin/az, /usr/libexec/platform-python*excludes:process.executable
process.nameinansible-lint, dnf, dotnet, helm, kubectl, pwsh, yumexcludes:process.name
process.parent.executablestarts_with/jenkins-data/docker, /var/run/docker/, /run/containerd/excludes:process.parent.executable field:"process.parent.executable" value:"/jenkins-data/docker" field:"process.parent.executable" value:"/var/run/docker/" field:"process.parent.executable" value:"/run/containerd/"
process.working_directorystarts_with/opt/azurevstsagent/agentexcludes:process.working_directory field:"process.working_directory" value:"/opt/azurevstsagent/agent"
destination.ipcidr_match10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.0.0/29, 192.0.0.8/32, 192.0.0.9/32, 192.0.0.10/32, 192.0.0.170/32, 192.0.0.171/32, 192.0.2.0/24, 192.31.196.0/24, 192.52.193.0/24, 192.168.0.0/16, 192.88.99.0/24, 224.0.0.0/4, 100.64.0.0/10, 192.175.48.0/24, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::1, FE80::/10, FF00::/8excludes:destination.ip
destination.ipeq0.0.0.0excludes:destination.ip field:"destination.ip" value:"0.0.0.0"
destination.ipis_null(no value, null check)excludes:destination.ip
process.nameeqsshexcludes:process.name field:"process.name" value:"ssh"

Indicators

These rows show field, operator, and value matches.