Potential PowerShell Pass-the-Hash/Relay Script

Detects PowerShell scripts associated with NTLM relay or pass-the-hash tooling and SMB/NTLM negotiation artifacts. Attackers use relay and PtH techniques to authenticate without passwords and pivot to other systems.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2024/03/27"
  3integration = ["windows"]
  4maturity = "production"
  5updated_date = "2026/04/27"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Detects PowerShell scripts associated with NTLM relay or pass-the-hash tooling and SMB/NTLM negotiation
 11artifacts. Attackers use relay and PtH techniques to authenticate without passwords and pivot to other systems.
 12"""
 13from = "now-9m"
 14index = ["logs-windows.powershell*", "winlogbeat-*"]
 15language = "kuery"
 16license = "Elastic License v2"
 17name = "Potential PowerShell Pass-the-Hash/Relay Script"
 18references = [
 19    "https://github.com/Kevin-Robertson/Invoke-TheHash/blob/master/Invoke-WMIExec.ps1",
 20    "https://github.com/Kevin-Robertson/Invoke-TheHash/blob/master/Invoke-SMBExec.ps1",
 21    "https://github.com/dafthack/Check-LocalAdminHash/blob/master/Check-LocalAdminHash.ps1",
 22    "https://github.com/nettitude/PoshC2/blob/master/resources/modules/Invoke-Tater.ps1",
 23    "https://github.com/Kevin-Robertson/Inveigh/blob/master/Inveigh.ps1",
 24]
 25risk_score = 73
 26rule_id = "951779c2-82ad-4a6c-82b8-296c1f691449"
 27severity = "high"
 28tags = [
 29    "Domain: Endpoint",
 30    "OS: Windows",
 31    "Use Case: Threat Detection",
 32    "Tactic: Credential Access",
 33    "Resources: Investigation Guide",
 34    "Data Source: PowerShell Logs",
 35]
 36timestamp_override = "event.ingested"
 37type = "query"
 38
 39query = '''
 40event.category:process and host.os.type:windows and
 41  powershell.file.script_block_text : (
 42    ("NTLMSSPNegotiate" and ("NegotiateSMB" or "NegotiateSMB2")) or
 43    "4E544C4D53535000" or
 44    "0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50" or
 45    "0x4e,0x54,0x20,0x4c,0x4d" or
 46    "0x53,0x4d,0x42,0x20,0x32" or
 47    "0x81,0xbb,0x7a,0x36,0x44,0x98,0xf1,0x35,0xad,0x32,0x98,0xf0,0x38"
 48  ) and
 49  not file.directory : "C:\ProgramData\Microsoft\Windows Defender Advanced Threat Protection\Downloads"
 50'''
 51
 52note = """## Triage and analysis
 53
 54### Investigating Potential PowerShell Pass-the-Hash/Relay Script
 55
 56#### Possible investigation steps
 57
 58- Does the visible script content implement relay-listener behavior, pass-the-hash execution, target validation, or more than one?
 59  - Focus: `powershell.file.script_block_text` and any file-backed `file.path`.
 60  - Implication: escalate when the fragment builds NTLMSSP/SMB messages, starts HTTP/SMB/proxy listeners, forwards authentication, references WMI/SMB/WinRM helpers, or pairs byte arrays with target selection; lower suspicion only for parser or lab text with no targeting, listener, credential, or execution logic.
 61
 62- Does full script reconstruction reveal target lists, credential material, service names, or listener settings the matching fragment did not show?
 63  - Why: script block logging often splits relay mode, target configuration, and execution helpers into separate fragments.
 64  - Focus: `powershell.file.script_block_id`, `powershell.sequence`, `powershell.total`, and reconstructed `powershell.file.script_block_text` on `host.id`; reassemble by `powershell.sequence` and treat missing fragments as unresolved. $investigate_0
 65  - Implication: escalate on remote targets, password hashes, "TargetList", "SMBRelayTarget", "wpad.dat", "WPAD", listener ports, service names, execution helpers, or fan-out logic; lower suspicion only for research or validation code with no credential material, targets, listener, or execution path.
 66
 67- Which recovered PowerShell process explains launch context and downstream pivots?
 68  - Focus: If endpoint process telemetry exists, recover the matching process via `host.id + process.pid` before interpreting `process.*` or `process.parent.*`; if absent, keep launch context unresolved. Record `process.command_line`, `process.parent.executable`, `process.Ext.session_info.logon_type`, `process.Ext.authentication_id`, and `process.entity_id`. $investigate_1
 69  - Implication: escalate when the recovered launch shows encoded commands, remote-administration launchers, Office or browser parents, elevated context, or service/network logon sessions that do not fit the user; do not infer legitimacy from `process.pid` alone.
 70
 71- Do authentication events show local operator context or relay/PtH follow-on?
 72  - Focus: same-`host.id`/`user.id` Windows Security events for `event.code` 4624, 4625, or 4648; review `source.ip`, `winlog.event_data.AuthenticationPackageName`, and `winlog.logon.type`. $investigate_2
 73  - Hint: bridge recovered `process.Ext.authentication_id` to `winlog.event_data.TargetLogonId` only for the local session; prove relay/PtH with same-host inbound NTLM without `user.id`, target-host 4624/4625, or DC-side 4776 for reconstructed targets or sources.
 74  - Implication: escalate when local, target-host, or DC authentication shows unexpected type 3 NTLM, repeated failures, privileged sessions, or explicit-credential use tied to reconstructed targets; missing authentication telemetry is unresolved, not benign.
 75
 76- Do network events show outbound relay or hash-validation activity to targets named in the script?
 77  - Why: relay code often resolves targets, then reaches SMB, RPC, WinRM, or HTTP destinations; listener-only capture may not.
 78  - Focus: If endpoint network telemetry exists, scope same-host events to `host.id`, `process.pid`, and the alert window, or use reconstructed targets when process identity is unavailable; separate DNS (`dns.question.name`, `dns.resolved_ip`) from connections (`destination.ip`, `destination.port`). $investigate_3
 79  - Implication: escalate when the host fans out to named targets or reaches SMB, RPC, WinRM, or relay ports (80, 443, 445) outside the declared test scope; listener scripts stay suspicious with little outbound traffic when reconstruction shows "SMBRelayTarget", "wpad.dat", or similar relay-ready settings. Missing network telemetry is unresolved, not benign.
 80
 81- If local evidence remains suspicious or incomplete, do related alerts widen the user or host scope?
 82  - Focus: related alerts for `user.id`; if quiet, compare `host.id` for adjacent relay, pass-the-hash, remote-service, persistence, or post-compromise alerts.
 83    - $investigate_4
 84    - $investigate_5
 85  - Implication: broaden when local evidence stays suspicious or unresolved and related alerts show connected relay, listener-only capture, hash-validation fan-out, or lateral-movement behavior; keep local when surrounding alerts are absent or confined to the same declared test scope.
 86
 87- Weigh script intent, reconstruction, launch context, authentication follow-on, network contact, and related-alert scope: escalate when they align on unauthorized relay or pass-the-hash activity; close only when reconstructed content, launch, source, and available auth or network evidence support one controlled workflow with no contradictions; preserve and escalate if evidence is mixed or incomplete.
 88
 89### False positive analysis
 90
 91- Controlled testing, incident-response, or protocol-research workflows can trigger this rule when reconstructed `powershell.file.script_block_text` names only recognized targets or stays limited to parser/validation routines, recovered launch context matches the operator workflow, and authentication or network telemetry shows no live relay, listener, or execution outside scope. If engagement or change records exist, require alignment; otherwise, confirm recurring `user.id`, `host.id`, source pattern, recovered launch pattern, and target or port pattern across prior alerts from this rule. Do not close if hashes, listener ports, or follow-on authentication diverge.
 92- Before creating an exception, validate recurring `user.id`, `host.id`, recovered launch pattern, source pattern, and target set across prior alerts. Build the exception from that minimum workflow. Avoid exceptions on byte sequences alone, `user.name` alone, or host alone.
 93
 94### Response and remediation
 95
 96- If confirmed benign, reverse any temporary containment and document the script content, recovered launch chain, source path pattern, target set, and session origin or logon-type pattern that proved the lab, response, or troubleshooting workflow. Create an exception only if the same workflow recurs across prior alerts from this rule.
 97- If suspicious but unconfirmed, preserve the reconstructed `powershell.file.script_block_text`, `powershell.file.script_block_id`, fragment ordering metadata, recovered process and parent details, named targets, listener ports, service names, UNC paths, and related authentication or network evidence before destructive actions.
 98- Apply reversible containment first, such as host isolation if tolerable or temporary controls on exposed accounts and named targets. Avoid terminating processes or deleting artifacts until possible credential misuse and lateral movement scope is clearer.
 99- If confirmed malicious, isolate the endpoint when possible; otherwise escalate with the recorded `host.id`, recovered process identifiers, source IP values, named targets, and authentication evidence. Rotate or invalidate impacted credentials, review named systems for successful SMB/WMI/WinRM/service activity, restrict exposed NTLM pathways where supported, then remove only the scripts, services, scheduled tasks, persistence, or staging artifacts found during the investigation.
