Detection rules › Kusto

User account created and deleted within 10 mins

Severity
medium
Time window
25h
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

Identifies when a user account is created and then deleted within 10 minutes. This can be an indication of compromise and an adversary attempting to hide in the noise.

MITRE ATT&CK coverage

Event coverage

Rule body kusto

id: 4b93c5af-d20b-4236-b696-a28b8c51407f
name: User account created and deleted within 10 mins
description: |
  'Identifies when a user account is created and then deleted within 10 minutes. This can be an indication of compromise and an adversary attempting to hide in the noise.'
severity: Medium
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsForwardedEvents
    dataTypes:
      - WindowsEvent
queryFrequency: 1d
queryPeriod: 25h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1098
  - T1078
query: |
  let timeframe = 1d;
  let spanoftime = 10m;
  let threshold = 0;
  (union isfuzzy=true
      (SecurityEvent
      | where TimeGenerated > ago(timeframe+spanoftime)
      // A user account was created
      | where EventID == 4720
      | where AccountType =~ "User"
      | project creationTime = TimeGenerated, CreateEventID = EventID, CreateActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToCreate = SubjectAccount, CreatedBySubjectUserName = SubjectUserName, CreatedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToCreate = SubjectUserSid, UserPrincipalName
      ),
      (
      WindowsEvent
      | where TimeGenerated > ago(timeframe+spanoftime)
      // A user account was created
      | where EventID == 4720
      | extend SubjectUserSid = tostring(EventData.SubjectUserSid)
      | extend AccountType=case(EventData.SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
      | where AccountType =~ "User"
      | extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
      | extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)
      | extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
      | extend TargetSid = tostring(EventData.TargetSid)
      | extend UserPrincipalName = tostring(EventData.UserPrincipalName)
      | extend Activity = "4720 - A user account was created."
      | extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName) 
      | project creationTime = TimeGenerated, CreateEventID = EventID, CreateActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToCreate = SubjectAccount, CreatedBySubjectUserName = SubjectUserName, CreatedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToCreate = SubjectUserSid, UserPrincipalName
      )
    )
  | join kind = inner 
  (
    (union isfuzzy=true
      (SecurityEvent
      | where TimeGenerated > ago(timeframe)
      // A user account was deleted
      | where EventID == 4726
      | where AccountType == "User"
      | project deletionTime = TimeGenerated, DeleteEventID = EventID, DeleteActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToDelete = SubjectAccount, DeletedBySubjectUserName = SubjectUserName, DeletedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDelete = SubjectUserSid, UserPrincipalName
      ),
      (WindowsEvent
      | where TimeGenerated > ago(timeframe)
      // A user account was deleted
      | where EventID == 4726
      | extend SubjectUserSid = tostring(EventData.SubjectUserSid)
      | extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
      | extend AccountType=case(SubjectAccount endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
      | where AccountType == "User"
      | extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)
      | extend TargetSid = tostring(EventData.TargetSid)
      | extend UserPrincipalName = tostring(EventData.UserPrincipalName)
      | extend Activity = "4726 - A user account was deleted."
      | extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName) 
      | extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
      | project deletionTime = TimeGenerated, DeleteEventID = EventID, DeleteActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToDelete = SubjectAccount, DeletedBySubjectUserName = SubjectUserName, DeletedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDelete = SubjectUserSid, UserPrincipalName
      )
    )
  ) on Computer, TargetAccount
  | where deletionTime - creationTime < spanoftime
  | extend TimeDelta = deletionTime - creationTime
  | where tolong(TimeDelta) >= threshold
  | project TimeDelta, creationTime, CreateEventID, CreateActivity, Computer, TargetAccount, TargetSid, UserPrincipalName, AccountUsedToCreate, SIDofAccountUsedToCreate,
  deletionTime, DeleteEventID, DeleteActivity, AccountUsedToDelete, SIDofAccountUsedToDelete, TargetUserName, TargetDomainName, 
  CreatedBySubjectUserName, CreatedBySubjectDomainName, DeletedBySubjectUserName, DeletedBySubjectDomainName
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | project-away DomainIndex
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountUsedToCreate
      - identifier: Name
        columnName: CreatedBySubjectUserName
      - identifier: NTDomain
        columnName: CreatedBySubjectDomainName
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountUsedToDelete
      - identifier: Name
        columnName: DeletedBySubjectUserName
      - identifier: NTDomain
        columnName: DeletedBySubjectDomainName
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: TargetAccount
      - identifier: Name
        columnName: TargetUserName
      - identifier: NTDomain
        columnName: TargetDomainName
  - entityType: Account
    fieldMappings:
      - identifier: Sid
        columnName: TargetSid
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
version: 1.2.2
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Identity" ]

Stages and Predicates

Stage 0: let

let timeframe = 1d;
let spanoftime = 10m;
let threshold = 0;

Stage 1: union

union isfuzzy=true

Stage 2: source time_window=90000s

SecurityEvent

