EXPLORE
← Back to Explore
elastichighTTP

Potential AMSI Bypass via RPC Runtime Hooking

Identifies PowerShell script block content associated with an Antimalware Scan Interface (AMSI) bypass that hooks the RPC runtime marshaling stub NdrClientCall3 (or NdrClientCall2) in rpcrt4.dll. Unlike bypasses that patch AmsiScanBuffer or set amsiInitFailed, this technique operates at the RPC layer used by AMSI to delegate scan requests to the antivirus provider, tampering with the request before it reaches the engine and leaving AMSI itself unmodified. The loader allocates an executable trampoline and marshals a delegate to the native stub; these primitives appear in PowerShell Script Block Logging before the hook takes effect.

MITRE ATT&CK

defense-evasionexecution

Detection Query

event.code: "4104" and host.os.type: "windows" and
  powershell.file.script_block_text : (
    "NdrClientCall" or
    "NdrClientCall2" or
    "NdrClientCall3"
  ) and
  powershell.file.script_block_text : (
    "GetProcAddress" or
    "GetDelegateForFunctionPointer" or
    "VirtualProtect"
  )

Author

Elastic

Created

2026/06/22

Data Sources

PowerShell Logswinlogbeat-*logs-windows.powershell*

Tags

Domain: EndpointOS: WindowsUse Case: Threat DetectionTactic: Defense EvasionTactic: ExecutionData Source: PowerShell LogsResources: Investigation Guide
Raw Content
[metadata]
creation_date = "2026/06/22"
integration = ["windows"]
maturity = "production"
updated_date = "2026/06/22"

[rule]
author = ["Elastic"]
description = """
Identifies PowerShell script block content associated with an Antimalware Scan Interface (AMSI) bypass that hooks the
RPC runtime marshaling stub NdrClientCall3 (or NdrClientCall2) in rpcrt4.dll. Unlike bypasses that patch AmsiScanBuffer
or set amsiInitFailed, this technique operates at the RPC layer used by AMSI to delegate scan requests to the
antivirus provider, tampering with the request before it reaches the engine and leaving AMSI itself unmodified. The
loader allocates an executable trampoline and marshals a delegate to the native stub; these primitives appear in
PowerShell Script Block Logging before the hook takes effect.
"""
from = "now-9m"
index = ["winlogbeat-*", "logs-windows.powershell*"]
language = "kuery"
license = "Elastic License v2"
name = "Potential AMSI Bypass via RPC Runtime Hooking"
note = """## Triage and analysis

### Investigating Potential AMSI Bypass via RPC Runtime Hooking

The Antimalware Scan Interface (AMSI) delegates scan requests to the registered antivirus provider over RPC. By hooking
the RPC runtime marshaling stub `NdrClientCall3`/`NdrClientCall2` in `rpcrt4.dll`, an adversary can tamper with these
requests so that malicious content is reported as clean, without modifying `amsi.dll` or patching `AmsiScanBuffer`.
This makes the bypass harder to catch via AMSI buffer or memory-write telemetry; the most reliable host artifact is the
loader's own PowerShell script content, captured by Script Block Logging.

#### Possible investigation steps

- Review `powershell.file.script_block_text` for references to `NdrClientCall3`/`NdrClientCall2`, resolution of
  functions in `rpcrt4.dll`, allocation of executable (`PAGE_EXECUTE_READWRITE`) memory, and delegate marshaling via
  `GetDelegateForFunctionPointer`.
- PowerShell logs each statement as a separate event; pivot on `host.id` and the PowerShell process id to reconstruct
  the full loader sequence and confirm intent.
- Identify how PowerShell was launched (interactive, encoded command, remote session) and review the parent process
  tree for download or staging activity.
- Correlate with Elastic Defend endpoint telemetry on the same host around the same time. Note that this RPC-layer hook
  may not surface as a memory modification of `rpcrt4.dll`, which is expected for this technique.
- Hunt for the same script content, user, or host pattern across the environment.

### False positive analysis

- Security research, detection engineering, and red-team development that legitimately references the RPC runtime
  marshaling functions or allocates executable memory from PowerShell can match. Validate the user, host, parent
  process, and surrounding script blocks against authorized testing before closing as benign, and add exceptions for
  known testing identities or hosts.

### Response and remediation

- Isolate the host if the activity is confirmed malicious and review for follow-on payload execution that the bypass
  was intended to conceal.
- Terminate the offending PowerShell session and preserve the Script Block Logging events for analysis.
- Restrict PowerShell usage outside of IT and engineering business units using GPOs, AppLocker, or WDAC.
- Reset credentials for accounts active on the host during the session if follow-on activity is observed.

### Limitations

This rule detects the technique only when it is delivered as logged PowerShell script-block text. In-memory delivery, a
PowerShell v2 downgrade, or implementing the hook outside PowerShell (compiled binary) can evade Script Block Logging
and therefore this rule. For customers running Elastic Defend, prefer the companion Endpoint Rule which detects the
VirtualProtect call targeting rpcrt4.dll!NdrClientCall* directly via API telemetry and is not dependent on Script
Block Logging."""
references = [
    "https://github.com/andreisss/Ghosting-AMSI",
]
risk_score = 73
rule_id = "4027f24e-b02f-4605-872f-7bafd8fe1b33"
setup = """## Setup

PowerShell Script Block Logging must be enabled to generate the events used by this rule (event ID 4104). The
`Microsoft-Windows-PowerShell/Operational` channel must be collected by the Windows integration or Winlogbeat.

Setup instructions: https://ela.st/powershell-logging-setup"""
severity = "high"
tags = [
    "Domain: Endpoint",
    "OS: Windows",
    "Use Case: Threat Detection",
    "Tactic: Defense Evasion",
    "Tactic: Execution",
    "Data Source: PowerShell Logs",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "query"

query = '''
event.code: "4104" and host.os.type: "windows" and
  powershell.file.script_block_text : (
    "NdrClientCall" or
    "NdrClientCall2" or
    "NdrClientCall3"
  ) and
  powershell.file.script_block_text : (
    "GetProcAddress" or
    "GetDelegateForFunctionPointer" or
    "VirtualProtect"
  )
'''

[rule.investigation_fields]
field_names = [
    "@timestamp",
    "host.id",
    "host.name",
    "user.id",
    "user.name",
    "user.domain",
    "process.pid",
    "powershell.file.script_block_text",
    "powershell.file.script_block_id",
    "powershell.sequence",
    "powershell.total",
    "file.path",
]

[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1562"
name = "Impair Defenses"
reference = "https://attack.mitre.org/techniques/T1562/"

[[rule.threat.technique.subtechnique]]
id = "T1562.001"
name = "Disable or Modify Tools"
reference = "https://attack.mitre.org/techniques/T1562/001/"

[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"

[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"

[[rule.threat.technique.subtechnique]]
id = "T1059.001"
name = "PowerShell"
reference = "https://attack.mitre.org/techniques/T1059/001/"

[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"