Proxy Execution via Windows OpenSSH

Identifies attempts to execute commands via proxy using the Windows OpenSSH client. This may indicate an attempt to bypass application control via trusted Windows binaries.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2025/08/21"
  3integration = ["endpoint", "windows", "system", "m365_defender", "sentinel_one_cloud_funnel", "crowdstrike"]
  4maturity = "production"
  5updated_date = "2026/04/29"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies attempts to execute commands via proxy using the Windows OpenSSH client. This may indicate an attempt
 11to bypass application control via trusted Windows binaries.
 12"""
 13from = "now-9m"
 14index = [
 15    "endgame-*",
 16    "logs-crowdstrike.fdr*",
 17    "logs-endpoint.events.process-*",
 18    "logs-m365_defender.event-*",
 19    "logs-sentinel_one_cloud_funnel.*",
 20    "logs-system.security*",
 21    "logs-windows.forwarded*",
 22    "logs-windows.sysmon_operational-*",
 23    "winlogbeat-*",
 24]
 25language = "eql"
 26license = "Elastic License v2"
 27name = "Proxy Execution via Windows OpenSSH"
 28references = ["https://lolbas-project.github.io/lolbas/Binaries/Ssh/"]
 29risk_score = 73
 30rule_id = "8cd49fbc-a35a-4418-8688-133cc3a1e548"
 31severity = "high"
 32tags = [
 33    "Domain: Endpoint",
 34    "OS: Windows",
 35    "Use Case: Threat Detection",
 36    "Tactic: Defense Evasion",
 37    "Data Source: Elastic Endgame",
 38    "Data Source: Elastic Defend",
 39    "Data Source: Windows Security Event Logs",
 40    "Data Source: Microsoft Defender XDR",
 41    "Data Source: Sysmon",
 42    "Data Source: SentinelOne",
 43    "Data Source: Crowdstrike",
 44    "Resources: Investigation Guide",
 45]
 46timestamp_override = "event.ingested"
 47type = "eql"
 48
 49query = '''
 50process where host.os.type == "windows" and event.type == "start" and process.name : ("ssh.exe", "sftp.exe") and
 51  process.command_line : (
 52    "*Command=*powershell*", "*schtasks*", "*Command=*@echo off*", "*Command=*http*",
 53    "*Command=*mshta*", "*Command=*msiexec*", "*Command=*cmd /c*", "*Command=*cmd.exe*",
 54    "*Command=\"cmd /c*", "*LocalCommand=scp*&&*", "*LocalCommand=?scp*&&*", "*Command=*script*"
 55  )
 56'''
 57
 58note = """## Triage and analysis
 59
 60### Investigating Proxy Execution via Windows OpenSSH
 61
 62#### Possible investigation steps
 63
 64- What OpenSSH execution path did the alert capture?
 65  - Why: "ProxyCommand" launches a local helper through the user's shell, "LocalCommand" runs locally after connection only when "PermitLocalCommand" is enabled, and remote command options shift the action to the SSH target.
 66  - Focus: `process.name` and `process.command_line`, separating "ProxyCommand", "LocalCommand", "RemoteCommand", chained "scp"/"sftp", shell/LOLBIN helpers, and loopback targets like "localhost" or "127.0.0.1".
 67  - Implication: escalate when the option runs "cmd.exe", "powershell.exe", "mshta.exe", "msiexec.exe", "schtasks.exe", a downloader, script, or chained copy/execution command; close is plausible only when it stays inside a recognized bastion, transfer, or deployment pattern with no execution-oriented helper.
 68- Is the OpenSSH client and launcher context expected for that behavior?
 69  - Focus: `process.executable`, `process.pe.original_file_name`, `process.code_signature.trusted`, `process.parent.executable`, and `process.parent.command_line`, checking native "C:\\Windows\\System32\\OpenSSH\\" use versus renamed or user-writable copies.
 70  - Implication: escalate when identity or lineage is inconsistent, such as an unsigned or renamed client, a user-writable path, Office/browser/script-host ancestry, or another LOLBin as the launcher; a native signed client lowers only masquerade risk and does not clear proxy execution.
 71- Does the user and logon session fit recognized SSH automation on this host?
 72  - Focus: `user.id`, `user.name`, `host.id`, `process.Ext.session_info.logon_type`, and `process.Ext.authentication_id`.
 73  - Hint: if session origin matters, pivot on `host.id` from `process.Ext.authentication_id` to Windows Security `winlog.event_data.TargetLogonId`, then read `source.ip` and `winlog.event_data.AuthenticationPackageName`; search `winlog.event_data.SubjectLogonId` for explicit-credential event 4648. Missing Windows Security telemetry is unresolved, not benign. $investigate_4
 74  - Implication: escalate when the session is remote-interactive, network-origin, explicit-credential, or tied to a user/host pair that does not normally run this SSH pattern; lower concern only when the same identity, launcher, and command profile are recurrent for this host and no other evidence conflicts.
 75- Did the client reach the destination implied by the SSH option path?
 76  - Focus: process-scoped DNS and connections for `host.id` and `process.entity_id`; read `dns.question.name`, `dns.resolved_ip`, `destination.ip`, and `destination.port`. $investigate_2
 77  - Hint: if `process.entity_id` is unavailable, rerun with `host.id` + `process.pid` in the alert window. Interpret DNS lookups separately from connections. Missing network or DNS telemetry is unresolved, not benign; loopback `destination.ip` supports proxy-execution when the command targets localhost.
 78  - Implication: escalate when the process reaches loopback listeners, rare public infrastructure, unrelated internal systems, or admin ports outside the expected SSH workflow; bounded destinations matching the same operator and command pattern reduce scope but do not override suspicious local execution.
 79- Did the proxied path create local child execution or transfer artifacts?
 80  - Focus: child starts where `process.parent.entity_id` matches `process.entity_id`, plus manually queried file events scoped to the same process; read child `process.command_line`, staged `file.path`, and rename context from `file.Ext.original.path`. $investigate_3
 81  - Hint: if process-scoped pivots are unavailable, repeat with `host.id` + `process.pid` in a tight alert window and compare child commands or file writes to the OpenSSH option string. Missing file telemetry limits artifact review; it is not benign.
 82  - Implication: escalate when OpenSSH spawns shells, script hosts, scheduled-task helpers, or drops/copies executable, archive, or script content into new paths; absence of child/file evidence keeps the case process-local only when earlier command, lineage, and destination evidence fit.
 83- If local evidence remains suspicious or unresolved, does the pattern recur beyond this event?
 84  - Focus: recent alerts for `host.id`, keyed to OpenSSH proxy execution, script hosts, downloaders, scheduled tasks, credential access, or the suspicious `process.command_line` fragment. $investigate_1
 85  - Hint: if host scope stays unresolved, pivot to `user.id` for the same launcher, command fragment, or recovered destination pattern across other hosts. $investigate_0
 86  - Implication: broaden scope when the same proxy-execution pattern, destination, or follow-on artifact appears on unrelated hosts or sessions; a single event supports closure only when the local evidence already binds to one exact recognized workflow and outside confirmation covers any legitimacy gap.
 87- What disposition do command intent, identity, lineage, session, destination, artifacts, and recurrence support?
 88  - Implication: escalate for unauthorized local proxy execution, suspicious launcher/session context, rare or loopback destinations, staging, child shells, or repeated indirect execution; close only when alert-local evidence and recovery bind one recognized workflow on this host and outside confirmation verifies any telemetry gap; preserve evidence and escalate when evidence is mixed or incomplete.
 89
 90### False positive analysis
 91
 92- Recognized jump-host or bastion wrappers can use the native OpenSSH client with "ProxyCommand" for a fixed proxy helper or "RemoteCommand" for a bounded admin task. Confirm binary identity, launcher, option string, `user.id`, `host.id`, recovered destination, and child/file evidence all align with one workflow. Shell or LOLBin-bearing "ProxyCommand" and "LocalCommand" remain suspicious unless a controlled test or deployment wrapper is confirmed by telemetry and outside context.
 93- Recognized transfer or sync jobs can use "sftp.exe" or chained "scp" from a fixed automation account or host. Confirm `process.parent.executable`, transfer-oriented `process.command_line`, recovered `file.path`, destination evidence, `user.id`, and `host.id` stay inside that product workflow. Keep the alert suspicious if child `process.command_line` activity, scheduled-task helpers, executable staging, or SSH configuration changes diverge from the transfer pattern.
 94- Before creating an exception, validate recurrence for the same `process.executable`, `process.parent.executable`, option-bearing `process.command_line`, `user.id`, `host.id`, and recovered destination or transfer path. Avoid exceptions on "ssh.exe", "sftp.exe", or "Command=" alone.
 95
 96### Response and remediation
 97
 98- If confirmed benign, reverse temporary containment and document the exact command, launcher, user, host, destination or transfer path, and session evidence that justified closure. Create an exception only for the minimum recurring workflow pattern, not for OpenSSH use in general.
 99- If suspicious but unconfirmed, preserve the alert, Timeline view, command line, parent/child process tree, recovered destination or DNS evidence, staged/copied files, relevant authentication records, and SSH client/server configuration files before containment or cleanup. Apply reversible containment first, such as temporary destination restrictions or heightened monitoring for the affected user and host, and avoid process termination until scope is clearer.
