Detection rules › Kusto

User account enabled and disabled within 10 mins

Severity
medium
Time window
25h
Group by
Computer, TargetAccount
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

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

MITRE ATT&CK coverage

Telemetry coverage

Rule body

id: 3d023f64-8225-41a2-9570-2bd7c2c4535e
name: User account enabled and disabled within 10 mins
description: |
  'Identifies when a user account is enabled and then disabled 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: 
      - SecurityEvents 
  - 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 enabled
      | where EventID == 4722
      | where AccountType =~ "User"
      | where TargetAccount !endswith "$"
      | project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName
      ),
      (
      WindowsEvent
      | where TimeGenerated > ago(timeframe+spanoftime)
      // A user account was enabled
      | where EventID == 4722
      | 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)
      | where TargetAccount !endswith "$"
      | extend Activity="4722 - A user account was enabled."
      | extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName) 
      | extend TargetSid = tostring(EventData.TargetSid)
      | extend UserPrincipalName = tostring(EventData.UserPrincipalName)
      | project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName
      )
    )
  | join kind= inner (
    (union isfuzzy=true
      (SecurityEvent
      | where TimeGenerated > ago(timeframe)
      // A user account was disabled
      | where EventID == 4725
      | where AccountType =~ "User"
      | project DisableTime = TimeGenerated, DisableEventID = EventID, DisableActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToDisable = SubjectAccount, DisabledBySubjectUserName = SubjectUserName, DisabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDisable = SubjectUserSid, UserPrincipalName
      ),
      (WindowsEvent
      | where TimeGenerated > ago(timeframe)
      // A user account was disabled
      | where EventID == 4725
      | 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 TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName) 
      | extend TargetSid = tostring(EventData.TargetSid)
      | extend UserPrincipalName = tostring(EventData.UserPrincipalName)
      | extend Activity = "4725 - A user account was disabled."
      | project DisableTime = TimeGenerated, DisableEventID = EventID, DisableActivity = Activity, Computer = toupper(Computer), 
      TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
      AccountUsedToDisable = SubjectAccount, DisabledBySubjectUserName = SubjectUserName, DisabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDisable = SubjectUserSid, UserPrincipalName
      )
    )
  ) on Computer, TargetAccount
  | where DisableTime - EnableTime < spanoftime
  | extend TimeDelta = DisableTime - EnableTime
  | where tolong(TimeDelta) >= threshold
  | project TimeDelta, EnableTime, EnableEventID, EnableActivity, Computer, TargetAccount, TargetSid, TargetUserName, TargetDomainName, UserPrincipalName, 
  AccountUsedToEnable, SIDofAccountUsedToEnable, DisableTime, DisableEventID, DisableActivity, AccountUsedToDisable, SIDofAccountUsedToDisable, 
  EnabledBySubjectUserName, EnabledBySubjectDomainName, DisabledBySubjectUserName, DisabledBySubjectDomainName
  | 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: AccountUsedToEnable
      - identifier: Name
        columnName: EnabledBySubjectUserName
      - identifier: NTDomain
        columnName: EnabledBySubjectDomainName
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountUsedToDisable
      - identifier: Name
        columnName: DisabledBySubjectUserName
      - identifier: NTDomain
        columnName: DisabledBySubjectDomainName
  - 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.3
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 of 2 branches

Stage 2: source

SecurityEvent

Stage 3: where

| where TimeGenerated > ago(timeframe+spanoftime)

Stage 4: where

| where EventID == 4722

Stage 5: where

| where AccountType =~ "User"

Stage 6: where

| where TargetAccount !endswith "$"

Stage 7: project

| project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer), 
    TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
    AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName

Stage 8: source

WindowsEvent

Stage 9: where

| where TimeGenerated > ago(timeframe+spanoftime)

Stage 10: where

| where EventID == 4722

Stage 11: extend

| extend SubjectUserSid = tostring(EventData.SubjectUserSid)

Stage 12: 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")
AccountType =
if(SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"))"Machine"
elifisempty(SubjectUserSid)""
else"User"

Stage 13: where

| where AccountType =~ "User"

Stage 14: extend (3 consecutive steps)

| 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)

Stage 15: where

| where TargetAccount !endswith "$"

Stage 16: extend (4 consecutive steps)

| extend Activity="4722 - A user account was enabled."
| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
| extend TargetSid = tostring(EventData.TargetSid)
| extend UserPrincipalName = tostring(EventData.UserPrincipalName)

Stage 17: project

| project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer), 
    TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid, 
    AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName

Stage 18: join

join kind=inner (...) on Computer, TargetAccount

Stage 19: where DisableTime - EnableTime < 10m

where DisableTime - EnableTime < 10m

Stage 20: extend

extend TimeDelta

Stage 21: where

where TimeDelta >= 0

Stage 22: project

project AccountUsedToDisable, AccountUsedToEnable, Computer, DisableActivity, DisableEventID, DisableTime, DisabledBySubjectDomainName, DisabledBySubjectUserName, EnableActivity, EnableEventID, EnableTime, EnabledBySubjectDomainName, EnabledBySubjectUserName, SIDofAccountUsedToDisable, SIDofAccountUsedToEnable, TargetAccount, TargetDomainName, TargetSid, TargetUserName, TimeDelta, UserPrincipalName

Stage 23: extend

extend DomainIndex, HostName

Stage 24: extend

extend HostNameDomain
HostNameDomain =
ifDomainIndex != -1substring(Computer, (DomainIndex + 1))
elseComputer

Stage 25: project-away

project-away DomainIndex

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AccountTypeeq
  • User corpus 5 (kusto 5)
field:"AccountType" kind:eq value:"User"
EventIDeq
  • 4722 corpus 2 (kusto 2)
  • 4725 corpus 2 (splunk 1, kusto 1)
field:"EventID" kind:eq
TimeDeltage
  • 0 transforms: tolong corpus 3 (kusto 3)
field:"TimeDelta" kind:ge value:"0"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
TargetAccountends_with$excludes:TargetAccount field:"TargetAccount" value:"$"

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountUsedToDisableproject
AccountUsedToEnableproject
Computerproject
DisableActivityproject
DisableEventIDproject
DisableTimeproject
DisabledBySubjectDomainNameproject
DisabledBySubjectUserNameproject
EnableActivityproject
EnableEventIDproject
EnableTimeproject
EnabledBySubjectDomainNameproject
EnabledBySubjectUserNameproject
SIDofAccountUsedToDisableproject
SIDofAccountUsedToEnableproject
TargetAccountproject
TargetDomainNameproject
TargetSidproject
TargetUserNameproject
TimeDeltaproject
UserPrincipalNameproject
HostNameextend
HostNameDomainextend