Detection rules › Elastic

Suspicious Microsoft Diagnostics Wizard Execution

Status
production
Severity
high
Time window
9m
Author
Elastic
Source
github.com/elastic/detection-rules

Identifies potential abuse of the Microsoft Diagnostics Troubleshooting Wizard (MSDT) to proxy malicious command or binary execution via malicious process arguments.

MITRE ATT&CK coverage

Event coverage

Rule body elastic

[metadata]
creation_date = "2022/05/31"
integration = ["endpoint", "windows", "m365_defender", "crowdstrike", "sentinel_one_cloud_funnel"]
maturity = "production"
updated_date = "2026/04/30"

[rule]
author = ["Elastic"]
description = """
Identifies potential abuse of the Microsoft Diagnostics Troubleshooting Wizard (MSDT) to proxy malicious command or
binary execution via malicious process arguments.
"""
from = "now-9m"
index = [
    "logs-endpoint.events.process-*",
    "winlogbeat-*",
    "logs-windows.sysmon_operational-*",
    "endgame-*",
    "logs-m365_defender.event-*",
    "logs-crowdstrike.fdr*",
    "logs-sentinel_one_cloud_funnel.*",
]
language = "eql"
license = "Elastic License v2"
name = "Suspicious Microsoft Diagnostics Wizard Execution"
references = [
    "https://twitter.com/nao_sec/status/1530196847679401984",
    "https://lolbas-project.github.io/lolbas/Binaries/Msdt/",
]
risk_score = 73
rule_id = "2c3c29a4-f170-42f8-a3d8-2ceebc18eb6a"
severity = "high"
tags = [
    "Domain: Endpoint",
    "OS: Windows",
    "Use Case: Threat Detection",
    "Tactic: Defense Evasion",
    "Data Source: Elastic Endgame",
    "Data Source: Elastic Defend",
    "Data Source: Microsoft Defender XDR",
    "Data Source: Sysmon",
    "Data Source: Crowdstrike",
    "Data Source: SentinelOne",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"

query = '''
process where host.os.type == "windows" and event.type == "start" and
  (?process.pe.original_file_name == "msdt.exe" or process.name : "msdt.exe") and
  (
    process.args : ("IT_RebrowseForFile=*", "*FromBase64*", "*/../../../*", "IT_BrowseForFile=*") or
    (
      process.args : ("-af", "/af") and process.args : "/skip" and
      process.parent.name : ("explorer.exe", "cmd.exe", "powershell.exe", "cscript.exe", "wscript.exe", "mshta.exe", "rundll32.exe", "regsvr32.exe") and
      process.args : ("?:\\WINDOWS\\diagnostics\\index\\PCWDiagnostic.xml", "PCWDiagnostic.xml", "?:\\Users\\Public\\*", "?:\\Windows\\Temp\\*")
    ) or

    (process.pe.original_file_name == "msdt.exe" and not process.name : "msdt.exe" and process.name != null) or

    (
      ?process.pe.original_file_name == "msdt.exe" and
      not process.executable : (
        "?:\\Windows\\system32\\msdt.exe",
        "?:\\Windows\\SysWOW64\\msdt.exe",
        /* Crowdstrike specific exclusion as it uses NT Object paths */
        "\\Device\\HarddiskVolume*\\Windows\\system32\\msdt.exe",
        "\\Device\\HarddiskVolume*\\Windows\\SysWOW64\\msdt.exe"
      )
    )
  )
'''

note = """## Triage and analysis

### Investigating Suspicious Microsoft Diagnostics Wizard Execution

#### Possible investigation steps

- Does the alert show MSDT proxy-execution behavior or a bounded diagnostic launch?
  - Why: MSDT abuse depends on PCWDiagnostic answer files, rebrowse or browse-file parameters, traversal, or encoded input, not on "msdt.exe" alone.
  - Focus: `process.command_line` and `process.args`, classifying answer-file use, rebrowse or browse-file parameters, encoded input, traversal, and package location.
  - Implication: escalate when arguments point to attacker-controlled content, encoded or traversal input, or user-writable answer files; lower concern only when they resolve to a recognized local diagnostic pack with no external, encoded, traversal, or user-writable references.

- Do binary identity and launcher lineage fit a legitimate diagnostic launch?
  - Focus: `process.executable`, `process.pe.original_file_name`, `process.code_signature.trusted`, `process.parent.executable`, and `process.parent.command_line`.
  - Implication: escalate when MSDT is renamed, relocated, unsigned or untrusted, or launched by Office, a browser, script host, "mshta.exe", "rundll32.exe", "regsvr32.exe", or a shell using profile or temp content; lower concern when a trusted Windows MSDT path and signed helpdesk, OEM, or management parent launch the same diagnostic pack.

- Did MSDT or a diagnostic-host child launch another binary or script?
  - Focus: child process events where `process.parent.entity_id` matches alert `process.entity_id`; record child `process.entity_id`, `process.executable`, `process.command_line`, and `process.code_signature.trusted`. $investigate_0
  - Hint: if the first child is a signed diagnostic host, inspect that child's descendants before treating the chain as contained.
  - Implication: escalate when MSDT or its diagnostic-host child launches shells, script interpreters, "mshta.exe", "regsvr32.exe", "rundll32.exe", unsigned payloads, or content from user-writable paths; lower concern when the child chain stays inside expected Microsoft or OEM diagnostic components.

- Do file events show package staging or later execution?
  - Focus: if file telemetry exists, pivot with `host.id` plus alert `process.entity_id`, parent `process.parent.entity_id`, direct-child parent linkage, and exact referenced paths when present; otherwise use `host.id`, `process.pid`, and alert-time window for referenced path, provenance, write timing, and later execution. Missing file telemetry is unresolved, not benign. $investigate_1
  - Implication: escalate when the package appears in Public, Temp, profile, share, or newly written staging paths, carries web or archive provenance, or later executes; lower concern only when artifact evidence stays bound to the same recognized diagnostic package.

- If remote delivery is suggested, do optional network events show retrieval or external control?
  - Focus: when network telemetry exists, query with `host.id` plus alert `process.entity_id` or alert-backed `process.parent.entity_id`, separating DNS from connections. Review child-process network activity from recovered child results. Missing network telemetry is unresolved, not benign. $investigate_2
  - Implication: escalate when the parent, MSDT, or child chain retrieves remote HTML/package content or contacts unrelated infrastructure; lower concern only when available network evidence stays local or vendor-aligned with the same diagnostic package.

- If local evidence is suspicious or unresolved, does related alert history broaden scope?
  - Focus: compare related alerts for `user.id` and `host.id` over 48 hours for recurring MSDT command patterns, parent launchers, package paths, child payloads, or remote indicators. $investigate_3 $investigate_4
  - Implication: broaden scope when the same proxy-execution pattern appears across unrelated hosts or users; keep response local only when current process, file, child, and network evidence bind one recognized diagnostic workflow.

- What disposition is supported?
  - Weigh command-line intent, image identity, parent lineage, package evidence, child or descendant processes, and file or network corroboration; escalate proxy execution or payload delivery, close only when evidence binds one recognized diagnostic workflow, and preserve artifacts when evidence is mixed or incomplete.

### False positive analysis

- Helpdesk, OEM troubleshooting, software deployment, or validation can trigger this rule when a signed support or management parent starts Microsoft-signed MSDT from a standard Windows path, uses the same controlled local diagnostic pack, and produces the same child-process set. Close only when parent path and command line, MSDT path and signature, command line, package path, child behavior, `user.id`, and `host.id` align in the current case; records can corroborate but not replace telemetry.
- Do not create exceptions on `process.name`, `process.pe.original_file_name`, or Microsoft signature alone.

### Response and remediation

- If confirmed benign:
  - Reverse temporary containment and document the process, parent, package, and child-process evidence. Build exceptions only from the confirmed parent path plus command-line/package pattern plus `host.id` or `user.id`, not from "msdt.exe" alone.
- If suspicious but unconfirmed:
  - Preserve the alert, MSDT `process.entity_id`, `process.pid`, `process.command_line`, `process.args`, parent evidence, package path, child identifiers, suspicious package copies, and remote indicators.
  - Apply reversible containment for the affected `host.id` and `user.id`, such as temporary network restrictions, heightened monitoring, or child-process blocking. Isolate only for spawned payload behavior or high host criticality.
- If confirmed malicious:
  - Isolate the host or escalate after preserving the MSDT and child identifiers, package paths, payload paths, command lines, and remote indicators.
  - Terminate MSDT, diagnostic-host, and payload processes only after recording identifiers; block malicious child binaries, package paths, domains, and IP indicators.
  - Remove malicious ".xml", ".msi", ".diagcab", remote package, or payload artifacts, then remediate the parent document, browser, script, or management path.
- Post-incident hardening:
  - Restrict MSDT where business use no longer requires it, verify Follina-era mitigations, and retain process, file, and network telemetry for MSDT, parents, and children.
"""

setup = """## 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

### Additional data sources

This rule also supports the following third-party data sources. For setup instructions, refer to the links below:

- [CrowdStrike](https://ela.st/crowdstrike-integration)
- [Microsoft Defender XDR](https://ela.st/m365-defender)
- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
- [Sysmon Event ID 1 - Process Creation](https://ela.st/sysmon-event-1-setup)
"""

[rule.investigation_fields]
field_names = [
    "@timestamp",
    "host.name",
    "host.id",
    "user.name",
    "user.id",
    "process.entity_id",
    "process.pid",
    "process.executable",
    "process.command_line",
    "process.args",
    "process.pe.original_file_name",
    "process.code_signature.trusted",
    "process.parent.entity_id",
    "process.parent.executable",
    "process.parent.command_line",
]

[transform]

[[transform.investigate]]
label = "Child process events for the same MSDT instance"
description = ""
providers = [
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.parent.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
  ]
]
relativeFrom = "now-1h"
relativeTo = "now"

