Potential Notepad Markdown RCE Exploitation

Identifies a process started by Notepad after opening a Markdown file. This may indicate successful exploitation of a Notepad markdown parsing vulnerability (CVE-2026-20841) that can lead to arbitrary code execution.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/02/16"
  3integration = ["endpoint", "windows", "m365_defender", "sentinel_one_cloud_funnel"]
  4maturity = "production"
  5updated_date = "2026/05/01"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies a process started by Notepad after opening a Markdown file. This may indicate successful exploitation of a
 11Notepad markdown parsing vulnerability (CVE-2026-20841) that can lead to arbitrary code execution.
 12"""
 13from = "now-9m"
 14index = [
 15    "endgame-*",
 16    "logs-endpoint.events.process-*",
 17    "logs-m365_defender.event-*",
 18    "logs-sentinel_one_cloud_funnel.*",
 19    "logs-windows.sysmon_operational-*"
 20]
 21language = "eql"
 22license = "Elastic License v2"
 23name = "Potential Notepad Markdown RCE Exploitation"
 24references = ["https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-20841"]
 25risk_score = 73
 26rule_id = "7f3521dd-fb80-4548-a7eb-8db37b898dc2"
 27severity = "high"
 28tags = [
 29    "Domain: Endpoint",
 30    "OS: Windows",
 31    "Use Case: Threat Detection",
 32    "Tactic: Execution",
 33    "Data Source: Elastic Endgame",
 34    "Data Source: Elastic Defend",
 35    "Data Source: Microsoft Defender XDR",
 36    "Data Source: Sysmon",
 37    "Data Source: SentinelOne",
 38    "Resources: Investigation Guide",
 39]
 40timestamp_override = "event.ingested"
 41type = "eql"
 42
 43query = '''
 44process where host.os.type == "windows" and event.type == "start" and
 45  process.parent.name : "notepad.exe" and process.parent.args : "*.md" and
 46  not process.executable : "C:\\Program Files\\WindowsApps\\Microsoft.WindowsNotepad_*\\Notepad\\Notepad.exe"
 47'''
 48
 49note = """## Triage and analysis
 50
 51### Investigating Potential Notepad Markdown RCE Exploitation
 52
 53#### Possible investigation steps
 54
 55- What exact Notepad-to-child path did the alert preserve?
 56  - Why: CVE-2026-20841 abuse centers on Markdown link handling that invokes unsafe protocol handlers, so the launched child and target are decisive.
 57  - Focus: `process.parent.executable`, `process.parent.command_line`, `process.parent.args`, child `process.executable`, and child `process.command_line`.
 58  - Implication: escalate when Notepad opened Markdown and launched a shell, script host, downloader, browser, archive handler, installer, or custom protocol handler with a URL, UNC path, file URI, app installer URI, or staged payload; lower suspicion only when child and target match a confirmed validation workflow.
 59
 60- What does child binary identity add to command intent?
 61  - Focus: child `process.executable`, `process.hash.sha256`, `process.code_signature.subject_name`, `process.code_signature.trusted`, and `process.pe.original_file_name`.
 62  - Implication: escalate when the child is unsigned, user-writable, signer/name-mismatched, or unusual for Markdown link handling; reduce suspicion only when signer, path, hash history, and command target all fit the same confirmed validation workflow.
 63
 64- Does parent launch context point to lure delivery or controlled use?
 65  - Focus: Markdown path from `process.parent.args`, `process.parent.command_line`, `user.id`, `host.id`, and `host.name`.
 66  - Implication: escalate when the path is in downloads, mail or chat caches, temp, removable media, network shares, or lure-like filenames; lower suspicion only when source path and child target match a confirmed validation case for the same user or host.
 67
 68- Did the child start more exploit-chain processes?
 69  - Focus: process events on `host.id` where `process.parent.entity_id` links to `process.entity_id`, recording descendant `process.executable` and `process.command_line`. $investigate_4
 70  - Hint: prefer entity ID-linked descendants; treat PID-only matches as candidates tightly anchored to `@timestamp`. Inspect `process.Ext.ancestry` only when direct lineage is incomplete.
 71  - Implication: escalate for descendant shells, script interpreters, LOLBins, installers, payload runners, or browser-to-download chains; no descendants limits process scope but does not clear the Notepad launch.
 72
 73- Did the child stage artifacts for later execution?
 74  - Focus: if file telemetry exists, review process-scoped `file.path`, `file.Ext.original.path`, and later `process.executable` reuse of a written path. $investigate_0
 75  - Hint: recover with `host.id` + `process.entity_id`; if unavailable, use `host.id` + `process.pid` + a tight alert window. Missing file telemetry is unresolved, not benign.
 76  - Implication: escalate when the child writes executables, scripts, shortcuts, archives, or renamed payloads to user-writable paths, or a written artifact later runs; clean available file telemetry means staging is not corroborated, not that the alert is benign.
 77
 78- Did the child contact payload-delivery destinations?
 79  - Focus: if DNS or connection telemetry exists, review `dns.question.name`, `destination.ip`, and `destination.port`. $investigate_1
 80  - Hint: recover with `host.id` + `process.entity_id`; if unavailable, use `host.id` + `process.pid` + a tight alert window. Missing DNS or connection telemetry is unresolved, not benign.
 81  - Implication: escalate when the child resolves or connects to rare, external, newly introduced, or delivery-oriented destinations outside its role; clean available network telemetry removes one corroborator but cannot override suspicious alert-local execution.
 82
 83- If local evidence is suspicious or unresolved, does the pattern change scope?
 84  - Focus: related alerts for the same `user.id` over 48 hours, comparing child `process.executable`, child `process.command_line`, and source-path patterns from `process.parent.args` when present. $investigate_2
 85  - Hint: if the user view is empty or ambiguous, compare related alerts for the same `host.id` over 48 hours before broadening. $investigate_3
 86  - Implication: broaden when related alerts show the same Notepad-child pattern, source-path family, or delivery indicator across unrelated users or hosts; use recurrence for scope only, not benign closure.
 87
 88- Escalate when child intent, descendant lineage, artifact staging, or destinations point to payload delivery, even if one conditional telemetry family is absent; close only when source path, child identity, command target, and `user.id`/`host.id` context prove one benign workflow; preserve evidence and escalate when telemetry is missing or contradictory.
 89
 90### False positive analysis
 91
 92- Authorized CVE validation, QA, or security lab activity can trigger this rule when controlled Markdown samples intentionally launch a stable helper. Confirm `process.parent.args`, child `process.executable`, child hash or signer, child `process.command_line`, and `user.id`/`host.id` cohort all match the same validation workflow; prior alerts support stability only after those anchors align.
 93- Before creating an exception, validate stability for the same child identity, child command-target pattern, Markdown path family from `process.parent.args`, and `user.id` or `host.id` scope. Avoid exceptions on `process.parent.name`, `process.name`, or Notepad alone.
 94
 95### Response and remediation
 96
 97- If confirmed benign:
 98  - Reverse temporary containment and record the Markdown path, child process identity, command target, and cohort evidence that confirmed the benign workflow. Build an exception only for the same stable `user.id` or `host.id` scope and child identity.
 99- If suspicious but unconfirmed:
