Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

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

Back
Ida356c8bd-c81d-428b-aa36-83be706be034
RulenameMicrosoft Entra ID Local Device Join Information and Transport Key Registry Keys Access
DescriptionThis 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://o365blog.com/post/deviceidentity/
SeverityMedium
TacticsDiscovery
TechniquesT1012
Required data connectorsSecurityEvents
WindowsSecurityEvents
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Windows Security Events/Analytic Rules/LocalDeviceJoinInfoAndTransportKeyRegKeysAccess.yaml
Version1.0.5
Arm templatea356c8bd-c81d-428b-aa36-83be706be034.json
Deploy To Azure
// 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), '.'))
)
)
relevantTechniques:
- T1012
name: Microsoft Entra ID Local Device Join Information and Transport Key Registry Keys Access
requiredDataConnectors:
- dataTypes:
  - SecurityEvent
  connectorId: SecurityEvents
- dataTypes:
  - SecurityEvent
  connectorId: WindowsSecurityEvents
entityMappings:
- fieldMappings:
  - identifier: FullName
    columnName: Account
  - identifier: Name
    columnName: SubjectUserName
  - identifier: NTDomain
    columnName: SubjectDomainName
  entityType: Account
- fieldMappings:
  - identifier: FullName
    columnName: Computer
  - identifier: HostName
    columnName: HostName
  - identifier: DnsDomain
    columnName: DnsDomain
  entityType: Host
triggerThreshold: 0
id: a356c8bd-c81d-428b-aa36-83be706be034
tactics:
- Discovery
version: 1.0.5
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Windows Security Events/Analytic Rules/LocalDeviceJoinInfoAndTransportKeyRegKeysAccess.yaml
queryPeriod: 1d
kind: Scheduled
tags:
- SimuLand
- ATR
- AADInternals
queryFrequency: 1d
severity: Medium
status: Available
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://o365blog.com/post/deviceidentity/'  
query: |
  // 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), '.'))
  )
  )  
triggerOperator: gt
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/a356c8bd-c81d-428b-aa36-83be706be034')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/a356c8bd-c81d-428b-aa36-83be706be034')]",
      "properties": {
        "alertRuleTemplateName": "a356c8bd-c81d-428b-aa36-83be706be034",
        "customDetails": null,
        "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).\n This information can be used to export the Device Certificate (dkpub / dkpriv) and Transport key (tkpub/tkpriv).\n These set of keys can be used to impersonate existing Microsoft Entra ID joined devices.\n This detection requires an access control entry (ACE) on the system access control list (SACL) of the following securable objects:\n HKLM:\\SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin (Microsoft Entra ID joined devices)\n HKCU:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WorkplaceJoin (Microsoft Entra ID registered devices)\n HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Cryptography\\Ngc\\KeyTransportKey (Transport Key)\n 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\n Reference: https://o365blog.com/post/deviceidentity/'\n",
        "displayName": "Microsoft Entra ID Local Device Join Information and Transport Key Registry Keys Access",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Account",
                "identifier": "FullName"
              },
              {
                "columnName": "SubjectUserName",
                "identifier": "Name"
              },
              {
                "columnName": "SubjectDomainName",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "Computer",
                "identifier": "FullName"
              },
              {
                "columnName": "HostName",
                "identifier": "HostName"
              },
              {
                "columnName": "DnsDomain",
                "identifier": "DnsDomain"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Windows Security Events/Analytic Rules/LocalDeviceJoinInfoAndTransportKeyRegKeysAccess.yaml",
        "query": "// AADJoined or Register Device Registry Keys\nlet aadJoinRoot = \"\\\\REGISTRY\\\\MACHINE\\\\SYSTEM\\\\ControlSet001\\\\Control\\\\CloudDomainJoin\\\\JoinInfo\\\\\";\nlet aadRegisteredRoot = \"\\\\SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\WorkplaceJoin\";\n// Transport Key Registry Key\nlet keyTransportKey = \"\\\\REGISTRY\\\\MACHINE\\\\SYSTEM\\\\ControlSet001\\\\Control\\\\Cryptography\\\\Ngc\\\\KeyTransportKey\\\\\";\n(union isfuzzy=true\n(\n// Access to Object Requested\nSecurityEvent\n| where EventID == '4656'\n| where EventData has aadJoinRoot or EventData has aadRegisteredRoot\n| extend EventData = parse_xml(EventData).EventData.Data\n| mv-expand bagexpansion=array EventData\n| evaluate bag_unpack(EventData)\n| extend Key = tostring(column_ifexists('@Name', \"\")), Value = column_ifexists('#text', \"\")\n| evaluate pivot(Key, any(Value), TimeGenerated, Computer, EventID)\n| where ObjectType == 'Key'\n| where ObjectName startswith aadJoinRoot and SubjectLogonId != '0x3e7' //Local System\n| extend ProcessId = column_ifexists(\"ProcessId\", \"\"), Process = split(ProcessName, '\\\\', -1)[-1],Account = strcat(SubjectDomainName, \"\\\\\", SubjectUserName)\n| join kind=innerunique (\n    SecurityEvent\n    | where EventID == '4656'\n    | where EventData has keyTransportKey\n    | extend EventData = parse_xml(EventData).EventData.Data\n    | mv-expand bagexpansion=array EventData\n    | evaluate bag_unpack(EventData)\n    | extend Key = tostring(column_ifexists('@Name', \"\")), Value = column_ifexists('#text', \"\")\n    | evaluate pivot(Key, any(Value), TimeGenerated, Computer, EventID)\n    | extend ObjectName = column_ifexists(\"ObjectName\", \"\"),ObjectType = column_ifexists(\"ObjectType\", \"\")\n    | where ObjectType == 'Key'\n    | where ObjectName startswith keyTransportKey and SubjectLogonId != '0x3e7' //Local System\n    | extend ProcessId = column_ifexists(\"ProcessId\", \"\"), Process = split(ProcessName, '\\\\', -1)[-1],Account = strcat(SubjectDomainName, \"\\\\\", SubjectUserName)\n) on $left.Computer == $right.Computer and $left.SubjectLogonId == $right.SubjectLogonId and $left.ProcessId == $right.ProcessId\n| project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, SubjectLogonId, ObjectName, tostring(Process), ProcessName, ProcessId, EventID\n),\n// Accessing Object\n(\nSecurityEvent\n| where EventID == '4663'\n| where ObjectType == 'Key'\n| where (ObjectName startswith aadJoinRoot or ObjectName contains aadRegisteredRoot) and SubjectLogonId != '0x3e7' //Local System\n| extend Account = SubjectAccount\n| join kind=innerunique (\n    SecurityEvent\n    | where EventID == '4663'\n    | where ObjectType == 'Key'\n    | where ObjectName has keyTransportKey and SubjectLogonId != '0x3e7' //Local System\n    | extend Account = SubjectAccount\n) on $left.Computer == $right.Computer and $left.SubjectLogonId == $right.SubjectLogonId and $left.ProcessId == $right.ProcessId\n| project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, SubjectLogonId, ObjectName, Process, ProcessName, ProcessId, EventID\n| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))\n)\n)\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Discovery"
        ],
        "tags": [
          "SimuLand",
          "ATR",
          "AADInternals"
        ],
        "techniques": [
          "T1012"
        ],
        "templateVersion": "1.0.5",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}