Persistence via Hidden Run Key Detected
Identifies a persistence mechanism that utilizes the NtSetValueKey native API to create a hidden (null terminated) registry key. An adversary may use this method to hide from system utilities such as the Registry Editor (regedit).
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2020/11/15"
3integration = ["endpoint", "windows", "crowdstrike", "sentinel_one_cloud_funnel", "m365_defender"]
4maturity = "production"
5updated_date = "2026/05/03"
6
7[rule]
8author = ["Elastic"]
9description = """
10Identifies a persistence mechanism that utilizes the NtSetValueKey native API to create a hidden (null terminated)
11registry key. An adversary may use this method to hide from system utilities such as the Registry Editor (regedit).
12"""
13from = "now-9m"
14index = [
15 "logs-endpoint.events.registry-*",
16 "winlogbeat-*",
17 "logs-windows.sysmon_operational-*",
18 "endgame-*",
19 "logs-crowdstrike.fdr*",
20 "logs-sentinel_one_cloud_funnel.*",
21 "logs-m365_defender.event-*",
22]
23language = "eql"
24license = "Elastic License v2"
25name = "Persistence via Hidden Run Key Detected"
26references = [
27 "https://github.com/outflanknl/SharpHide",
28 "https://github.com/ewhitehats/InvisiblePersistence/blob/master/InvisibleRegValues_Whitepaper.pdf",
29]
30risk_score = 73
31rule_id = "a9b05c3b-b304-4bf9-970d-acdfaef2944c"
32severity = "high"
33tags = [
34 "Domain: Endpoint",
35 "OS: Windows",
36 "Use Case: Threat Detection",
37 "Tactic: Persistence",
38 "Resources: Investigation Guide",
39 "Data Source: Elastic Endgame",
40 "Data Source: Elastic Defend",
41 "Data Source: Sysmon",
42 "Data Source: Crowdstrike",
43 "Data Source: SentinelOne",
44 "Data Source: Microsoft Defender XDR",
45]
46timestamp_override = "event.ingested"
47type = "eql"
48
49query = '''
50registry where host.os.type == "windows" and event.type == "change" and length(registry.data.strings) > 0 and
51
52 /* Registry Path ends with backslash */
53 registry.path : "*\\Run\\" and
54 registry.path : (
55 "*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\",
56 "*\\Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run\\",
57 "*\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run\\"
58 )
59'''
60
61note = """## Triage and analysis
62
63### Investigating Persistence via Hidden Run Key Detected
64
65#### Possible investigation steps
66
67- Does the registry event support hidden or null-terminated Run-value semantics, and what would it launch?
68 - Why: trailing Run-key `registry.path` with launch data can indicate a null-prefixed value name created through NtSetValueKey; raw telemetry can preserve it when regedit does not.
69 - Focus: `registry.path`, `registry.hive`, `registry.value`, `registry.data.type`, and `registry.data.strings`.
70 - Hint: trust the registry event over GUI rendering; record exact `registry.data.strings` before tools normalize the hidden value name.
71 - Implication: escalate when raw evidence supports hidden-value semantics and an unexpected command; lower suspicion only when exact raw value, payload, user, and host match authorized hidden-registry testing. The alert indicates a hiding technique, not one tool.
72
73- Which hive and logon scope would activate the hidden autorun?
74 - Focus: `registry.hive`, `registry.path`, `user.id`, and `host.id`.
75 - Implication: prioritize host-wide containment for machine hives and user-focused containment for user hives; lower urgency only when raw value evidence supports a constrained test user or lab host.
76
77- Which process wrote the hidden value, and does its identity and launch chain fit the expected test or validation workflow?
78 - Focus: `process.entity_id`, `process.executable`, `process.command_line`, `process.parent.executable`, `process.code_signature.subject_name`, and `process.code_signature.trusted`.
79 - Hint: use the writer's process-start event on `host.id` and `process.entity_id` to confirm command line, parent, and signer. $investigate_0
80 - Implication: escalate when the writer is a script host, document child, remote-admin tool, unsigned/untrusted binary, user-writable executable, or SharpHide-like command line; lower suspicion when signer, path, parent, command line, user, and host align with the same recognized test or validation component.
81
82- Did the hidden autorun command execute after logon or spawn suspicious follow-on activity?
83 - Focus: post-alert process starts on `host.id` where `process.executable` or `process.command_line` matches the `registry.data.strings` payload.
84 - Hint: for user-hive paths, include `user.id`; for machine-hive paths, check the next interactive logon. Use `process.parent.name` and `process.parent.executable` to identify the launcher. $investigate_1
85 - Implication: escalate when the payload launches after logon, runs through a shell, script host, LOLBin, user-writable path, or obfuscated chain, or creates unusual children; no later launch leaves execution unproven, not benign, unless the value and writer are fully explained.
86
87- Did the same writer modify other startup or hidden-registry locations around the alert time?
88 - Focus: writer-scoped registry events, especially `registry.path`, `registry.key`, `registry.value`, and `registry.data.strings` for Run, RunOnce, Policies\\Explorer\\Run, or related startup paths.
89 - Hint: start with `host.id` and `process.entity_id`, then widen only to the same host and startup-path family. $investigate_2
90 - Implication: escalate when the process spreads persistence across startup keys, rewrites the same path, or creates additional hidden or malformed values; a single bounded change lowers scope only after writer and payload are independently explained.
91
92- If local evidence remains suspicious or unresolved, do same-user or same-host alerts show broader activity?
93 - Focus: recent same-`user.id` alerts tied to persistence, defense evasion, credential access, or lateral movement. $investigate_3
94 - Hint: review same-`host.id` alerts when local evidence is suspicious or incomplete. $investigate_4
95 - Implication: broaden containment when related alerts show precursor access, more persistence, credential abuse, or follow-on movement; keep local only when related alerts are absent and the hidden value is otherwise exactly explained.
96
97- Escalate when hidden Run semantics pair with a suspicious writer, payload, later execution, adjacent persistence, or related alerts; close only when same-host telemetry and corroborating test or validation records explain the exact value, writer, payload, user, and host with no contradictions; preserve registry, process, and payload evidence and escalate when answers remain mixed or incomplete.
98
99### False positive analysis
100
101- Authorized adversary emulation, detection validation, or security-product testing can create hidden Run values to test detection of regedit-hidden startup entries. Close only when `process.executable`, `process.code_signature.subject_name`, `process.command_line`, `registry.path`, `registry.data.strings`, `user.id`, and `host.id` match the same authorized test chain, and any later `process.command_line` stays bounded to it. Without validation records, do not close on recurrence alone; keep suspicious or unresolved until exact test scope is verified.
102- Routine software installation can explain ordinary Run-key writes, but should not require a hidden or null-terminated value name. Treat installer or support claims as insufficient unless telemetry proves the exact hidden value, writer, payload, and execution pattern belong to a recognized lab, validation, or product-test workflow.
103- Before an exception, require a verified test or validation workflow, then confirm the same `process.executable`, `process.code_signature.subject_name`, `registry.path`, `registry.data.strings`, `user.id`, and `host.id` pattern recurs across prior alerts from this rule. Build the exception from that minimum confirmed workflow; avoid exceptions on Run paths alone, `process.name` alone, payload strings alone, or the rule name alone.
104
105### Response and remediation
106
107- If confirmed benign, reverse any temporary containment and record the `process.executable`, signer, `process.command_line`, `registry.path`, exact `registry.data.strings`, `user.id`, `host.id`, and later logon execution pattern that proved the authorized workflow. Keep any exception narrow and tied to the recurring workflow.
108- If suspicious but unconfirmed, first preserve the raw registry event, affected Run-key hive with the hidden value intact when possible, writer `process.entity_id`, writer command line, process lineage, payload string from `registry.data.strings`, and related-alert context. Then apply reversible containment tied to those findings, such as isolating the host if its role tolerates it, temporarily preventing the affected user from logging back in, or blocking execution of the referenced payload.
109- If confirmed malicious, preserve the same registry and process evidence before destructive action. Use tooling that can enumerate null-prefixed value names to remove the hidden autorun, collect or quarantine the referenced payload artifact, and review the same `process.entity_id`, `registry.path`, and startup-key family for additional hidden or visible autoruns before cleanup. Terminate a recovered payload process only after recording its `process.entity_id` and command line.
110- After containment, review other hosts and users for the same `registry.data.strings`, startup-path family, signer, or payload path before deleting artifacts, then verify that no additional Run or RunOnce locations reference the same payload.
111- Post-incident hardening: restrict startup-key modifications to recognized installers and management tools, retain registry and process telemetry needed to correlate hidden Run-key writes with writer lineage and logon execution, and record any adjacent hidden-registry or startup-persistence variant in case notes.
112"""
113
114setup = """## Setup
115
116This 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.
117
118Setup instructions: https://ela.st/install-elastic-defend
119
120### Additional data sources
121
122This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
123
124- [CrowdStrike](https://ela.st/crowdstrike-integration)
125- [Microsoft Defender XDR](https://ela.st/m365-defender)
126- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
127- [Sysmon Registry Events](https://ela.st/sysmon-event-reg-setup)
128"""
129
130[rule.investigation_fields]
131field_names = [
132 "@timestamp",
133 "host.name",
134 "host.id",
135 "user.name",
136 "user.id",
137 "process.entity_id",
138 "process.pid",
139 "process.name",
140 "process.executable",
141 "process.command_line",
142 "registry.path",
143 "registry.key",
144 "registry.value",
145 "registry.data.type",
146 "registry.data.strings",
147]
148
149[transform]
150
151[[transform.investigate]]
152label = "Process events for the registry writer"
153description = ""
154providers = [
155 [
156 { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
157 { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
158 { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
159 ]
160]
161relativeFrom = "now-1h"
162relativeTo = "now"
163
164[[transform.investigate]]
165label = "Process starts matching the hidden Run payload"
166description = ""
167providers = [
168 [
169 { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
170 { excluded = false, field = "event.type", queryType = "phrase", value = "start", valueType = "string" },
171 { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
172 { excluded = false, field = "process.command_line", queryType = "phrase", value = "{{registry.data.strings}}", valueType = "string" }
173 ],
174 [
175 { excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
176 { excluded = false, field = "event.type", queryType = "phrase", value = "start", valueType = "string" },
177 { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
178 { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
179 ]
180]
181relativeFrom = "now"
182relativeTo = "now"
183
184[[transform.investigate]]
185label = "Registry events from the writer process"
186description = ""
187providers = [
188 [
189 { excluded = false, field = "event.category", queryType = "phrase", value = "registry", valueType = "string" },
190 { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
191 { excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" }
192 ]
193]
194relativeFrom = "now-1h"
195relativeTo = "now"
196
197[[transform.investigate]]
198label = "Alerts associated with the user"
199description = ""
200providers = [
201 [
202 { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
203 { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
204 ]
205]
206relativeFrom = "now-48h/h"
207relativeTo = "now"
208
209[[transform.investigate]]
210label = "Alerts associated with the host"
211description = ""
212providers = [
213 [
214 { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
215 { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
216 ]
217]
218relativeFrom = "now-48h/h"
219relativeTo = "now"
220
221[[rule.threat]]
222framework = "MITRE ATT&CK"
223
224[[rule.threat.technique]]
225id = "T1547"
226name = "Boot or Logon Autostart Execution"
227reference = "https://attack.mitre.org/techniques/T1547/"
228
229[[rule.threat.technique.subtechnique]]
230id = "T1547.001"
231name = "Registry Run Keys / Startup Folder"
232reference = "https://attack.mitre.org/techniques/T1547/001/"
233
234[rule.threat.tactic]
235id = "TA0003"
236name = "Persistence"
237reference = "https://attack.mitre.org/tactics/TA0003/"
238
239[[rule.threat]]
240framework = "MITRE ATT&CK"
241
242[[rule.threat.technique]]
243id = "T1106"
244name = "Native API"
245reference = "https://attack.mitre.org/techniques/T1106/"
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 = "T1112"
257name = "Modify Registry"
258reference = "https://attack.mitre.org/techniques/T1112/"
259
260[[rule.threat.technique]]
261id = "T1564"
262name = "Hide Artifacts"
263reference = "https://attack.mitre.org/techniques/T1564/"
264
265[rule.threat.tactic]
266id = "TA0005"
267name = "Defense Evasion"
268reference = "https://attack.mitre.org/tactics/TA0005/"
Triage and analysis
Investigating Persistence via Hidden Run Key Detected
Possible investigation steps
-
Does the registry event support hidden or null-terminated Run-value semantics, and what would it launch?
- Why: trailing Run-key
registry.pathwith launch data can indicate a null-prefixed value name created through NtSetValueKey; raw telemetry can preserve it when regedit does not. - Focus:
registry.path,registry.hive,registry.value,registry.data.type, andregistry.data.strings. - Hint: trust the registry event over GUI rendering; record exact
registry.data.stringsbefore tools normalize the hidden value name. - Implication: escalate when raw evidence supports hidden-value semantics and an unexpected command; lower suspicion only when exact raw value, payload, user, and host match authorized hidden-registry testing. The alert indicates a hiding technique, not one tool.
- Why: trailing Run-key
-
Which hive and logon scope would activate the hidden autorun?
- Focus:
registry.hive,registry.path,user.id, andhost.id. - Implication: prioritize host-wide containment for machine hives and user-focused containment for user hives; lower urgency only when raw value evidence supports a constrained test user or lab host.
- Focus:
-
Which process wrote the hidden value, and does its identity and launch chain fit the expected test or validation workflow?
- Focus:
process.entity_id,process.executable,process.command_line,process.parent.executable,process.code_signature.subject_name, andprocess.code_signature.trusted. - Hint: use the writer's process-start event on
host.idandprocess.entity_idto confirm command line, parent, and signer. $investigate_0 - Implication: escalate when the writer is a script host, document child, remote-admin tool, unsigned/untrusted binary, user-writable executable, or SharpHide-like command line; lower suspicion when signer, path, parent, command line, user, and host align with the same recognized test or validation component.
- Focus:
-
Did the hidden autorun command execute after logon or spawn suspicious follow-on activity?
- Focus: post-alert process starts on
host.idwhereprocess.executableorprocess.command_linematches theregistry.data.stringspayload. - Hint: for user-hive paths, include
user.id; for machine-hive paths, check the next interactive logon. Useprocess.parent.nameandprocess.parent.executableto identify the launcher. $investigate_1 - Implication: escalate when the payload launches after logon, runs through a shell, script host, LOLBin, user-writable path, or obfuscated chain, or creates unusual children; no later launch leaves execution unproven, not benign, unless the value and writer are fully explained.
- Focus: post-alert process starts on
-
Did the same writer modify other startup or hidden-registry locations around the alert time?
- Focus: writer-scoped registry events, especially
registry.path,registry.key,registry.value, andregistry.data.stringsfor Run, RunOnce, Policies\Explorer\Run, or related startup paths. - Hint: start with
host.idandprocess.entity_id, then widen only to the same host and startup-path family. $investigate_2 - Implication: escalate when the process spreads persistence across startup keys, rewrites the same path, or creates additional hidden or malformed values; a single bounded change lowers scope only after writer and payload are independently explained.
- Focus: writer-scoped registry events, especially
-
If local evidence remains suspicious or unresolved, do same-user or same-host alerts show broader activity?
- Focus: recent same-
user.idalerts tied to persistence, defense evasion, credential access, or lateral movement. $investigate_3 - Hint: review same-
host.idalerts when local evidence is suspicious or incomplete. $investigate_4 - Implication: broaden containment when related alerts show precursor access, more persistence, credential abuse, or follow-on movement; keep local only when related alerts are absent and the hidden value is otherwise exactly explained.
- Focus: recent same-
-
Escalate when hidden Run semantics pair with a suspicious writer, payload, later execution, adjacent persistence, or related alerts; close only when same-host telemetry and corroborating test or validation records explain the exact value, writer, payload, user, and host with no contradictions; preserve registry, process, and payload evidence and escalate when answers remain mixed or incomplete.
False positive analysis
- Authorized adversary emulation, detection validation, or security-product testing can create hidden Run values to test detection of regedit-hidden startup entries. Close only when
process.executable,process.code_signature.subject_name,process.command_line,registry.path,registry.data.strings,user.id, andhost.idmatch the same authorized test chain, and any laterprocess.command_linestays bounded to it. Without validation records, do not close on recurrence alone; keep suspicious or unresolved until exact test scope is verified. - Routine software installation can explain ordinary Run-key writes, but should not require a hidden or null-terminated value name. Treat installer or support claims as insufficient unless telemetry proves the exact hidden value, writer, payload, and execution pattern belong to a recognized lab, validation, or product-test workflow.
- Before an exception, require a verified test or validation workflow, then confirm the same
process.executable,process.code_signature.subject_name,registry.path,registry.data.strings,user.id, andhost.idpattern recurs across prior alerts from this rule. Build the exception from that minimum confirmed workflow; avoid exceptions on Run paths alone,process.namealone, payload strings alone, or the rule name alone.
Response and remediation
- If confirmed benign, reverse any temporary containment and record the
process.executable, signer,process.command_line,registry.path, exactregistry.data.strings,user.id,host.id, and later logon execution pattern that proved the authorized workflow. Keep any exception narrow and tied to the recurring workflow. - If suspicious but unconfirmed, first preserve the raw registry event, affected Run-key hive with the hidden value intact when possible, writer
process.entity_id, writer command line, process lineage, payload string fromregistry.data.strings, and related-alert context. Then apply reversible containment tied to those findings, such as isolating the host if its role tolerates it, temporarily preventing the affected user from logging back in, or blocking execution of the referenced payload. - If confirmed malicious, preserve the same registry and process evidence before destructive action. Use tooling that can enumerate null-prefixed value names to remove the hidden autorun, collect or quarantine the referenced payload artifact, and review the same
process.entity_id,registry.path, and startup-key family for additional hidden or visible autoruns before cleanup. Terminate a recovered payload process only after recording itsprocess.entity_idand command line. - After containment, review other hosts and users for the same
registry.data.strings, startup-path family, signer, or payload path before deleting artifacts, then verify that no additional Run or RunOnce locations reference the same payload. - Post-incident hardening: restrict startup-key modifications to recognized installers and management tools, retain registry and process telemetry needed to correlate hidden Run-key writes with writer lineage and logon execution, and record any adjacent hidden-registry or startup-persistence variant in case notes.
References
Related rules
- Creation of a Hidden Local User Account
- Persistence via Microsoft Office AddIns
- Persistence via TelemetryController Scheduled Task Hijack
- Suspicious ImagePath Service Creation
- Suspicious Startup Shell Folder Modification