Stage 3: where

| where TimeGenerated > ago(timeframe+spanoftime)

Stage 4: where

| where EventID == 4720

Stage 5: where

| where AccountType =~ "User"

Stage 6: project

| project creationTime = TimeGenerated, CreateEventID = EventID, CreateActivity = Activity, Computer = toupper(Computer), 
    TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
    AccountUsedToCreate = SubjectAccount, CreatedBySubjectUserName = SubjectUserName, CreatedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToCreate = SubjectUserSid, UserPrincipalName

Stage 7: source

WindowsEvent

Stage 8: where

| where TimeGenerated > ago(timeframe+spanoftime)

Stage 9: where

| where EventID == 4720

Stage 10: extend

| extend SubjectUserSid = tostring(EventData.SubjectUserSid)

Stage 11: extend

| extend AccountType=case(EventData.SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")

Stage 12: where

| where AccountType =~ "User"

Stage 13: extend

| extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))

Stage 14: extend

| extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)

Stage 15: extend

| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)

Stage 16: extend

| extend TargetSid = tostring(EventData.TargetSid)

Stage 17: extend

| extend UserPrincipalName = tostring(EventData.UserPrincipalName)

Stage 18: extend

| extend Activity = "4720 - A user account was created."

Stage 19: extend

| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)

Stage 20: project

| project creationTime = TimeGenerated, CreateEventID = EventID, CreateActivity = Activity, Computer = toupper(Computer), 
    TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
    AccountUsedToCreate = SubjectAccount, CreatedBySubjectUserName = SubjectUserName, CreatedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToCreate = SubjectUserSid, UserPrincipalName

Stage 21: join

| join kind = inner 
(
  (union isfuzzy=true
    (SecurityEvent
    | where TimeGenerated > ago(timeframe)
    | where EventID == 4726
    | where AccountType == "User"
    | project deletionTime = TimeGenerated, DeleteEventID = EventID, DeleteActivity = Activity, Computer = toupper(Computer), 
    TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
    AccountUsedToDelete = SubjectAccount, DeletedBySubjectUserName = SubjectUserName, DeletedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDelete = SubjectUserSid, UserPrincipalName
    ),
    (WindowsEvent
    | where TimeGenerated > ago(timeframe)
    | where EventID == 4726
    | extend SubjectUserSid = tostring(EventData.SubjectUserSid)
    | extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
    | extend AccountType=case(SubjectAccount endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
    | where AccountType == "User"
    | extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)
    | extend TargetSid = tostring(EventData.TargetSid)
    | extend UserPrincipalName = tostring(EventData.UserPrincipalName)
    | extend Activity = "4726 - A user account was deleted."
    | extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName) 
    | extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
    | project deletionTime = TimeGenerated, DeleteEventID = EventID, DeleteActivity = Activity, Computer = toupper(Computer), 
    TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
    AccountUsedToDelete = SubjectAccount, DeletedBySubjectUserName = SubjectUserName, DeletedBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDelete = SubjectUserSid, UserPrincipalName
    )
  )
) on Computer, TargetAccount

Stage 22: where where deletionTime - creationTime < 10m

| where deletionTime - creationTime < spanoftime

Stage 23: extend

| extend TimeDelta = deletionTime - creationTime

Stage 24: where

| where tolong(TimeDelta) >= threshold

Stage 25: project

| project TimeDelta, creationTime, CreateEventID, CreateActivity, Computer, TargetAccount, TargetSid, UserPrincipalName, AccountUsedToCreate, SIDofAccountUsedToCreate,
deletionTime, DeleteEventID, DeleteActivity, AccountUsedToDelete, SIDofAccountUsedToDelete, TargetUserName, TargetDomainName, 
CreatedBySubjectUserName, CreatedBySubjectDomainName, DeletedBySubjectUserName, DeletedBySubjectDomainName

Stage 26: extend

| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))

Stage 27: extend

| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)

Stage 28: project-away

| project-away DomainIndex

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
AccountTypeeq
  • User corpus 9 (kusto 9)
EventIDeq
  • 4720 transforms: cased corpus 5 (kusto 3, splunk 2)
  • 4726 transforms: cased corpus 2 (splunk 1, kusto 1)
TimeDeltage
  • 0 transforms: tolong, cased corpus 2 (kusto 2)

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
AccountUsedToCreateproject
AccountUsedToDeleteproject
Computerproject
CreateActivityproject
CreateEventIDproject
CreatedBySubjectDomainNameproject
CreatedBySubjectUserNameproject
DeleteActivityproject
DeleteEventIDproject
DeletedBySubjectDomainNameproject
DeletedBySubjectUserNameproject
SIDofAccountUsedToCreateproject
SIDofAccountUsedToDeleteproject
TargetAccountproject
TargetDomainNameproject
TargetSidproject
TargetUserNameproject
TimeDeltaproject
UserPrincipalNameproject
creationTimeproject
deletionTimeproject
HostNameextend
HostNameDomainextend