Detection rules › Kusto
Detect Registry Run Key Creation/Modification
This analytic rule detects any registry value or key creation in the registry run keys. This could be an indication of a persistence attempt by an adversary.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | T1112 Modify Registry, T1547 Boot or Logon Autostart Execution |
| Privilege Escalation | T1547 Boot or Logon Autostart Execution |
Event coverage
| Provider | Event/ActionType | Title |
|---|---|---|
| Sysmon | Event ID 12 | RegistryEvent (Object create and delete) |
| Sysmon | Event ID 13 | RegistryEvent (Value Set) |
| Sysmon | Event ID 14 | RegistryEvent (Key and Value Rename) |
| Security-Auditing | Event ID 4657 | A registry value was modified. |
| Security-Auditing | Event ID 4660 | An object was deleted. |
| Security-Auditing | Event ID 4663 | An attempt was made to access an object. |
| Defender-DeviceRegistryEvents | RegistryKeyDeleted | Registry key deleted |
| Defender-DeviceRegistryEvents | RegistryValueSet | Registry value set |
| Defender-DeviceRegistryEvents | RegistryValueDeleted | Registry value deleted |
| Defender-DeviceRegistryEvents | RegistryKeyRenamed | Registry key renamed |
Rule body kusto
id: dd041e4e-1ee2-41ec-ba4e-82a71d628260
name: Detect Registry Run Key Creation/Modification
description: |
This analytic rule detects any registry value or key creation in the registry run keys. This could be an indication of a persistence attempt by an adversary.
severity: Medium
status: Available
tags:
- Schema: _ASim_RegistryEvent
SchemaVersion: 0.1.2
requiredDataConnectors:
- connectorId: CrowdStrikeFalconEndpointProtection
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftThreatProtection
dataTypes:
- SecurityAlert
- connectorId: SentinelOne
dataTypes:
- SentinelOne_CL
- connectorId: VMwareCarbonBlack
dataTypes:
- CarbonBlackEvents_CL
- connectorId: CiscoSecureEndpoint
dataTypes:
- CiscoSecureEndpoint_CL
- connectorId: TrendMicroApexOne
dataTypes:
- TMApexOneEvent
- connectorId: TrendMicroApexOneAma
dataTypes:
- TMApexOneEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
- DefenseEvasion
relevantTechniques:
- T1547
- T1112
query: |
// List of startup registry keys to monitor
let startupRegistryList = dynamic([
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServices',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServices',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Userinit',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Shell',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows'
]);
_ASim_RegistryEvent
| where EventType in ('RegistryValueSet', 'RegistryKeyCreated') and RegistryKey has_any (startupRegistryList)
| project
TimeGenerated,
DvcHostname,
ActorUsername,
ActorUsernameType,
ActingProcessId,
ActingProcessName,
ActingProcessCommandLine,
RegistryKey,
RegistryValue,
RegistryValueType,
RegistryValueData
| extend HostName = tostring(split(DvcHostname, '.')[0])
| extend DnsDomain = tostring(strcat_array(array_slice(split(DvcHostname, '.'), 1, -1), '.'))
| extend Username = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[1]), ActorUsername)
| extend NTDomain = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[0]), ActorUsername)
| extend Username = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[0]), Username)
| extend UPNSuffix = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[1]), '')
| extend RegHive = tostring(split(RegistryKey, '\\')[0]), RegKey = tostring(strcat_array(array_slice(split(RegistryKey, '\\'), 1, -1), '\\'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- identifier: NTDomain
columnName: NTDomain
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Username
- identifier: UPNSuffix
columnName: UPNSuffix
- identifier: NTDomain
columnName: NTDomain
- entityType: Process
fieldMappings:
- identifier: ProcessId
columnName: ActingProcessId
- identifier: CommandLine
columnName: ActingProcessCommandLine
- entityType: RegistryKey
fieldMappings:
- identifier: Hive
columnName: RegHive
- identifier: Key
columnName: RegKey
- entityType: RegistryValue
fieldMappings:
- identifier: Name
columnName: RegistryValue
- identifier: Value
columnName: RegistryValueData
- identifier: ValueType
columnName: RegistryValueType
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: "Registry Run Keys Modified on {{DvcHostname}} ({{DvcIpAddr}}) by ({{ActorUsername}})"
alertDescriptionFormat: "Process {{ActingProcessName}} ProcessId: ({{ActingProcessId}}) modified registry run key {{RegistryKey}}."
version: 1.0.0
kind: Scheduled
Stages and Predicates
Let binding: startupRegistryList
let startupRegistryList = dynamic([
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServicesOnce',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServices',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunServices',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Userinit',
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Shell',
'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows'
]);
Stage 1: source
_ASim_RegistryEvent
Stage 2: where
| where EventType in ('RegistryValueSet', 'RegistryKeyCreated') and RegistryKey has_any (startupRegistryList)
References startupRegistryList (defined above).
Stage 3: project
| project
TimeGenerated,
DvcHostname,
ActorUsername,
ActorUsernameType,
ActingProcessId,
ActingProcessName,
ActingProcessCommandLine,
RegistryKey,
RegistryValue,
RegistryValueType,
RegistryValueData
Stage 4: extend (7 consecutive steps)
| extend HostName = tostring(split(DvcHostname, '.')[0])
| extend DnsDomain = tostring(strcat_array(array_slice(split(DvcHostname, '.'), 1, -1), '.'))
| extend Username = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[1]), ActorUsername)
| extend NTDomain = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[0]), ActorUsername)
| extend Username = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[0]), Username)
| extend UPNSuffix = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[1]), '')
| extend RegHive = tostring(split(RegistryKey, '\\')[0]), RegKey = tostring(strcat_array(array_slice(split(RegistryKey, '\\'), 1, -1), '\\'))
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.
| Field | Kind | Values |
|---|---|---|
EventType | in |
|
RegistryKey | match |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
ActingProcessCommandLine | project |
ActingProcessId | project |
ActingProcessName | project |
ActorUsername | project |
ActorUsernameType | project |
DvcHostname | project |
RegistryKey | project |
RegistryValue | project |
RegistryValueData | project |
RegistryValueType | project |
TimeGenerated | project |
HostName | extend |
DnsDomain | extend |
Username | extend |
NTDomain | extend |
UPNSuffix | extend |
RegHive | extend |
RegKey | extend |