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

Account created from non-approved sources

Back
Id99d589fa-7337-40d7-91a0-c96d0c4fa437
RulenameAccount created from non-approved sources
DescriptionThis query looks for an account being created from a domain that is not regularly seen in a tenant.

Attackers may attempt to add accounts from these sources as a means of establishing persistant access to an environment.

Created accounts should be investigated to confirm expected creation.

Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-accounts
SeverityMedium
TacticsPersistence
TechniquesT1136.003
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency1d
Query period7d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/Accountcreatedfromnon-approvedsources.yaml
Version1.2.1
Arm template99d589fa-7337-40d7-91a0-c96d0c4fa437.json
Deploy To Azure
let core_domains = (SigninLogs
  | where TimeGenerated > ago(7d)
  | where ResultType == 0
  | extend domain = tolower(split(UserPrincipalName, "@")[1])
  | summarize by tostring(domain));
  let alternative_domains = (SigninLogs
  | where TimeGenerated > ago(7d)
  | where isnotempty(AlternateSignInName)
  | where ResultType == 0
  | extend domain = tolower(split(AlternateSignInName, "@")[1])
  | summarize by tostring(domain));
  AuditLogs
  | where TimeGenerated > ago(1d)
  | where OperationName =~ "Add User"
  | 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 UserAdded = tostring(TargetResources[0].userPrincipalName)
  | extend UserAddedDomain = case(
  UserAdded has "#EXT#", tostring(split(tostring(split(UserAdded, "#EXT#")[0]), "_")[1]),
  UserAdded !has "#EXT#", tostring(split(UserAdded, "@")[1]),
  UserAdded)
  | where UserAddedDomain !in (core_domains) and UserAddedDomain !in (alternative_domains)
  | extend AddedByName = case(
  InitiatingUserPrincipalName has "#EXT#", tostring(split(tostring(split(InitiatingUserPrincipalName, "#EXT#")[0]), "_")[0]),
  InitiatingUserPrincipalName !has "#EXT#", tostring(split(InitiatingUserPrincipalName, "@")[0]),
  InitiatingUserPrincipalName)
  | extend AddedByUPNSuffix = case(
  InitiatingUserPrincipalName has "#EXT#", tostring(split(tostring(split(InitiatingUserPrincipalName, "#EXT#")[0]), "_")[1]),
  InitiatingUserPrincipalName !has "#EXT#", tostring(split(InitiatingUserPrincipalName, "@")[1]),
  InitiatingUserPrincipalName)
  | extend UserAddedName = case(
  UserAdded has "#EXT#", tostring(split(tostring(split(UserAdded, "#EXT#")[0]), "_")[0]),
  UserAdded !has "#EXT#", tostring(split(UserAdded, "@")[0]),
  UserAdded)
description: |
  'This query looks for an account being created from a domain that is not regularly seen in a tenant.
    Attackers may attempt to add accounts from these sources as a means of establishing persistant access to an environment.
    Created accounts should be investigated to confirm expected creation.
    Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-accounts'  
kind: Scheduled
tactics:
- Persistence
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
  - AuditLogs
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/Accountcreatedfromnon-approvedsources.yaml
severity: Medium
name: Account created from non-approved sources
metadata:
  support:
    tier: Community
  author:
    name: Microsoft Security Research
  categories:
    domains:
    - Security - Others
    - Identity
  source:
    kind: Community
triggerThreshold: 0
queryPeriod: 7d
query: |
  let core_domains = (SigninLogs
    | where TimeGenerated > ago(7d)
    | where ResultType == 0
    | extend domain = tolower(split(UserPrincipalName, "@")[1])
    | summarize by tostring(domain));
    let alternative_domains = (SigninLogs
    | where TimeGenerated > ago(7d)
    | where isnotempty(AlternateSignInName)
    | where ResultType == 0
    | extend domain = tolower(split(AlternateSignInName, "@")[1])
    | summarize by tostring(domain));
    AuditLogs
    | where TimeGenerated > ago(1d)
    | where OperationName =~ "Add User"
    | 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 UserAdded = tostring(TargetResources[0].userPrincipalName)
    | extend UserAddedDomain = case(
    UserAdded has "#EXT#", tostring(split(tostring(split(UserAdded, "#EXT#")[0]), "_")[1]),
    UserAdded !has "#EXT#", tostring(split(UserAdded, "@")[1]),
    UserAdded)
    | where UserAddedDomain !in (core_domains) and UserAddedDomain !in (alternative_domains)
    | extend AddedByName = case(
    InitiatingUserPrincipalName has "#EXT#", tostring(split(tostring(split(InitiatingUserPrincipalName, "#EXT#")[0]), "_")[0]),
    InitiatingUserPrincipalName !has "#EXT#", tostring(split(InitiatingUserPrincipalName, "@")[0]),
    InitiatingUserPrincipalName)
    | extend AddedByUPNSuffix = case(
    InitiatingUserPrincipalName has "#EXT#", tostring(split(tostring(split(InitiatingUserPrincipalName, "#EXT#")[0]), "_")[1]),
    InitiatingUserPrincipalName !has "#EXT#", tostring(split(InitiatingUserPrincipalName, "@")[1]),
    InitiatingUserPrincipalName)
    | extend UserAddedName = case(
    UserAdded has "#EXT#", tostring(split(tostring(split(UserAdded, "#EXT#")[0]), "_")[0]),
    UserAdded !has "#EXT#", tostring(split(UserAdded, "@")[0]),
    UserAdded)  
relevantTechniques:
- T1136.003
id: 99d589fa-7337-40d7-91a0-c96d0c4fa437
queryFrequency: 1d
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: InitiatingAppName
    identifier: Name
  - columnName: InitiatingAppServicePrincipalId
    identifier: AadUserId
- entityType: Account
  fieldMappings:
  - columnName: InitiatingUserPrincipalName
    identifier: FullName
  - columnName: AddedByName
    identifier: Name
  - columnName: AddedByUPNSuffix
    identifier: UPNSuffix
- entityType: Account
  fieldMappings:
  - columnName: InitiatingAadUserId
    identifier: AadUserId
- entityType: IP
  fieldMappings:
  - columnName: InitiatingIpAddress
    identifier: Address
- entityType: Account
  fieldMappings:
  - columnName: UserAdded
    identifier: FullName
  - columnName: UserAddedName
    identifier: Name
  - columnName: UserAddedDomain
    identifier: UPNSuffix
triggerOperator: gt
version: 1.2.1
tags:
- AADSecOpsGuide