[[transform.investigate]]
label = "File events for parent and child processes"
description = ""
providers = [
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "file", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
  ],
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "file", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.parent.entity_id}}", valueType = "string" }
  ],
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "file", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.parent.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
  ]
]
relativeFrom = "now-1h"
relativeTo = "now"

[[transform.investigate]]
label = "Network and DNS events for MSDT or its parent"
description = ""
providers = [
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "network", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
  ],
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "dns", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
  ],
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "network", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.parent.entity_id}}", valueType = "string" }
  ],
  [
    { excluded = false, field = "event.category", queryType = "phrase", value = "dns", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.parent.entity_id}}", valueType = "string" }
  ]
]
relativeFrom = "now-1h"
relativeTo = "now"

[[transform.investigate]]
label = "Alerts associated with the user"
description = ""
providers = [
  [
    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
  ]
]
relativeFrom = "now-48h/h"
relativeTo = "now"

[[transform.investigate]]
label = "Alerts associated with the host"
description = ""
providers = [
  [
    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
  ]
]
relativeFrom = "now-48h/h"
relativeTo = "now"

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

[[rule.threat.technique]]
id = "T1036"
name = "Masquerading"
reference = "https://attack.mitre.org/techniques/T1036/"

[[rule.threat.technique.subtechnique]]
id = "T1036.003"
name = "Rename Legitimate Utilities"
reference = "https://attack.mitre.org/techniques/T1036/003/"

