Detection rules › Kusto
User State changed from Guest to Member
Detects when a guest account in a tenant is converted to a member of the tenant. Monitoring guest accounts and the access they are provided is important to detect potential account abuse. Accounts converted to members should be investigated to ensure the activity was legitimate. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#monitoring-for-failed-unusual-sign-ins
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Azure | Update user |
Rules detecting the same action
These rules filter on the same operation.
- [Entra ID] Authentication Method Changed for Privileged Account (Kusto)
- Account Created and Deleted in Short Timeframe (Kusto)
- Account Created And Deleted Within A Close Time Frame (Sigma)
- Account created from non-approved sources (Kusto)
- Account created or deleted by non-approved user (Kusto)
- Addition of a Temporary Access Pass to a Privileged Account (Kusto)
- Authentication Method Changed for Privileged Account (Kusto)
- Authentication Methods Changed for Privileged Account (Kusto)
Rule body
id: a09a0b8e-30fe-4ebf-94a0-cffe50f579cd
name: User State changed from Guest to Member
description: |
'Detects when a guest account in a tenant is converted to a member of the tenant.
Monitoring guest accounts and the access they are provided is important to detect potential account abuse.
Accounts converted to members should be investigated to ensure the activity was legitimate.
Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#monitoring-for-failed-unusual-sign-ins'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1098
tags:
- AADSecOpsGuide
query: |
AuditLogs
| where OperationName =~ "Update user"
| where Result =~ "success"
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| where TargetResources_modifiedProperties.displayName =~ "TargetId.UserType"
| extend UpdatingAppName = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend UpdatingServicePrincipalId = tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalId)
| extend UpdatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend UpdatingUserAadUserId = tostring(parse_json(tostring(InitiatedBy.user)).id)
| extend UpdatingUserIPAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend UpdatingUser = iif(isnotempty(UpdatingServicePrincipalId), UpdatingServicePrincipalId, UpdatingUserPrincipalName)
| extend UpdatedUserPrincipalName = tostring(TargetResources.userPrincipalName)
| project-reorder TimeGenerated, UpdatedUserPrincipalName, UpdatingUser
| where parse_json(tostring(TargetResources_modifiedProperties.newValue)) =~ "\"Member\"" and parse_json(tostring(TargetResources_modifiedProperties.oldValue)) =~ "\"Guest\""
| extend InitiatingAccountName = tostring(split(UpdatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(UpdatingUserPrincipalName, "@")[1])
| extend TargetAccountName = tostring(split(UpdatedUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(UpdatedUserPrincipalName, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UpdatingAppName
- identifier: AadUserId
columnName: UpdatingServicePrincipalId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UpdatingUserPrincipalName
- identifier: Name
columnName: InitiatingAccountName
- identifier: UPNSuffix
columnName: InitiatingAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: UpdatingUserAadUserId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UpdatedUserPrincipalName
- identifier: Name
columnName: TargetAccountName
- identifier: UPNSuffix
columnName: TargetAccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: UpdatingUserIPAddress
version: 1.1.0
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Stage 1: source
AuditLogs
Stage 2: where
| where OperationName =~ "Update user"
Stage 3: where
| where Result =~ "success"
Stage 4: mv-expand
| mv-expand TargetResources
Stage 5: mv-expand
| mv-expand TargetResources.modifiedProperties
Stage 6: where
| where TargetResources_modifiedProperties.displayName =~ "TargetId.UserType"
Stage 7: extend (7 consecutive steps)
| extend UpdatingAppName = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend UpdatingServicePrincipalId = tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalId)
| extend UpdatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend UpdatingUserAadUserId = tostring(parse_json(tostring(InitiatedBy.user)).id)
| extend UpdatingUserIPAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend UpdatingUser = iif(isnotempty(UpdatingServicePrincipalId), UpdatingServicePrincipalId, UpdatingUserPrincipalName)
| extend UpdatedUserPrincipalName = tostring(TargetResources.userPrincipalName)
Stage 8: project-reorder
| project-reorder TimeGenerated, UpdatedUserPrincipalName, UpdatingUser
Stage 9: where
| where parse_json(tostring(TargetResources_modifiedProperties.newValue)) =~ "\"Member\"" and parse_json(tostring(TargetResources_modifiedProperties.oldValue)) =~ "\"Guest\""
Stage 10: extend
| extend InitiatingAccountName = tostring(split(UpdatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(UpdatingUserPrincipalName, "@")[1])
Stage 11: extend
| extend TargetAccountName = tostring(split(UpdatedUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(UpdatedUserPrincipalName, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OperationName | eq |
| field:"OperationName" kind:eq value:"Update user" |
Result | eq |
| field:"Result" kind:eq value:"success" |
displayName | eq |
| field:"displayName" kind:eq value:"TargetId.UserType" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UpdatingAppName | extend |
UpdatingServicePrincipalId | extend |
UpdatingUserPrincipalName | extend |
UpdatingUserAadUserId | extend |
UpdatingUserIPAddress | extend |
UpdatingUser | extend |
UpdatedUserPrincipalName | extend |
InitiatingAccountName | extend |
InitiatingAccountUPNSuffix | extend |
TargetAccountName | extend |
TargetAccountUPNSuffix | extend |