Suspicious Entra ID Joined Device Update
| Id | 3a3c6835-0086-40ca-b033-a93bf26d878f |
| Rulename | Suspicious Entra ID Joined Device Update |
| Description | This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance. This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys. Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf |
| Severity | Medium |
| Tactics | CredentialAccess |
| Techniques | T1528 |
| Required data connectors | AzureActiveDirectory |
| Kind | Scheduled |
| Query frequency | 1d |
| Query period | 1d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/SuspiciousAADJoinedDeviceUpdate.yaml |
| Version | 1.0.4 |
| Arm template | 3a3c6835-0086-40ca-b033-a93bf26d878f.json |
AuditLogs
| where OperationName =~ "Update device"
| mv-apply TargetResource=TargetResources on (
where TargetResource.type =~ "Device"
| extend ModifiedProperties = TargetResource.modifiedProperties
| extend DeviceId = TargetResource.id)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "CloudDisplayName"
| extend OldName = Prop.oldValue
| extend NewName = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "IsCompliant"
| extend OldComplianceState = Prop.oldValue
| extend NewComplianceState = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "TargetId.DeviceTrustType"
| extend OldTrustType = Prop.oldValue
| extend NewTrustType = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = Prop.newValue)
| extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
| extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
| extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
| extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))
| where OldDeviceName != NewDeviceName
| where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'
// Most common is transferring from AAD Registered to AAD Joined - we just want AAD Joined devices
| where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'
// We can modify this value to tune FPs - more properties changed about the device beyond its name the more suspicious it could be
| where UpdatedPropertiesCount > 1
| project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount
| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
queryPeriod: 1d
query: |
AuditLogs
| where OperationName =~ "Update device"
| mv-apply TargetResource=TargetResources on (
where TargetResource.type =~ "Device"
| extend ModifiedProperties = TargetResource.modifiedProperties
| extend DeviceId = TargetResource.id)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "CloudDisplayName"
| extend OldName = Prop.oldValue
| extend NewName = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "IsCompliant"
| extend OldComplianceState = Prop.oldValue
| extend NewComplianceState = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "TargetId.DeviceTrustType"
| extend OldTrustType = Prop.oldValue
| extend NewTrustType = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = Prop.newValue)
| extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
| extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
| extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
| extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))
| where OldDeviceName != NewDeviceName
| where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'
// Most common is transferring from AAD Registered to AAD Joined - we just want AAD Joined devices
| where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'
// We can modify this value to tune FPs - more properties changed about the device beyond its name the more suspicious it could be
| where UpdatedPropertiesCount > 1
| project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount
| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
name: Suspicious Entra ID Joined Device Update
entityMappings:
- fieldMappings:
- columnName: NewDeviceName
identifier: HostName
entityType: Host
- fieldMappings:
- columnName: OldDeviceName
identifier: HostName
entityType: Host
- fieldMappings:
- columnName: DeviceId
identifier: AzureID
entityType: Host
- fieldMappings:
- columnName: InitiatingUserPrincipalName
identifier: FullName
- columnName: InitiatedByName
identifier: Name
- columnName: InitiatedByUPNSuffix
identifier: UPNSuffix
entityType: Account
- fieldMappings:
- columnName: InitiatingAadUserId
identifier: AadUserId
entityType: Account
- fieldMappings:
- columnName: InitiatingAppServicePrincipalId
identifier: AadUserId
entityType: Account
- fieldMappings:
- columnName: InitiatingIpAddress
identifier: Address
entityType: IP
queryFrequency: 1d
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/SuspiciousAADJoinedDeviceUpdate.yaml
alertDetailsOverride:
alertDisplayNameFormat: Suspicious Entra ID Joined Device Update {{OldDeviceName}} renamed to {{NewDeviceName}} and {{UpdatedPropertiesCount}} properties changed
alertDescriptionFormat: |
This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
In this case {{OldDeviceName}} was renamed to {{NewDeviceName}} and {{UpdatedPropertiesCount}} properties were changed.
This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
description: |
'This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf'
kind: Scheduled
version: 1.0.4
status: Available
severity: Medium
relevantTechniques:
- T1528
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
id: 3a3c6835-0086-40ca-b033-a93bf26d878f