← Back to SOC feed Coverage →

Create account

kql MEDIUM Azure-Sentinel
DeviceProcessEvents
huntingmicrosoftofficialpersistence
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-23T23:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may create user accounts as part of establishing persistence or maintaining access within an environment. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential long-term compromise and unauthorized access attempts.

KQL 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") and Timestamp > ago(3d)
// 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, ProcessCommandLine, InitiatingProcessCommandLine 

Analytic Rule Definition

id: ae177dc6-a3ba-474d-87a9-28ff7efb7b21
name: Create account
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).
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - DeviceProcessEvents
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") and Timestamp > ago(3d)
  // 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, ProcessCommandLine, InitiatingProcessCommandLine 

Required Data Sources

Sentinel TableNotes
DeviceProcessEventsEnsure this data connector is enabled

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/Microsoft 365 Defender/Persistence/Create account.yaml