User account created without expected attributes defined
Id | dc99e38c-f4e9-4837-94d7-353ac0b01a77 |
Rulename | User account created without expected attributes defined |
Description | This query looks for accounts being created that do not have attributes populated that are commonly populated in the tenant. Attackers may attempt to add accounts as a means of establishing persistant access to an environment, looking for anomalies in created accounts may help identify illegitimately created accounts. Created accounts should be investigated to ensure they were legitimated created. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#accounts-not-following-naming-policies |
Severity | Low |
Tactics | Persistence |
Techniques | T1136.003 |
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/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml |
Version | 1.0.3 |
Arm template | dc99e38c-f4e9-4837-94d7-353ac0b01a77.json |
let threshold = 10;
let default_ad_attributes = dynamic(["LastDirSyncTime", "StsRefreshTokensValidFrom", "Included Updated Properties", "AccountEnabled", "Action Client Name", "SourceAnchor"]);
AuditLogs
| where OperationName =~ "Add user"
| where Result =~ "success"
| extend properties = TargetResources[0].modifiedProperties
| mv-expand properties
| evaluate bag_unpack(properties) : (displayName:string, oldValue: string, newValue: string , TenantId : string, SourceSystem : string, TimeGenerated : datetime, ResourceId : string, OperationName : string, OperationVersion : string, Category : string, ResultType : string, ResultSignature : string, ResultDescription : string, DurationMs : long, CorrelationId : string, Resource : string, ResourceGroup : string, ResourceProvider : string, Identity : string, Level : string, Location : string, AdditionalDetails : dynamic, Id : string, InitiatedBy : dynamic, LoggedByService : string, Result : string, ResultReason : string, TargetResources : dynamic, AADTenantId : string, ActivityDisplayName : string, ActivityDateTime : datetime, AADOperationType : string, Type : string)
| extend displayName = column_ifexists("displayName", "Unknown Value")
| summarize count() by displayName, TenantId
| where displayName !in (default_ad_attributes)
| top threshold by count_ desc
| summarize make_set(displayName) by TenantId
| join kind=inner (AuditLogs
| where Result =~ "success"
| where OperationName =~ "Add user"
| extend CreatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend AccountProperties = TargetResources[0].modifiedProperties
| mv-expand AccountProperties
| extend PropName = tostring(AccountProperties.displayName)) on TenantId
| summarize makeset(PropName) by TimeGenerated, CorrelationId, CreatedUserPrincipalName, CreatingUserPrincipalName, tostring(set_displayName)
| extend missing_props = set_difference(todynamic(set_displayName), set_PropName)
| where array_length(missing_props) > 0
| join kind=innerunique (AuditLogs
| where Result =~ "success"
| where OperationName =~ "Add user"
| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)) on CorrelationId, CreatedUserPrincipalName
| extend ExpectedProperties = set_displayName
| project-away set_displayName, set_PropName
severity: Low
queryFrequency: 1d
relevantTechniques:
- T1136.003
tactics:
- Persistence
kind: Scheduled
query: |
let threshold = 10;
let default_ad_attributes = dynamic(["LastDirSyncTime", "StsRefreshTokensValidFrom", "Included Updated Properties", "AccountEnabled", "Action Client Name", "SourceAnchor"]);
AuditLogs
| where OperationName =~ "Add user"
| where Result =~ "success"
| extend properties = TargetResources[0].modifiedProperties
| mv-expand properties
| evaluate bag_unpack(properties) : (displayName:string, oldValue: string, newValue: string , TenantId : string, SourceSystem : string, TimeGenerated : datetime, ResourceId : string, OperationName : string, OperationVersion : string, Category : string, ResultType : string, ResultSignature : string, ResultDescription : string, DurationMs : long, CorrelationId : string, Resource : string, ResourceGroup : string, ResourceProvider : string, Identity : string, Level : string, Location : string, AdditionalDetails : dynamic, Id : string, InitiatedBy : dynamic, LoggedByService : string, Result : string, ResultReason : string, TargetResources : dynamic, AADTenantId : string, ActivityDisplayName : string, ActivityDateTime : datetime, AADOperationType : string, Type : string)
| extend displayName = column_ifexists("displayName", "Unknown Value")
| summarize count() by displayName, TenantId
| where displayName !in (default_ad_attributes)
| top threshold by count_ desc
| summarize make_set(displayName) by TenantId
| join kind=inner (AuditLogs
| where Result =~ "success"
| where OperationName =~ "Add user"
| extend CreatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend AccountProperties = TargetResources[0].modifiedProperties
| mv-expand AccountProperties
| extend PropName = tostring(AccountProperties.displayName)) on TenantId
| summarize makeset(PropName) by TimeGenerated, CorrelationId, CreatedUserPrincipalName, CreatingUserPrincipalName, tostring(set_displayName)
| extend missing_props = set_difference(todynamic(set_displayName), set_PropName)
| where array_length(missing_props) > 0
| join kind=innerunique (AuditLogs
| where Result =~ "success"
| where OperationName =~ "Add user"
| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)) on CorrelationId, CreatedUserPrincipalName
| extend ExpectedProperties = set_displayName
| project-away set_displayName, set_PropName
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml
queryPeriod: 1d
version: 1.0.3
tags:
- AADSecOpsGuide
metadata:
support:
tier: Community
source:
kind: Community
categories:
domains:
- Security - Others
author:
name: Pete Bryan
name: User account created without expected attributes defined
requiredDataConnectors:
- dataTypes:
- AuditLogs
connectorId: AzureActiveDirectory
triggerOperator: gt
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: CreatingUserPrincipalName
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: CreatedUserPrincipalName
id: dc99e38c-f4e9-4837-94d7-353ac0b01a77
description: |
'This query looks for accounts being created that do not have attributes populated that are commonly populated in the tenant.
Attackers may attempt to add accounts as a means of establishing persistant access to an environment, looking for anomalies in created accounts may help identify illegitimately created accounts.
Created accounts should be investigated to ensure they were legitimated created.
Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#accounts-not-following-naming-policies'
triggerThreshold: 0
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/dc99e38c-f4e9-4837-94d7-353ac0b01a77')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/dc99e38c-f4e9-4837-94d7-353ac0b01a77')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "Scheduled",
"apiVersion": "2022-11-01-preview",
"properties": {
"displayName": "User account created without expected attributes defined",
"description": "'This query looks for accounts being created that do not have attributes populated that are commonly populated in the tenant.\n Attackers may attempt to add accounts as a means of establishing persistant access to an environment, looking for anomalies in created accounts may help identify illegitimately created accounts.\n Created accounts should be investigated to ensure they were legitimated created.\n Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#accounts-not-following-naming-policies'\n",
"severity": "Low",
"enabled": true,
"query": "let threshold = 10;\nlet default_ad_attributes = dynamic([\"LastDirSyncTime\", \"StsRefreshTokensValidFrom\", \"Included Updated Properties\", \"AccountEnabled\", \"Action Client Name\", \"SourceAnchor\"]);\nAuditLogs\n| where OperationName =~ \"Add user\"\n| where Result =~ \"success\"\n| extend properties = TargetResources[0].modifiedProperties\n| mv-expand properties\n| evaluate bag_unpack(properties) : (displayName:string, oldValue: string, newValue: string , TenantId : string, SourceSystem : string, TimeGenerated : datetime, ResourceId : string, OperationName : string, OperationVersion : string, Category : string, ResultType : string, ResultSignature : string, ResultDescription : string, DurationMs : long, CorrelationId : string, Resource : string, ResourceGroup : string, ResourceProvider : string, Identity : string, Level : string, Location : string, AdditionalDetails : dynamic, Id : string, InitiatedBy : dynamic, LoggedByService : string, Result : string, ResultReason : string, TargetResources : dynamic, AADTenantId : string, ActivityDisplayName : string, ActivityDateTime : datetime, AADOperationType : string, Type : string)\n| extend displayName = column_ifexists(\"displayName\", \"Unknown Value\")\n| summarize count() by displayName, TenantId\n| where displayName !in (default_ad_attributes)\n| top threshold by count_ desc\n| summarize make_set(displayName) by TenantId\n| join kind=inner (AuditLogs\n| where Result =~ \"success\"\n| where OperationName =~ \"Add user\"\n| extend CreatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)\n| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)\n| extend AccountProperties = TargetResources[0].modifiedProperties\n| mv-expand AccountProperties\n| extend PropName = tostring(AccountProperties.displayName)) on TenantId\n| summarize makeset(PropName) by TimeGenerated, CorrelationId, CreatedUserPrincipalName, CreatingUserPrincipalName, tostring(set_displayName)\n| extend missing_props = set_difference(todynamic(set_displayName), set_PropName)\n| where array_length(missing_props) > 0\n| join kind=innerunique (AuditLogs\n| where Result =~ \"success\"\n| where OperationName =~ \"Add user\"\n| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)) on CorrelationId, CreatedUserPrincipalName\n| extend ExpectedProperties = set_displayName\n| project-away set_displayName, set_PropName\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Persistence"
],
"techniques": [
"T1136.003"
],
"alertRuleTemplateName": "dc99e38c-f4e9-4837-94d7-353ac0b01a77",
"customDetails": null,
"entityMappings": [
{
"fieldMappings": [
{
"columnName": "CreatingUserPrincipalName",
"identifier": "FullName"
}
],
"entityType": "Account"
},
{
"fieldMappings": [
{
"columnName": "CreatedUserPrincipalName",
"identifier": "FullName"
}
],
"entityType": "Account"
}
],
"tags": [
"AADSecOpsGuide"
],
"templateVersion": "1.0.3",
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml"
}
}
]
}