Kusto Query Language is designed to work with large-scale data sets and is particularly well-suited for log and telemetry data analysis. It allows users to perform complex data manipulations, aggregations, and visualizations to derive insights from vast amounts of data efficiently.
Below are some of the KQL queries for AD Security Events.
——————————————————————————————————————
AAD Password Protection-AllEvents
//If you add “Microsoft-AzureADPasswordProtection-DCAgent/Admin” as a log source to Sentinel/Log Analytics you can query Azure AD Password Protection events
Event
| where Source == “Microsoft-AzureADPasswordProtection-DCAgent”
| where EventID in (“10014”, “10015”, “10016”, “30002”, “30004”, “30026”, “10024”, “30008”, “30010”, “30028”, “30024”, “30003”, “30005”, “30027”, “30022”, “30007”, “10025”, “30009”, “30029”, “30023”)
——————————————————————————————————————
Table = SecurityEvent | Account Pre Auth Changes
//Detect when Kerberos preauthentication is enabled or disabled for a user
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where EventID == 4738
| where AccountType == “User”
| where UserAccountControl has_any (“2064”, “2096”)
| extend Action = case(UserAccountControl has “2096”, strcat(“Kerberos preauthentication disabled”),
UserAccountControl has “2064”, strcat(“Kerberos preauthentication enabled”),
“unknown”)
| project TimeGenerated, Actor=SubjectAccount, User=TargetAccount, Action
——————————————————————————————————————
Table = SecurityEvent | Account Sensitivity Changed
//Detect when the ‘account is sensitive and cannot be delegated’ flag on an account is changed
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| project TimeGenerated, EventID, TargetAccount, SubjectAccount, UserAccountControl
| where EventID == “4738”
| where UserAccountControl has_any(“2094”, “2062”)
| extend Activity = case
(UserAccountControl contains “2094”, strcat(“Account Sensitivity Enabled”),
UserAccountControl contains “2062”, strcat(“Account Sensitivity Disabled”),
“Unknown”)
| project TimeGenerated, Target=TargetAccount, Actor=SubjectAccount, Activity
——————————————————————————————————————
Table = SecurityEvent | Account Set Password Not Required
//Alert when an Active Directory account is set to password not required
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| project TimeGenerated, EventID, TargetAccount, SubjectAccount, UserAccountControl
| where EventID == “4738”
| where UserAccountControl has (“2082”)
| extend Activity = strcat(“Account set to password not required”)
| project TimeGenerated, Target=TargetAccount, Actor=SubjectAccount, Activity
——————————————————————————————————————
Table = SecurityEvent | Anomalous IPC Recon
//Use series_decompose_anomalies to detect potentially anomalous IPC$ recon events. Configure start time as your anomaly learning period and timeframe as your detection period.
// Detection threshold determines the sensitivity, the higher the threshold value the higher the anomaly required to detect.
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent.
let starttime = 7d;
let timeframe = 30m;
let detectionthreshold = 2;
let outliers =
SecurityEvent
| project TimeGenerated, Account, Computer, EventID, ShareName
| where TimeGenerated > ago(starttime)
// Exclude known Accounts that often connect to various machines, such as Defender for ID or vulnerability management software
| where Account !in (“DOMAIN\Account1”)
| where EventID == “5140”
| where ShareName == “\\\IPC$” | order by TimeGenerated | summarize Events=count()by Account, bin(TimeGenerated, timeframe) | summarize EventCount=make_list(Events),TimeGenerated=make_list(TimeGenerated) by Account | extend outliers=series_decompose_anomalies(EventCount, detectionthreshold) | mv-expand TimeGenerated, EventCount, outliers | where outliers == 1 | distinct Account; SecurityEvent | project TimeGenerated, Account, Computer, EventID, ShareName, IpAddress | where TimeGenerated > ago(timeframe) | where EventID == “5140” | where ShareName == “\\\IPC$”
// Exclude computer objects connecting to themselves by parsing DOMAIN\Computer$ objects and Computer.DOMAIN.COM objects and excluding matches
| parse Account with * “\” AccountParse “$”
| parse Computer with ComputerParse “.” *
| where AccountParse != ComputerParse
// Find remaining outliers and make a set
| where Account in (outliers)
| summarize AccountActivity=make_set(Computer) by Account
——————————————————————————————————————
Table = SecurityEvent | Daily Summary of Group Additions
//Create a daily report of users being added to on premise Active Directory groups, summarized by group name
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where TimeGenerated > ago (7d)
| where AccountType == “User”
| where EventID in (4728, 4732, 4756, 4761, 4746, 4751)
| project TimeGenerated, MemberName, [‘Group Name’]=TargetUserName, EventID
| parse MemberName with * ‘CN=’ UserAdded ‘,’ *
| summarize UsersAdded=make_set(UserAdded) by [‘Group Name’], startofday(TimeGenerated)
| sort by [‘Group Name’] asc, TimeGenerated desc
——————————————————————————————————————
Table = SecurityEvent | Detect Privileged AAD Admin Password Change
//Detects when a user with a privileged Azure AD role has had their on premises Active Directory password changed by someone other than themselves.
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
//Data connector required for this query – Microsoft Sentinel UEBA
let timeframe=7d;
//First find any users that hold privileged Azure AD roles
IdentityInfo
| where TimeGenerated > ago(21d)
| where isnotempty(AssignedRoles)
| where AssignedRoles != “[]”
| summarize arg_max(TimeGenerated, *) by AccountUPN
| project AccountUPN, AccountName, AccountSID
//Join those users based on AccountSID to on premises Active Directory password reset events
| join kind=inner (
SecurityEvent
| where TimeGenerated > ago(timeframe)
| where EventID == “4724”
| project
TimeGenerated,
Activity,
SubjectAccount,
TargetAccount,
TargetSid,
SubjectUserSid
)
on $left.AccountSID == $right.TargetSid
| where SubjectUserSid != TargetSid
//Summarize event data to make it easy to read
| project [‘Time of Password Reset’]=TimeGenerated, Activity, Actor=SubjectAccount, [‘Target UserPrincipalName’]=AccountUPN,[‘Target AccountName’]=TargetAccount
——————————————————————————————————————
Table = SecurityEvent | GPO Inheritance Changed
//Detect when group policy inheritance is either allowed or blocked
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| project TimeGenerated, EventID, EventData, SubjectAccount
| where EventID == “5136”
| parse EventData with * ‘ObjectDN”>’ OU ” LDAPAttribute ” AttributeValue ‘%%’ OperationType ‘</Data’ *
| project
TimeGenerated,
Actor=SubjectAccount,
OU,
LDAPAttribute,
AttributeValue,
OperationType
| where LDAPAttribute == “gPOptions”
| where AttributeValue == “1”
| extend Activity = case
(OperationType == “14674” and AttributeValue == “1”, strcat(“Group Policy Inheritance Blocked”),
OperationType == “14675” and AttributeValue == “1”, strcat(“Group Policy Inheritance Allowed”),
“Unknown”)
| project TimeGenerated, Actor, OU, Activity
——————————————————————————————————————
Table = SecurityEvent | Logon To Device List Changed
//Alert when the ‘Log on to’ device list is changed for a user
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where EventID == 4738
| where AccountType == “User”
//Include domain accounts only (excluding local accounts)
| where TargetDomainName == SubjectDomainName
| extend [‘Allowed Devices’] = case(isnotempty(UserWorkstations) and UserWorkstations != “-” and UserWorkstations != “%%1793”, split(UserWorkstations, “,”),
(isnotempty(UserWorkstations) and UserWorkstations == “%%1793”), strcat(“User can log onto all devices”),
“unknown”)
//Exclude other 4738 events where the device list isn’t changed
| where [‘Allowed Devices’] != “unknown”
| project TimeGenerated, Actor=SubjectAccount, User=TargetAccount, [‘Allowed Devices’]
——————————————————————————————————————
Table = SecurityEvent | Summarize Privileges Assigned on Logon
//Create a summary of your computers and the accounts that have logged on with special privileges over the last 30 days
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where TimeGenerated > ago (30d)
| project TimeGenerated, EventID, Account, AccountType, PrivilegeList, Computer
| where EventID == “4672”
| where Account != “NT AUTHORITY\SYSTEM” and Account !has “Window Manager”
| where AccountType == “User”
//The privilege list is stored in a string of text that we need to split
| extend Privs=extract_all(@”Se(.*?)Privilege”, PrivilegeList)
//Once we retrieve the privileges from the string of text we can recreate the proper naming
| mv-expand Privs
| extend Privilege=strcat(‘Se’, Privs, ‘Privilege’)
| project TimeGenerated, Account, Computer, Privilege
| summarize [‘List of Privileges’]=make_set(Privilege) by Computer, Account
| sort by Computer asc
——————————————————————————————————————
Table = SecurityEvent | Summarize RDP Activity
//Creates a list of computers that your users have connected to via RDP and the total count of distinct computers each user has connected to
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where TimeGenerated > ago(7d)
| where EventID == “4624”
| where LogonType == 10
//Extend new column that drops Account to lower case so users are correctly summarized, i.e User123 and user123 are combined
| extend AccountName=tolower(Account)
| summarize
[‘Count of Computers’]=dcount(Computer),
[‘List of Computers’]=make_set(Computer)
by AccountName
| sort by [‘Count of Computers’] desc
——————————————————————————————————————
Table = SecurityEvent | UAC Flag Parser
//Creates a parser for all user account control changes changing the code into a readable message
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where isnotempty(UserAccountControl) and UserAccountControl != “-“
| where AccountType == “User”
| extend x = extract_all(@”([0-9]{4})”, UserAccountControl)
| mv-expand x
| extend [‘User Account Flag Description’] = case
(
x == “2048”, strcat(“Account Enabled”),
x == “2049”, strcat(“Home Directory Required – Disabled”),
x == “2050”, strcat(“Password Not Required – Disabled”),
x == “2051”, strcat(“Temp Duplicate Account – Disabled”),
x == “2052”, strcat(“Normal Account – Disabled”),
x == “2053”, strcat(“MNS Logon Account – Disabled”),
x == “2054”, strcat(“Interdomain Trust Account – Disabled”),
x == “2055”, strcat(“Workstation Trust Account – Disabled”),
x == “2056”, strcat(“Server Trust Account – Disabled”),
x == “2057”, strcat(“Don’t Expire Password – Disabled”),
x == “2058”, strcat(“Account Unlocked”),
x == “2059”, strcat(“Encrypted Text Password Allowed – Disabled”),
x == “2060”, strcat(“Smartcard Required – Disabled”),
x == “2061”, strcat(“Trusted For Delegation – Disabled”),
x == “2062”, strcat(“Not Delegated – Disabled”),
x == “2063”, strcat(“Use DES Key Only – Disabled”),
x == “2064”, strcat(“Don’t Require Preauth – Disabled”),
x == “2065”, strcat(“Password Expired – Disabled”),
x == “2066”, strcat(“Trusted To Authenticate For Delegation – Disabled”),
x == “2067”, strcat(“Exclude Authorization Information – Disabled”),
x == “2068”, strcat(“Undefined UserAccountControl Bit 20 – Disabled”),
x == “2069”, strcat(“Protect Kerberos Service Tickets with AES Keys – Disabled”),
x == “2070”, strcat(“Undefined UserAccountControl Bit 22 – Disabled”),
x == “2071”, strcat(“Undefined UserAccountControl Bit 23 – Disabled”),
x == “2072”, strcat(“Undefined UserAccountControl Bit 24 – Disabled”),
x == “2073”, strcat(“Undefined UserAccountControl Bit 25 – Disabled”),
x == “2074”, strcat(“Undefined UserAccountControl Bit 26 – Disabled”),
x == “2075”, strcat(“Undefined UserAccountControl Bit 27 – Disabled”),
x == “2076”, strcat(“Undefined UserAccountControl Bit 28 – Disabled”),
x == “2077”, strcat(“Undefined UserAccountControl Bit 29 – Disabled”),
x == “2078”, strcat(“Undefined UserAccountControl Bit 30 – Disabled”),
x == “2079”, strcat(“Undefined UserAccountControl Bit 31 – Disabled”),
x == “2080”, strcat(“Account Disabled”),
x == “2081”, strcat(“Home Directory Required – Enabled”),
x == “2082”, strcat(“Password Not Required – Enabled”),
x == “2083”, strcat(“Temp Duplicate Account – Enabled”),
x == “2084”, strcat(“Normal Account – Enabled”),
x == “2085”, strcat(“MNS Logon Account – Enabled”),
x == “2086”, strcat(“Interdomain Trust Account – Enabled”),
x == “2087”, strcat(“Workstation Trust Account – Enabled”),
x == “2088”, strcat(“Server Trust Account – Enabled”),
x == “2089”, strcat(“Don’t Expire Password – Enabled”),
x == “2090”, strcat(“Account Locked”),
x == “2091”, strcat(“Encrypted Text Password Allowed – Enabled”),
x == “2092”, strcat(“Smartcard Required – Enabled”),
x == “2093”, strcat(“Trusted For Delegation – Enabled”),
x == “2094”, strcat(“Not Delegated – Enabled”),
x == “2095”, strcat(“Use DES Key Only – Enabled”),
x == “2096”, strcat(“Don’t Require Preauth – Enabled”),
x == “2097”, strcat(“Password Expired – Enabled”),
x == “2098”, strcat(“Trusted To Authenticate For Delegation – Enabled”),
x == “2099”, strcat(“Exclude Authorization Information – Enabled”),
x == “2100”, strcat(“Undefined UserAccountControl Bit 20 – Enabled”),
x == “2101”, strcat(“Protect Kerberos Service Tickets with AES Keys – Enabled”),
x == “2102”, strcat(“Undefined UserAccountControl Bit 22 – Enabled”),
x == “2103”, strcat(“Undefined UserAccountControl Bit 23 – Enabled”),
x == “2104”, strcat(“Undefined UserAccountControl Bit 24 – Enabled”),
x == “2105”, strcat(“Undefined UserAccountControl Bit 25 – Enabled”),
x == “2106”, strcat(“Undefined UserAccountControl Bit 26 – Enabled”),
x == “2107”, strcat(“Undefined UserAccountControl Bit 27 – Enabled”),
x == “2108”, strcat(“Undefined UserAccountControl Bit 28 – Enabled”),
x == “2109”, strcat(“Undefined UserAccountControl Bit 29 – Enabled”),
x == “2110”, strcat(“Undefined UserAccountControl Bit 30 – Enabled”),
x == “2111”, strcat(“Undefined UserAccountControl Bit 31 – Enabled”),
“Unknown”)
| project
TimeGenerated,
TargetAccount,
Actor=SubjectAccount,
UserAccountControl=x,
[‘User Account Flag Description’]
——————————————————————————————————————
Table = SecurityEvent | Unconstrained Delegation Enabled
//Detects when unconstrained kerberos delegation is enabled on a computer object
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where EventID == “4742”
| parse EventData with * ‘NewUacValue”>’ NewUacValue ” *
| parse EventData with * ‘TargetUserName”>’ ComputerName ” *
| parse EventData with * ‘SubjectUserName”>’ Actor ” *
| where NewUacValue == “0x2080”
| project TimeGenerated, Activity, ComputerName, Actor
——————————————————————————————————————
Table = SecurityEvent | Unconstrained Delegation to User
//Detects when unconstrained kerberos delegation is enabled on a user object
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where EventID == “4738”
| parse EventData with * ‘NewUacValue”>’ NewUacValue ” *
| parse EventData with * ‘TargetUserName”>’ UserName ” *
| parse EventData with * ‘SubjectUserName”>’ Actor ” *
| where NewUacValue == “0x2010”
| project TimeGenerated, Activity, UserName, Actor
——————————————————————————————————————
Table = SecurityEvent | Visualize Accounts Created Disabled Deleted
//Visualize Active Directory accounts created, disabled and deleted per day
//Data connector required for this query – Windows Security Events via AMA or Security Events via Legacy Agent
SecurityEvent
| where TimeGenerated > ago(30d)
| where AccountType == “User”
| project TimeGenerated, Account, EventID, TargetAccount
| where EventID in (“4720”, “4725”, “4726”)
| where TargetAccount !endswith “$”
| summarize
[‘Accounts Created’]=countif(EventID == “4720”),
[‘Accounts Deleted’]=countif(EventID == “4726”),
[‘Accounts Disabled’]=countif(EventID == “4725”)
by startofday(TimeGenerated)
| render columnchart
with (
kind=unstacked,
xtitle=”Day”,
ytitle=”Count”,
title=”Active Directory User Accounts Created, Disabled and Deleted per day”)
——————————————————————————————————————