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.1.0 |
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"]);
let addUsers = AuditLogs
| where OperationName =~ "Add user"
| where Result =~ "success"
| extend AccountProperties = TargetResources[0].modifiedProperties
| mv-expand AccountProperties
;
addUsers
| evaluate bag_unpack(AccountProperties) : (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 (
addUsers
| extend CreatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend CreatingAadUserId = tostring(InitiatedBy.user.id)
| extend CreatingUserIPAddress = tostring(InitiatedBy.user.ipAddress)
| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend PropName = tostring(AccountProperties.displayName))
on TenantId
| summarize makeset(PropName) by TimeGenerated, CorrelationId, CreatedUserPrincipalName, CreatingUserPrincipalName, CreatingAadUserId, CreatingUserIPAddress, 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
| extend InitiatingAccountName = tostring(split(CreatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(CreatingUserPrincipalName, "@")[1])
| extend TargetAccountName = tostring(split(CreatedUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(CreatedUserPrincipalName, "@")[1])
id: dc99e38c-f4e9-4837-94d7-353ac0b01a77
tactics:
- Persistence
queryPeriod: 1d
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml
metadata:
categories:
domains:
- Security - Others
source:
kind: Community
support:
tier: Community
author:
name: Microsoft Security Research
triggerThreshold: 0
name: User account created without expected attributes defined
query: |
let threshold = 10;
let default_ad_attributes = dynamic(["LastDirSyncTime", "StsRefreshTokensValidFrom", "Included Updated Properties", "AccountEnabled", "Action Client Name", "SourceAnchor"]);
let addUsers = AuditLogs
| where OperationName =~ "Add user"
| where Result =~ "success"
| extend AccountProperties = TargetResources[0].modifiedProperties
| mv-expand AccountProperties
;
addUsers
| evaluate bag_unpack(AccountProperties) : (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 (
addUsers
| extend CreatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend CreatingAadUserId = tostring(InitiatedBy.user.id)
| extend CreatingUserIPAddress = tostring(InitiatedBy.user.ipAddress)
| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend PropName = tostring(AccountProperties.displayName))
on TenantId
| summarize makeset(PropName) by TimeGenerated, CorrelationId, CreatedUserPrincipalName, CreatingUserPrincipalName, CreatingAadUserId, CreatingUserIPAddress, 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
| extend InitiatingAccountName = tostring(split(CreatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(CreatingUserPrincipalName, "@")[1])
| extend TargetAccountName = tostring(split(CreatedUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(CreatedUserPrincipalName, "@")[1])
severity: Low
triggerOperator: gt
kind: Scheduled
relevantTechniques:
- T1136.003
tags:
- AADSecOpsGuide
queryFrequency: 1d
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
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'
version: 1.1.0
entityMappings:
- fieldMappings:
- columnName: CreatingUserPrincipalName
identifier: FullName
- columnName: InitiatingAccountName
identifier: Name
- columnName: InitiatingAccountUPNSuffix
identifier: UPNSuffix
entityType: Account
- fieldMappings:
- columnName: CreatingAadUserId
identifier: AadUserId
entityType: Account
- fieldMappings:
- columnName: CreatedUserPrincipalName
identifier: FullName
- columnName: TargetAccountName
identifier: Name
- columnName: TargetAccountUPNSuffix
identifier: UPNSuffix
entityType: Account
- fieldMappings:
- columnName: CreatingUserIPAddress
identifier: Address
entityType: IP
{
"$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/dc99e38c-f4e9-4837-94d7-353ac0b01a77')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/dc99e38c-f4e9-4837-94d7-353ac0b01a77')]",
"properties": {
"alertRuleTemplateName": "dc99e38c-f4e9-4837-94d7-353ac0b01a77",
"customDetails": null,
"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",
"displayName": "User account created without expected attributes defined",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "CreatingUserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "InitiatingAccountName",
"identifier": "Name"
},
{
"columnName": "InitiatingAccountUPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "CreatingAadUserId",
"identifier": "AadUserId"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "CreatedUserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "TargetAccountName",
"identifier": "Name"
},
{
"columnName": "TargetAccountUPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "CreatingUserIPAddress",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml",
"query": "let threshold = 10;\nlet default_ad_attributes = dynamic([\"LastDirSyncTime\", \"StsRefreshTokensValidFrom\", \"Included Updated Properties\", \"AccountEnabled\", \"Action Client Name\", \"SourceAnchor\"]);\nlet addUsers = AuditLogs\n| where OperationName =~ \"Add user\"\n| where Result =~ \"success\"\n| extend AccountProperties = TargetResources[0].modifiedProperties\n| mv-expand AccountProperties\n;\naddUsers\n| evaluate bag_unpack(AccountProperties) : (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 (\naddUsers\n| extend CreatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)\n| extend CreatingAadUserId = tostring(InitiatedBy.user.id)\n| extend CreatingUserIPAddress = tostring(InitiatedBy.user.ipAddress)\n| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)\n| extend PropName = tostring(AccountProperties.displayName)) \non TenantId\n| summarize makeset(PropName) by TimeGenerated, CorrelationId, CreatedUserPrincipalName, CreatingUserPrincipalName, CreatingAadUserId, CreatingUserIPAddress, 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 (\nAuditLogs\n| where Result =~ \"success\"\n| where OperationName =~ \"Add user\"\n| extend CreatedUserPrincipalName = tostring(TargetResources[0].userPrincipalName)) \non CorrelationId, CreatedUserPrincipalName\n| extend ExpectedProperties = set_displayName\n| project-away set_displayName, set_PropName\n| extend InitiatingAccountName = tostring(split(CreatingUserPrincipalName, \"@\")[0]), InitiatingAccountUPNSuffix = tostring(split(CreatingUserPrincipalName, \"@\")[1])\n| extend TargetAccountName = tostring(split(CreatedUserPrincipalName, \"@\")[0]), TargetAccountUPNSuffix = tostring(split(CreatedUserPrincipalName, \"@\")[1])\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"severity": "Low",
"subTechniques": [
"T1136.003"
],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Persistence"
],
"tags": [
"AADSecOpsGuide"
],
"techniques": [
"T1136"
],
"templateVersion": "1.1.0",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}