100- If confirmed malicious, isolate the host or contain the account when command intent, launcher lineage, destination, authentication, or artifact evidence shows unauthorized proxy execution. Weigh host criticality before isolation, block confirmed malicious destinations or hashes, and record the process instance and artifact identifiers before killing processes or deleting files.
101- Eradicate only the artifacts and settings found during the investigation: copied payloads, scripts, scheduled tasks, downloaded content, unauthorized "ProxyCommand", "LocalCommand", or "PermitLocalCommand" settings, and any malicious key material such as unexpected ".ssh\\authorized_keys" or "%PROGRAMDATA%\\ssh\\administrators_authorized_keys" entries. Then remediate the launcher or access path that allowed the OpenSSH proxy launch.
102- Rotate credentials, tokens, and SSH keys when authentication records, session origin, transferred files, or key artifacts show explicit-credential abuse, privileged account misuse, or unauthorized key-based access. Review adjacent admin sessions for the same `source.ip`, `host.id`, or `user.id` before restoring normal access.
103- Post-incident hardening: restrict OpenSSH client use to recognized bastion, deployment, or transfer hosts where feasible; disable "PermitLocalCommand" unless required; review "%PROGRAMDATA%\\ssh\\ssh_config" and affected users' ".ssh\\config" for unauthorized command options; retain the confirmed command, parent, destination, and user pattern for future triage and exception review.
104"""
105
106setup = """## Setup
107
108This 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.
109
110Setup instructions: https://ela.st/install-elastic-defend
111
112### Additional data sources
113
114This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
115
116- [CrowdStrike](https://ela.st/crowdstrike-integration)
117- [Microsoft Defender XDR](https://ela.st/m365-defender)
118- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
119- [Sysmon Event ID 1 - Process Creation](https://ela.st/sysmon-event-1-setup)
120- [Windows Process Creation Logs](https://ela.st/audit-process-creation)
121"""
122
123[rule.investigation_fields]
124field_names = [
125    "@timestamp",
126    "host.name",
127    "host.id",
128    "user.name",
129    "user.id",
130    "process.executable",
131    "process.pe.original_file_name",
132    "process.command_line",
133    "process.pid",
134    "process.entity_id",
135    "process.Ext.authentication_id",
136    "process.Ext.session_info.logon_type",
137    "process.parent.executable",
138    "process.parent.command_line",
139    "process.code_signature.trusted",
140]
141
142[transform]
143
144[[transform.investigate]]
145label = "Alerts associated with the user"
146description = ""
147providers = [
148  [
149    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
150    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
151  ]
152]
153relativeFrom = "now-48h/h"
154relativeTo = "now"
155
156[[transform.investigate]]
157label = "Alerts associated with the host"
158description = ""
159providers = [
160  [
161    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
162    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
163  ]
164]
165relativeFrom = "now-48h/h"
166relativeTo = "now"
167
168[[transform.investigate]]
169label = "Network activity for the alerting OpenSSH process"
170description = ""
171providers = [
172  [
173    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
174    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" },
175    { excluded = false, field = "event.category", queryType = "phrase", value = "network", valueType = "string" }
176  ]
177]
178relativeFrom = "now-1h"
179relativeTo = "now"
180
181[[transform.investigate]]
182label = "Child process starts from the same OpenSSH instance"
183description = ""
184providers = [
185  [
186    { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
187    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
188    { excluded = false, field = "process.parent.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
189  ]
190]
191relativeFrom = "now-1h"
192relativeTo = "now"
193
194[[transform.investigate]]
195label = "Windows Security events for the OpenSSH session"
196description = ""
197providers = [
198  [
199    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
200    { excluded = false, field = "winlog.event_data.TargetLogonId", queryType = "phrase", value = "{{process.Ext.authentication_id}}", valueType = "string" },
201    { excluded = false, field = "event.code", queryType = "phrase", value = "4624", valueType = "string" }
202  ],
203  [
204    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
205    { excluded = false, field = "winlog.event_data.SubjectLogonId", queryType = "phrase", value = "{{process.Ext.authentication_id}}", valueType = "string" },
206    { excluded = false, field = "event.code", queryType = "phrase", value = "4648", valueType = "string" }
207  ]
208]
209relativeFrom = "now-24h"
210relativeTo = "now"
211
212[[rule.threat]]
213framework = "MITRE ATT&CK"
214[[rule.threat.technique]]
215id = "T1202"
216name = "Indirect Command Execution"
217reference = "https://attack.mitre.org/techniques/T1202/"
218
219[rule.threat.tactic]
220id = "TA0005"
221name = "Defense Evasion"
222reference = "https://attack.mitre.org/tactics/TA0005/"

Triage and analysis

Investigating Proxy Execution via Windows OpenSSH

Possible investigation steps

  • What OpenSSH execution path did the alert capture?
    • Why: "ProxyCommand" launches a local helper through the user's shell, "LocalCommand" runs locally after connection only when "PermitLocalCommand" is enabled, and remote command options shift the action to the SSH target.
    • Focus: process.name and process.command_line, separating "ProxyCommand", "LocalCommand", "RemoteCommand", chained "scp"/"sftp", shell/LOLBIN helpers, and loopback targets like "localhost" or "127.0.0.1".
    • Implication: escalate when the option runs "cmd.exe", "powershell.exe", "mshta.exe", "msiexec.exe", "schtasks.exe", a downloader, script, or chained copy/execution command; close is plausible only when it stays inside a recognized bastion, transfer, or deployment pattern with no execution-oriented helper.
  • Is the OpenSSH client and launcher context expected for that behavior?
    • Focus: process.executable, process.pe.original_file_name, process.code_signature.trusted, process.parent.executable, and process.parent.command_line, checking native "C:\Windows\System32\OpenSSH" use versus renamed or user-writable copies.
    • Implication: escalate when identity or lineage is inconsistent, such as an unsigned or renamed client, a user-writable path, Office/browser/script-host ancestry, or another LOLBin as the launcher; a native signed client lowers only masquerade risk and does not clear proxy execution.
  • Does the user and logon session fit recognized SSH automation on this host?
    • Focus: user.id, user.name, host.id, process.Ext.session_info.logon_type, and process.Ext.authentication_id.
    • Hint: if session origin matters, pivot on host.id from process.Ext.authentication_id to Windows Security winlog.event_data.TargetLogonId, then read source.ip and winlog.event_data.AuthenticationPackageName; search winlog.event_data.SubjectLogonId for explicit-credential event 4648. Missing Windows Security telemetry is unresolved, not benign. $investigate_4
    • Implication: escalate when the session is remote-interactive, network-origin, explicit-credential, or tied to a user/host pair that does not normally run this SSH pattern; lower concern only when the same identity, launcher, and command profile are recurrent for this host and no other evidence conflicts.
  • Did the client reach the destination implied by the SSH option path?
    • Focus: process-scoped DNS and connections for host.id and process.entity_id; read dns.question.name, dns.resolved_ip, destination.ip, and destination.port. $investigate_2
    • Hint: if process.entity_id is unavailable, rerun with host.id + process.pid in the alert window. Interpret DNS lookups separately from connections. Missing network or DNS telemetry is unresolved, not benign; loopback destination.ip supports proxy-execution when the command targets localhost.
    • Implication: escalate when the process reaches loopback listeners, rare public infrastructure, unrelated internal systems, or admin ports outside the expected SSH workflow; bounded destinations matching the same operator and command pattern reduce scope but do not override suspicious local execution.
  • Did the proxied path create local child execution or transfer artifacts?
    • Focus: child starts where process.parent.entity_id matches process.entity_id, plus manually queried file events scoped to the same process; read child process.command_line, staged file.path, and rename context from file.Ext.original.path. $investigate_3
    • Hint: if process-scoped pivots are unavailable, repeat with host.id + process.pid in a tight alert window and compare child commands or file writes to the OpenSSH option string. Missing file telemetry limits artifact review; it is not benign.
    • Implication: escalate when OpenSSH spawns shells, script hosts, scheduled-task helpers, or drops/copies executable, archive, or script content into new paths; absence of child/file evidence keeps the case process-local only when earlier command, lineage, and destination evidence fit.
  • If local evidence remains suspicious or unresolved, does the pattern recur beyond this event?
    • Focus: recent alerts for host.id, keyed to OpenSSH proxy execution, script hosts, downloaders, scheduled tasks, credential access, or the suspicious process.command_line fragment. $investigate_1
    • Hint: if host scope stays unresolved, pivot to user.id for the same launcher, command fragment, or recovered destination pattern across other hosts. $investigate_0
    • Implication: broaden scope when the same proxy-execution pattern, destination, or follow-on artifact appears on unrelated hosts or sessions; a single event supports closure only when the local evidence already binds to one exact recognized workflow and outside confirmation covers any legitimacy gap.
  • What disposition do command intent, identity, lineage, session, destination, artifacts, and recurrence support?
    • Implication: escalate for unauthorized local proxy execution, suspicious launcher/session context, rare or loopback destinations, staging, child shells, or repeated indirect execution; close only when alert-local evidence and recovery bind one recognized workflow on this host and outside confirmation verifies any telemetry gap; preserve evidence and escalate when evidence is mixed or incomplete.

False positive analysis

  • Recognized jump-host or bastion wrappers can use the native OpenSSH client with "ProxyCommand" for a fixed proxy helper or "RemoteCommand" for a bounded admin task. Confirm binary identity, launcher, option string, user.id, host.id, recovered destination, and child/file evidence all align with one workflow. Shell or LOLBin-bearing "ProxyCommand" and "LocalCommand" remain suspicious unless a controlled test or deployment wrapper is confirmed by telemetry and outside context.
  • Recognized transfer or sync jobs can use "sftp.exe" or chained "scp" from a fixed automation account or host. Confirm process.parent.executable, transfer-oriented process.command_line, recovered file.path, destination evidence, user.id, and host.id stay inside that product workflow. Keep the alert suspicious if child process.command_line activity, scheduled-task helpers, executable staging, or SSH configuration changes diverge from the transfer pattern.
  • Before creating an exception, validate recurrence for the same process.executable, process.parent.executable, option-bearing process.command_line, user.id, host.id, and recovered destination or transfer path. Avoid exceptions on "ssh.exe", "sftp.exe", or "Command=" alone.

Response and remediation

  • If confirmed benign, reverse temporary containment and document the exact command, launcher, user, host, destination or transfer path, and session evidence that justified closure. Create an exception only for the minimum recurring workflow pattern, not for OpenSSH use in general.
  • If suspicious but unconfirmed, preserve the alert, Timeline view, command line, parent/child process tree, recovered destination or DNS evidence, staged/copied files, relevant authentication records, and SSH client/server configuration files before containment or cleanup. Apply reversible containment first, such as temporary destination restrictions or heightened monitoring for the affected user and host, and avoid process termination until scope is clearer.
  • If confirmed malicious, isolate the host or contain the account when command intent, launcher lineage, destination, authentication, or artifact evidence shows unauthorized proxy execution. Weigh host criticality before isolation, block confirmed malicious destinations or hashes, and record the process instance and artifact identifiers before killing processes or deleting files.
  • Eradicate only the artifacts and settings found during the investigation: copied payloads, scripts, scheduled tasks, downloaded content, unauthorized "ProxyCommand", "LocalCommand", or "PermitLocalCommand" settings, and any malicious key material such as unexpected ".ssh\authorized_keys" or "%PROGRAMDATA%\ssh\administrators_authorized_keys" entries. Then remediate the launcher or access path that allowed the OpenSSH proxy launch.
  • Rotate credentials, tokens, and SSH keys when authentication records, session origin, transferred files, or key artifacts show explicit-credential abuse, privileged account misuse, or unauthorized key-based access. Review adjacent admin sessions for the same source.ip, host.id, or user.id before restoring normal access.
  • Post-incident hardening: restrict OpenSSH client use to recognized bastion, deployment, or transfer hosts where feasible; disable "PermitLocalCommand" unless required; review "%PROGRAMDATA%\ssh\ssh_config" and affected users' ".ssh\config" for unauthorized command options; retain the confirmed command, parent, destination, and user pattern for future triage and exception review.

References

Related rules

to-top