[[rule.threat.technique]]
id = "T1218"
name = "System Binary Proxy Execution"
reference = "https://attack.mitre.org/techniques/T1218/"

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

Stages and Predicates

Stage 1: process

process where host.os.type == "windows" and event.type == "start" and
  (?process.pe.original_file_name == "msdt.exe" or process.name : "msdt.exe") and
  (
    process.args : ("IT_RebrowseForFile=*", "*FromBase64*", "*/../../../*", "IT_BrowseForFile=*") or
    (
      process.args : ("-af", "/af") and process.args : "/skip" and
      process.parent.name : ("explorer.exe", "cmd.exe", "powershell.exe", "cscript.exe", "wscript.exe", "mshta.exe", "rundll32.exe", "regsvr32.exe") and
      process.args : ("?:\\WINDOWS\\diagnostics\\index\\PCWDiagnostic.xml", "PCWDiagnostic.xml", "?:\\Users\\Public\\*", "?:\\Windows\\Temp\\*")
    ) or
    (process.pe.original_file_name == "msdt.exe" and not process.name : "msdt.exe" and process.name != null) or
    (
      ?process.pe.original_file_name == "msdt.exe" and
      not process.executable : (
        "?:\\Windows\\system32\\msdt.exe",
        "?:\\Windows\\SysWOW64\\msdt.exe",
        "\\Device\\HarddiskVolume*\\Windows\\system32\\msdt.exe",
        "\\Device\\HarddiskVolume*\\Windows\\SysWOW64\\msdt.exe"
      )
    )
  )

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
event.typeeq
  • start corpus 606 (elastic 606)
process.argswildcard
  • */../../../*
  • *FromBase64*
  • -af
  • /af
  • /skip
  • ?:\Users\Public\*
  • ?:\WINDOWS\diagnostics\index\PCWDiagnostic.xml
  • ?:\Windows\Temp\*
  • IT_BrowseForFile=*
  • IT_RebrowseForFile=*
  • PCWDiagnostic.xml
process.nameis_not_null
  • (no value, null check)
process.namewildcard
  • msdt.exe corpus 8 (elastic 6, splunk 2)
process.parent.namewildcard
  • cmd.exe corpus 15 (elastic 10, splunk 4, kusto 1)
  • cscript.exe corpus 7 (elastic 6, splunk 1)
  • explorer.exe corpus 20 (elastic 19, splunk 1)
  • mshta.exe corpus 12 (elastic 10, splunk 2)
  • powershell.exe corpus 15 (elastic 12, kusto 2, splunk 1)
  • regsvr32.exe corpus 7 (elastic 7)
  • rundll32.exe corpus 10 (elastic 10)
  • wscript.exe corpus 11 (elastic 10, splunk 1)
process.pe.original_file_nameeq
  • msdt.exe corpus 8 (sigma 6, elastic 2)