100- Retain PowerShell Script Block Logging plus supporting endpoint, authentication, and network telemetry. Document adjacent variants such as listener-only capture, hash-validation fan-out, or non-PowerShell relay/pass-the-hash alerts for detection follow-up.
101"""
102
103setup = """## Setup
104
105PowerShell Script Block Logging must be enabled to generate the events used by this rule (e.g., 4104).
106Setup instructions: https://ela.st/powershell-logging-setup
107"""
108
109[rule.investigation_fields]
110field_names = [
111    "@timestamp",
112    "user.name",
113    "user.id",
114    "user.domain",
115    "powershell.file.script_block_text",
116    "powershell.file.script_block_id",
117    "powershell.sequence",
118    "powershell.total",
119    "file.path",
120    "file.directory",
121    "file.name",
122    "process.pid",
123    "host.name",
124    "host.id",
125    "powershell.file.script_block_length"
126]
127
128[transform]
129
130[[transform.investigate]]
131label = "All PowerShell 4104 fragments for this script on this host"
132description = ""
133providers = [
134  [
135    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
136    { excluded = false, field = "powershell.file.script_block_id", queryType = "phrase", value = "{{powershell.file.script_block_id}}", valueType = "string" },
137    { excluded = false, field = "event.code", queryType = "phrase", value = "4104", valueType = "string" }
138  ]
139]
140relativeFrom = "now-1h"
141relativeTo = "now"
142
143[[transform.investigate]]
144label = "Process events for the PowerShell instance"
145description = ""
146providers = [
147  [
148    { excluded = false, field = "process.pid", queryType = "phrase", value = "{{process.pid}}", valueType = "string" },
149    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
150    { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" }
151  ]
152]
153relativeFrom = "now-1h"
154relativeTo = "now"
155
156[[transform.investigate]]
157label = "Windows Security authentication events for the user"
158description = ""
159providers = [
160  [
161    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
162    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" },
163    { excluded = false, field = "event.code", queryType = "phrase", value = "4624", valueType = "string" }
164  ],
165  [
166    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
167    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" },
168    { excluded = false, field = "event.code", queryType = "phrase", value = "4625", valueType = "string" }
169  ],
170  [
171    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
172    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" },
173    { excluded = false, field = "event.code", queryType = "phrase", value = "4648", valueType = "string" }
174  ]
175]
176relativeFrom = "now-24h"
177relativeTo = "now"
178
179[[transform.investigate]]
180label = "Network events for the PowerShell process"
181description = ""
182providers = [
183  [
184    { excluded = false, field = "process.pid", queryType = "phrase", value = "{{process.pid}}", valueType = "string" },
185    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
186    { excluded = false, field = "event.category", queryType = "phrase", value = "network", valueType = "string" }
187  ]
188]
189relativeFrom = "now-1h"
190relativeTo = "now"
191
192[[transform.investigate]]
193label = "Alerts associated with the user"
194description = ""
195providers = [
196  [
197    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
198    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
199  ]
200]
201relativeFrom = "now-48h/h"
202relativeTo = "now"
203
204[[transform.investigate]]
205label = "Alerts associated with the host"
206description = ""
207providers = [
208  [
209    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
210    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
211  ]
212]
213relativeFrom = "now-48h/h"
214relativeTo = "now"
215
216[[rule.threat]]
217framework = "MITRE ATT&CK"
218
219[[rule.threat.technique]]
220id = "T1557"
221name = "Adversary-in-the-Middle"
222reference = "https://attack.mitre.org/techniques/T1557/"
223
224[[rule.threat.technique.subtechnique]]
225id = "T1557.001"
226name = "LLMNR/NBT-NS Poisoning and SMB Relay"
227reference = "https://attack.mitre.org/techniques/T1557/001/"
228
229[rule.threat.tactic]
230id = "TA0006"
231name = "Credential Access"
232reference = "https://attack.mitre.org/tactics/TA0006/"
233
234[[rule.threat]]
235framework = "MITRE ATT&CK"
236
237[[rule.threat.technique]]
238id = "T1059"
239name = "Command and Scripting Interpreter"
240reference = "https://attack.mitre.org/techniques/T1059/"
241
242[[rule.threat.technique.subtechnique]]
243id = "T1059.001"
244name = "PowerShell"
245reference = "https://attack.mitre.org/techniques/T1059/001/"
246
247[rule.threat.tactic]
248id = "TA0002"
249name = "Execution"
250reference = "https://attack.mitre.org/tactics/TA0002/"
251
252[[rule.threat]]
253framework = "MITRE ATT&CK"
254
255[[rule.threat.technique]]
256id = "T1550"
257name = "Use Alternate Authentication Material"
258reference = "https://attack.mitre.org/techniques/T1550/"
259
260[[rule.threat.technique.subtechnique]]
261id = "T1550.002"
262name = "Pass the Hash"
263reference = "https://attack.mitre.org/techniques/T1550/002/"
264
265[rule.threat.tactic]
266id = "TA0008"
267name = "Lateral Movement"
268reference = "https://attack.mitre.org/tactics/TA0008/"

Triage and analysis

Investigating Potential PowerShell Pass-the-Hash/Relay Script

Possible investigation steps

  • Does the visible script content implement relay-listener behavior, pass-the-hash execution, target validation, or more than one?

    • Focus: powershell.file.script_block_text and any file-backed file.path.
    • Implication: escalate when the fragment builds NTLMSSP/SMB messages, starts HTTP/SMB/proxy listeners, forwards authentication, references WMI/SMB/WinRM helpers, or pairs byte arrays with target selection; lower suspicion only for parser or lab text with no targeting, listener, credential, or execution logic.
  • Does full script reconstruction reveal target lists, credential material, service names, or listener settings the matching fragment did not show?

    • Why: script block logging often splits relay mode, target configuration, and execution helpers into separate fragments.
    • Focus: powershell.file.script_block_id, powershell.sequence, powershell.total, and reconstructed powershell.file.script_block_text on host.id; reassemble by powershell.sequence and treat missing fragments as unresolved. $investigate_0
    • Implication: escalate on remote targets, password hashes, "TargetList", "SMBRelayTarget", "wpad.dat", "WPAD", listener ports, service names, execution helpers, or fan-out logic; lower suspicion only for research or validation code with no credential material, targets, listener, or execution path.
  • Which recovered PowerShell process explains launch context and downstream pivots?

    • Focus: If endpoint process telemetry exists, recover the matching process via host.id + process.pid before interpreting process.* or process.parent.*; if absent, keep launch context unresolved. Record process.command_line, process.parent.executable, process.Ext.session_info.logon_type, process.Ext.authentication_id, and process.entity_id. $investigate_1
    • Implication: escalate when the recovered launch shows encoded commands, remote-administration launchers, Office or browser parents, elevated context, or service/network logon sessions that do not fit the user; do not infer legitimacy from process.pid alone.
  • Do authentication events show local operator context or relay/PtH follow-on?

    • Focus: same-host.id/user.id Windows Security events for event.code 4624, 4625, or 4648; review source.ip, winlog.event_data.AuthenticationPackageName, and winlog.logon.type. $investigate_2
    • Hint: bridge recovered process.Ext.authentication_id to winlog.event_data.TargetLogonId only for the local session; prove relay/PtH with same-host inbound NTLM without user.id, target-host 4624/4625, or DC-side 4776 for reconstructed targets or sources.
    • Implication: escalate when local, target-host, or DC authentication shows unexpected type 3 NTLM, repeated failures, privileged sessions, or explicit-credential use tied to reconstructed targets; missing authentication telemetry is unresolved, not benign.
  • Do network events show outbound relay or hash-validation activity to targets named in the script?

    • Why: relay code often resolves targets, then reaches SMB, RPC, WinRM, or HTTP destinations; listener-only capture may not.
    • Focus: If endpoint network telemetry exists, scope same-host events to host.id, process.pid, and the alert window, or use reconstructed targets when process identity is unavailable; separate DNS (dns.question.name, dns.resolved_ip) from connections (destination.ip, destination.port). $investigate_3
    • Implication: escalate when the host fans out to named targets or reaches SMB, RPC, WinRM, or relay ports (80, 443, 445) outside the declared test scope; listener scripts stay suspicious with little outbound traffic when reconstruction shows "SMBRelayTarget", "wpad.dat", or similar relay-ready settings. Missing network telemetry is unresolved, not benign.
  • If local evidence remains suspicious or incomplete, do related alerts widen the user or host scope?

    • Focus: related alerts for user.id; if quiet, compare host.id for adjacent relay, pass-the-hash, remote-service, persistence, or post-compromise alerts.
      • $investigate_4
      • $investigate_5
    • Implication: broaden when local evidence stays suspicious or unresolved and related alerts show connected relay, listener-only capture, hash-validation fan-out, or lateral-movement behavior; keep local when surrounding alerts are absent or confined to the same declared test scope.
  • Weigh script intent, reconstruction, launch context, authentication follow-on, network contact, and related-alert scope: escalate when they align on unauthorized relay or pass-the-hash activity; close only when reconstructed content, launch, source, and available auth or network evidence support one controlled workflow with no contradictions; preserve and escalate if evidence is mixed or incomplete.

False positive analysis

  • Controlled testing, incident-response, or protocol-research workflows can trigger this rule when reconstructed powershell.file.script_block_text names only recognized targets or stays limited to parser/validation routines, recovered launch context matches the operator workflow, and authentication or network telemetry shows no live relay, listener, or execution outside scope. If engagement or change records exist, require alignment; otherwise, confirm recurring user.id, host.id, source pattern, recovered launch pattern, and target or port pattern across prior alerts from this rule. Do not close if hashes, listener ports, or follow-on authentication diverge.
  • Before creating an exception, validate recurring user.id, host.id, recovered launch pattern, source pattern, and target set across prior alerts. Build the exception from that minimum workflow. Avoid exceptions on byte sequences alone, user.name alone, or host alone.

Response and remediation

  • If confirmed benign, reverse any temporary containment and document the script content, recovered launch chain, source path pattern, target set, and session origin or logon-type pattern that proved the lab, response, or troubleshooting workflow. Create an exception only if the same workflow recurs across prior alerts from this rule.
  • If suspicious but unconfirmed, preserve the reconstructed powershell.file.script_block_text, powershell.file.script_block_id, fragment ordering metadata, recovered process and parent details, named targets, listener ports, service names, UNC paths, and related authentication or network evidence before destructive actions.
  • Apply reversible containment first, such as host isolation if tolerable or temporary controls on exposed accounts and named targets. Avoid terminating processes or deleting artifacts until possible credential misuse and lateral movement scope is clearer.
  • If confirmed malicious, isolate the endpoint when possible; otherwise escalate with the recorded host.id, recovered process identifiers, source IP values, named targets, and authentication evidence. Rotate or invalidate impacted credentials, review named systems for successful SMB/WMI/WinRM/service activity, restrict exposed NTLM pathways where supported, then remove only the scripts, services, scheduled tasks, persistence, or staging artifacts found during the investigation.
  • Retain PowerShell Script Block Logging plus supporting endpoint, authentication, and network telemetry. Document adjacent variants such as listener-only capture, hash-validation fan-out, or non-PowerShell relay/pass-the-hash alerts for detection follow-up.

References

Related rules

to-top