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

User account created without expected attributes defined

Back
Iddc99e38c-f4e9-4837-94d7-353ac0b01a77
RulenameUser account created without expected attributes defined
DescriptionThis 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
SeverityLow
TacticsPersistence
TechniquesT1136.003
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml
Version1.1.0
Arm templatedc99e38c-f4e9-4837-94d7-353ac0b01a77.json
Deploy To Azure
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])
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: CreatingUserPrincipalName
  - identifier: Name
    columnName: InitiatingAccountName
  - identifier: UPNSuffix
    columnName: InitiatingAccountUPNSuffix
- entityType: Account
  fieldMappings:
  - identifier: AadUserId
    columnName: CreatingAadUserId
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: CreatedUserPrincipalName
  - identifier: Name
    columnName: TargetAccountName
  - identifier: UPNSuffix
    columnName: TargetAccountUPNSuffix
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: CreatingUserIPAddress
queryFrequency: 1d
name: User account created without expected attributes defined
severity: Low
kind: Scheduled
tactics:
- Persistence
metadata:
  author:
    name: Microsoft Security Research
  source:
    kind: Community
  categories:
    domains:
    - Security - Others
  support:
    tier: Community
triggerThreshold: 0
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])  
triggerOperator: gt
queryPeriod: 1d
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/Useraccountcreatedwithoutexpectedattributesdefined.yaml
relevantTechniques:
- T1136.003
tags:
- AADSecOpsGuide
id: dc99e38c-f4e9-4837-94d7-353ac0b01a77
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - AuditLogs
version: 1.1.0
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'  
{
  "$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"
    }
  ]
}