↳ GitHub sourceAnalytics ruleMedium

Microsoft Entra ID Local Device Join Information and Transport Key Registry Keys Access

Description

'This detection uses Windows security events to detect suspicious access attempts by the same process to registry keys that provide information about an Microsoft Entra ID joined or registered devices and Transport keys (tkpub / tkpriv). This information can be used to export the Device Certificate (dkpub / dkpriv) and Transport key (tkpub/tkpriv). These set of keys can be used to impersonate existing Microsoft Entra ID joined devices. This detection requires an access control entry (ACE) on the system access control list (SACL) of the following securable objects: HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin (Microsoft Entra ID joined devices) HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WorkplaceJoin (Microsoft Entra ID registered devices) HKLM:\SYSTEM\CurrentControlSet\Control\Cryptography\Ngc\KeyTransportKey (Transport Key) Make sure you set the SACL to propagate to its sub-keys. You can find more information in here https://github.com/OTRF/Set-AuditRule/blob/master/rules/registry/aad_connect_health_service_agent.yml Reference: https://aadinternals.com/post/deviceidentity/'
Rule type
Scheduled
Version
1.0.6
Declared status
Available
Query frequency
1d
Query period
1d
Trigger
gt 0

Declared MITRE coverage

Declared sources

Metadata from the source file. No dependencies inferred from KQL.

Connectors

Data types

KQL query

Original query, unchanged.

// AADJoined or Register Device Registry Keys
let aadJoinRoot = "\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\CloudDomainJoin\\JoinInfo\\";
let aadRegisteredRoot = "\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WorkplaceJoin";
// Transport Key Registry Key
let keyTransportKey = "\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\Cryptography\\Ngc\\KeyTransportKey\\";
(union isfuzzy=true
(
// Access to Object Requested
SecurityEvent
| where EventID == '4656'
| where EventData has aadJoinRoot or EventData has aadRegisteredRoot
| extend EventData = parse_xml(EventData).EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")
| evaluate pivot(Key, any(Value), TimeGenerated, Computer, EventID)
| where ObjectType == 'Key'
| where ObjectName startswith aadJoinRoot and SubjectLogonId != '0x3e7' //Local System
| extend ProcessId = column_ifexists("ProcessId", ""), Process = split(ProcessName, '\\', -1)[-1],Account = strcat(SubjectDomainName, "\\", SubjectUserName)
| join kind=innerunique (
    SecurityEvent
    | where EventID == '4656'
    | where EventData has keyTransportKey
    | extend EventData = parse_xml(EventData).EventData.Data
    | mv-expand bagexpansion=array EventData
    | evaluate bag_unpack(EventData)
    | extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")
    | evaluate pivot(Key, any(Value), TimeGenerated, Computer, EventID)
    | extend ObjectName = column_ifexists("ObjectName", ""),ObjectType = column_ifexists("ObjectType", "")
    | where ObjectType == 'Key'
    | where ObjectName startswith keyTransportKey and SubjectLogonId != '0x3e7' //Local System
    | extend ProcessId = column_ifexists("ProcessId", ""), Process = split(ProcessName, '\\', -1)[-1],Account = strcat(SubjectDomainName, "\\", SubjectUserName)
) on $left.Computer == $right.Computer and $left.SubjectLogonId == $right.SubjectLogonId and $left.ProcessId == $right.ProcessId
| project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, SubjectLogonId, ObjectName, tostring(Process), ProcessName, ProcessId, EventID
),
// Accessing Object
(
SecurityEvent
| where EventID == '4663'
| where ObjectType == 'Key'
| where (ObjectName startswith aadJoinRoot or ObjectName contains aadRegisteredRoot) and SubjectLogonId != '0x3e7' //Local System
| extend Account = SubjectAccount
| join kind=innerunique (
    SecurityEvent
    | where EventID == '4663'
    | where ObjectType == 'Key'
    | where ObjectName has keyTransportKey and SubjectLogonId != '0x3e7' //Local System
    | extend Account = SubjectAccount
) on $left.Computer == $right.Computer and $left.SubjectLogonId == $right.SubjectLogonId and $left.ProcessId == $right.ProcessId
| project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, SubjectLogonId, ObjectName, Process, ProcessName, ProcessId, EventID
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
)
)

Declared entities

AccountHost

Related content

Links established from declared identifiers and solution manifests.

Source provenance

GitHub

Displayed values come from files in Azure/Azure-Sentinel. They describe the published template, not your workspace configuration.

Source identifier
a356c8bd-c81d-428b-aa36-83be706be034
Additional source files 3Solutions/Windows Security Events/Analytic Rules/LocalDeviceJoinInfoAndTransportKeyRegKeysAccess.yamlsource ↗Detections/SecurityEvent/LocalDeviceJoinInfoAndTransportKeyRegKeysAccess.yamlmigration-note ↗Solutions/Windows Security Events/Data/Solution_Windows Security Events.jsonsolution-membership ↗
GSTEP / CATALOG TRACKING

Added to catalog : 16 Sept 2026 · 05:49 UTC
Last change observed : 16 Sept 2026 · 05:49 UTC

GSTEP sync dates, separate from the source content’s publication dates.