100  - Preserve the triggering Markdown file, parent `process.parent.command_line`, child `process.entity_id`, child `process.command_line`, descendant process identifiers, written artifact paths, and DNS or IP indicators before cleanup.
101  - Apply reversible containment tied to observed indicators, such as temporary restriction of the observed destination or heightened monitoring on the affected `host.id` and `user.id`.
102  - Escalate to host isolation if the child continues spawning payloads, downloading content, or contacting suspicious destinations after preservation.
103- If confirmed malicious:
104  - Isolate the host first, then stop the malicious child and descendant `process.entity_id` values after recording `process.entity_id`, `process.command_line`, the Markdown path from `process.parent.args`, and related artifact or destination indicators. If direct response is unavailable, hand off the preserved evidence set to the containment team.
105  - Block confirmed malicious domains, IPs, URLs, hashes, and child process paths identified during the investigation.
106  - Eradicate only the dropped files, startup artifacts, scheduled tasks, registry changes, and payload runners tied to the exploit chain, then quarantine the triggering Markdown file and remediate its delivery path.
107- Post-incident hardening:
108  - Update the Windows Notepad App to a version that remediates CVE-2026-20841, and restrict unsafe protocol-handler launch exposure for Markdown viewers where enterprise controls support it.
109  - Preserve or expand file and network telemetry if missing visibility prevented confirmation of Markdown source, payload staging, or destination activity.
110  - Record the Markdown delivery path, child-process pattern, and adjacent protocol-handler or payload variants in the case notes.
111"""
112
113setup = """## Setup
114
115This 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.
116
117Setup instructions: https://ela.st/install-elastic-defend
118
119### Additional data sources
120
121This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
122
123- [Microsoft Defender XDR](https://ela.st/m365-defender)
124- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
125- [Sysmon Event ID 1 - Process Creation](https://ela.st/sysmon-event-1-setup)
126"""
127
128[rule.investigation_fields]
129field_names = [
130    "@timestamp",
131    "host.id",
132    "user.id",
133    "process.entity_id",
134    "process.pid",
135    "process.executable",
136    "process.command_line",
137    "process.hash.sha256",
138    "process.code_signature.subject_name",
139    "process.code_signature.trusted",
140    "process.code_signature.status",
141    "process.parent.executable",
142    "process.parent.command_line",
143    "process.parent.args",
144    "process.working_directory",
145]
146
147[transform]
148
149[[transform.investigate]]
150label = "File events for the same child process"
151description = ""
152providers = [
153  [
154    { excluded = false, field = "event.category", queryType = "phrase", value = "file", valueType = "string" },
155    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
156    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
157  ]
158]
159relativeFrom = "now-1h"
160relativeTo = "now"
161
162[[transform.investigate]]
163label = "Network events for the same child process"
164description = ""
165providers = [
166  [
167    { excluded = false, field = "event.category", queryType = "phrase", value = "network", valueType = "string" },
168    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
169    { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
170  ]
171]
172relativeFrom = "now-1h"
173relativeTo = "now"
174
175[[transform.investigate]]
176label = "Alerts associated with the user"
177description = ""
178providers = [
179  [
180    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
181    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
182  ]
183]
184relativeFrom = "now-48h/h"
185relativeTo = "now"
186
187[[transform.investigate]]
188label = "Alerts associated with the host"
189description = ""
190providers = [
191  [
192    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
193    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
194  ]
195]
196relativeFrom = "now-48h/h"
197relativeTo = "now"
198
199[[transform.investigate]]
200label = "Child process events for the Notepad-spawned child"
201description = ""
202providers = [
203  [
204    { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
205    { excluded = false, field = "event.type", queryType = "phrase", value = "start", valueType = "string" },
206    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
207    { excluded = false, field = "process.parent.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
208  ],
209  [
210    { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
211    { excluded = false, field = "event.type", queryType = "phrase", value = "start", valueType = "string" },
212    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
213    { excluded = false, field = "process.parent.pid", queryType = "phrase", value = "{{process.pid}}", valueType = "string" }
214  ]
215]
216relativeFrom = "now-1h"
217relativeTo = "now"
218
219[[rule.threat]]
220framework = "MITRE ATT&CK"
221
222[[rule.threat.technique]]
223id = "T1203"
224name = "Exploitation for Client Execution"
225reference = "https://attack.mitre.org/techniques/T1203/"
226
227[[rule.threat.technique]]
228id = "T1204"
229name = "User Execution"
230reference = "https://attack.mitre.org/techniques/T1204/"
231
232[[rule.threat.technique.subtechnique]]
233id = "T1204.002"
234name = "Malicious File"
235reference = "https://attack.mitre.org/techniques/T1204/002/"
236
237[rule.threat.tactic]
238id = "TA0002"
239name = "Execution"
240reference = "https://attack.mitre.org/tactics/TA0002/"

Triage and analysis

Investigating Potential Notepad Markdown RCE Exploitation

Possible investigation steps

  • What exact Notepad-to-child path did the alert preserve?

    • Why: CVE-2026-20841 abuse centers on Markdown link handling that invokes unsafe protocol handlers, so the launched child and target are decisive.
    • Focus: process.parent.executable, process.parent.command_line, process.parent.args, child process.executable, and child process.command_line.
    • Implication: escalate when Notepad opened Markdown and launched a shell, script host, downloader, browser, archive handler, installer, or custom protocol handler with a URL, UNC path, file URI, app installer URI, or staged payload; lower suspicion only when child and target match a confirmed validation workflow.
  • What does child binary identity add to command intent?

    • Focus: child process.executable, process.hash.sha256, process.code_signature.subject_name, process.code_signature.trusted, and process.pe.original_file_name.
    • Implication: escalate when the child is unsigned, user-writable, signer/name-mismatched, or unusual for Markdown link handling; reduce suspicion only when signer, path, hash history, and command target all fit the same confirmed validation workflow.
  • Does parent launch context point to lure delivery or controlled use?

    • Focus: Markdown path from process.parent.args, process.parent.command_line, user.id, host.id, and host.name.
    • Implication: escalate when the path is in downloads, mail or chat caches, temp, removable media, network shares, or lure-like filenames; lower suspicion only when source path and child target match a confirmed validation case for the same user or host.
  • Did the child start more exploit-chain processes?

    • Focus: process events on host.id where process.parent.entity_id links to process.entity_id, recording descendant process.executable and process.command_line. $investigate_4
    • Hint: prefer entity ID-linked descendants; treat PID-only matches as candidates tightly anchored to @timestamp. Inspect process.Ext.ancestry only when direct lineage is incomplete.
    • Implication: escalate for descendant shells, script interpreters, LOLBins, installers, payload runners, or browser-to-download chains; no descendants limits process scope but does not clear the Notepad launch.
  • Did the child stage artifacts for later execution?

    • Focus: if file telemetry exists, review process-scoped file.path, file.Ext.original.path, and later process.executable reuse of a written path. $investigate_0
    • Hint: recover with host.id + process.entity_id; if unavailable, use host.id + process.pid + a tight alert window. Missing file telemetry is unresolved, not benign.
    • Implication: escalate when the child writes executables, scripts, shortcuts, archives, or renamed payloads to user-writable paths, or a written artifact later runs; clean available file telemetry means staging is not corroborated, not that the alert is benign.
  • Did the child contact payload-delivery destinations?

    • Focus: if DNS or connection telemetry exists, review dns.question.name, destination.ip, and destination.port. $investigate_1
    • Hint: recover with host.id + process.entity_id; if unavailable, use host.id + process.pid + a tight alert window. Missing DNS or connection telemetry is unresolved, not benign.
    • Implication: escalate when the child resolves or connects to rare, external, newly introduced, or delivery-oriented destinations outside its role; clean available network telemetry removes one corroborator but cannot override suspicious alert-local execution.
  • If local evidence is suspicious or unresolved, does the pattern change scope?

    • Focus: related alerts for the same user.id over 48 hours, comparing child process.executable, child process.command_line, and source-path patterns from process.parent.args when present. $investigate_2
    • Hint: if the user view is empty or ambiguous, compare related alerts for the same host.id over 48 hours before broadening. $investigate_3
    • Implication: broaden when related alerts show the same Notepad-child pattern, source-path family, or delivery indicator across unrelated users or hosts; use recurrence for scope only, not benign closure.
  • Escalate when child intent, descendant lineage, artifact staging, or destinations point to payload delivery, even if one conditional telemetry family is absent; close only when source path, child identity, command target, and user.id/host.id context prove one benign workflow; preserve evidence and escalate when telemetry is missing or contradictory.

False positive analysis

  • Authorized CVE validation, QA, or security lab activity can trigger this rule when controlled Markdown samples intentionally launch a stable helper. Confirm process.parent.args, child process.executable, child hash or signer, child process.command_line, and user.id/host.id cohort all match the same validation workflow; prior alerts support stability only after those anchors align.
  • Before creating an exception, validate stability for the same child identity, child command-target pattern, Markdown path family from process.parent.args, and user.id or host.id scope. Avoid exceptions on process.parent.name, process.name, or Notepad alone.

Response and remediation

  • If confirmed benign:
    • Reverse temporary containment and record the Markdown path, child process identity, command target, and cohort evidence that confirmed the benign workflow. Build an exception only for the same stable user.id or host.id scope and child identity.
  • If suspicious but unconfirmed:
    • Preserve the triggering Markdown file, parent process.parent.command_line, child process.entity_id, child process.command_line, descendant process identifiers, written artifact paths, and DNS or IP indicators before cleanup.
    • Apply reversible containment tied to observed indicators, such as temporary restriction of the observed destination or heightened monitoring on the affected host.id and user.id.
    • Escalate to host isolation if the child continues spawning payloads, downloading content, or contacting suspicious destinations after preservation.
  • If confirmed malicious:
    • Isolate the host first, then stop the malicious child and descendant process.entity_id values after recording process.entity_id, process.command_line, the Markdown path from process.parent.args, and related artifact or destination indicators. If direct response is unavailable, hand off the preserved evidence set to the containment team.
    • Block confirmed malicious domains, IPs, URLs, hashes, and child process paths identified during the investigation.
    • Eradicate only the dropped files, startup artifacts, scheduled tasks, registry changes, and payload runners tied to the exploit chain, then quarantine the triggering Markdown file and remediate its delivery path.
  • Post-incident hardening:
    • Update the Windows Notepad App to a version that remediates CVE-2026-20841, and restrict unsafe protocol-handler launch exposure for Markdown viewers where enterprise controls support it.
    • Preserve or expand file and network telemetry if missing visibility prevented confirmation of Markdown source, payload staging, or destination activity.
    • Record the Markdown delivery path, child-process pattern, and adjacent protocol-handler or payload variants in the case notes.

References

Related rules

to-top