Detection rules › Kusto

Account Creation

Status
available
Severity
medium
Time window
1h
Group by
CreatedOnLocalMachine, CreatedUser, DeviceId, DeviceName, FileName, InitiatingProcessCommandLine, InitiatingProcessFileName, ProcessCommandLine, ProcessId
Source
github.com/Azure/Azure-Sentinel

User accounts may be created to achieve persistence on a machine. Read more here: https://attack.mitre.org/wiki/Technique/T1136. Tags: #CreateAccount. Query #1: Query for users being created using "net user" command. "net user" commands are noisy, so needs to be joined with another signal -. E.g. in this example we look for use of uncommon & undocumented commandline switches (e.g. /ad instead of /add).

MITRE ATT&CK coverage

TacticTechniques
PersistenceT1136 Create Account

Event coverage

Rule body kusto

id: 450f4e56-5bba-4070-b9d9-9204ba9d777d
name: Account Creation
description: |
  User accounts may be created to achieve persistence on a machine.
  Read more here: https://attack.mitre.org/wiki/Technique/T1136.
  Tags: #CreateAccount.
  Query #1: Query for users being created using "net user" command.
  "net user" commands are noisy, so needs to be joined with another signal -.
  E.g. in this example we look for use of uncommon & undocumented commandline switches (e.g. /ad instead of /add).
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1136
query: |
  DeviceProcessEvents
  // Pro-tip: 
  // There are many different ways to run a process from a file - e.g. by using full path, env. variables, ~1 annotation, more...
  // So, to find executions of a known filename, better filter on the filename (and possibly on folder path) than on the commandline.
  | where FileName in~ ("net.exe", "net1.exe")
  // Parse the user name from the commandline.
  // To have case-insensitive parsing use the i flag, to have non-greedy match (e.g. CreatedUser as short as possible), specify U flag:
  // "kind=regex flags=i"
  | parse kind=regex flags=iU ProcessCommandLine with * "user " CreatedUser " " * "/ad"
  // Filter rows where user could not be parsed - e.g. because it was not a user command, or the /add commandline switch was not specified.
  | where isnotempty(CreatedUser)
  // Every net.exe executed will run net1.exe with the same commandline.
  // in this where clause we remove such rows, as they duplicate the number of results we have without adding any value.
  | where not (FileName =~ "net1.exe" and InitiatingProcessFileName =~ "net.exe" and replace("net", "net1", InitiatingProcessCommandLine) =~ ProcessCommandLine)
  // If /domain is specified, so the user is created on the domain controller.
  // Also, any prefix that's longer than 1 char will also do the same, e.g. /do, /dom, /doma, ....
  | extend CreatedOnLocalMachine=(ProcessCommandLine !contains "/do")
  | where ProcessCommandLine !contains "/add" or (CreatedOnLocalMachine == 0 and ProcessCommandLine !contains "/domain")
  | summarize MachineCount=dcount(DeviceName) by CreatedUser, CreatedOnLocalMachine, InitiatingProcessFileName, FileName, ProcessId, ProcessCommandLine, InitiatingProcessCommandLine, DeviceId, DeviceName
  | extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
  | extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: ProcessId
      - identifier: CommandLine
        columnName: ProcessCommandLine 
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

DeviceProcessEvents

Stage 2: where

| where FileName in~ ("net.exe", "net1.exe")

Stage 3: parse

| parse kind=regex flags=iU ProcessCommandLine with * "user " CreatedUser " " * "/ad"

Stage 4: where

| where isnotempty(CreatedUser)

Stage 5: where

| where not (FileName =~ "net1.exe" and InitiatingProcessFileName =~ "net.exe" and replace("net", "net1", InitiatingProcessCommandLine) =~ ProcessCommandLine)

Stage 6: extend

| extend CreatedOnLocalMachine=(ProcessCommandLine !contains "/do")

Stage 7: where

| where ProcessCommandLine !contains "/add" or (CreatedOnLocalMachine == 0 and ProcessCommandLine !contains "/domain")

Stage 8: summarize

| summarize MachineCount=dcount(DeviceName) by CreatedUser, CreatedOnLocalMachine, InitiatingProcessFileName, FileName, ProcessId, ProcessCommandLine, InitiatingProcessCommandLine, DeviceId, DeviceName

Stage 9: extend

| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =
ifDeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))
elseDeviceName

Stage 10: extend

| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =
ifDeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))
else""

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
FileNameeqnet1.exe
InitiatingProcessCommandLinecross_field_compareProcessCommandLine
InitiatingProcessFileNameeqnet.exe

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
CreatedOnLocalMachineeq
  • 0 transforms: cased
CreatedUseris_not_null
  • (no value, null check)
FileNamein
  • net.exe
  • net1.exe

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
CreatedOnLocalMachinesummarize
CreatedUsersummarize
DeviceIdsummarize
DeviceNamesummarize
FileNamesummarize
InitiatingProcessCommandLinesummarize
InitiatingProcessFileNamesummarize
MachineCountsummarize
ProcessCommandLinesummarize
ProcessIdsummarize
HostNameextend
DnsDomainextend