🔏
RootGuard
HomeSOC OperationsIncident ResponseWindows ForensicsLinux ForensicsKQL Investigations
  • Welcome
    • RootGuard
      • Who Am I?
        • Professional Profile
  • Resources Hub
    • Blogs
      • Articles
        • Safeguarding SMEs: The Strategic Importance of a Security Operations Center (SOC)
      • Posts
        • Roadmap to Becoming a Cybersecurity Specialist
        • Starting a Career in Cybersecurity
        • A Guide to Landing Your First Cybersecurity Analyst Role
        • Moving from Intermediate to Expert Incident Responder
  • SOC Operations
    • Introduction
      • Development Resources
        • SOC Analysts Roadmap
        • Becoming A SOC Analyst
        • SOC Analysts Prep Interview Questions
    • Essential Skills
      • Critical Windows EventIDs to Monitor
    • Junior Analyst Skills
      • Splunk Use Cases
      • KQL Use Cases
        • Reconnaissance (TA0043)
        • Initial Access (TA0001)
        • Execution (TA0002)
        • Persistence (TA0003)
        • Privilege Escalation (TA0004)
        • Defence Evasion (TA0005)
        • Credential Access (TA0006)
        • Discovery (TA0007)
        • Lateral Movement (TA0008)
        • Collection (TA0009)
        • Command and Control (TA0011)
        • Exfiltration (TA0010)
        • Impact (TA0040)
      • Investigating Common Attacks
        • Domain Dominance Attacks - Detection & Analysis
        • Investigating a Suspected AD FS Distributed Key Management (DKM) Attack
        • Authentication From Suspicious DeviceName
        • Identifying Interactive or RemoteInteractive Session From Service Account
        • Identifying Split or Part Archive File Transfers
        • Detect Potential Cleartext Credentials in Command Line
        • Detecting Command Line Interpreters Launched via Scheduled Tasks
        • Detecting Files Containing Potentially Sensitive Data
        • Detecting DeviceNetworkEvents From Windows Processes and Domains by TLD
        • Detecting Silent cmd.exe Execution With Redirected STDERR & STDOUT
        • Detecting Low Prevalence DLL Loaded From Process In User Downloads Directory
        • Detecting Virtual Drive Mounted From Archive
        • Identify Execution of Script From User's Downloads Folder
        • Identify Potential RDP Tunneled Sessions
        • Identify Instances of PowerShell Invoke-WebRequest, IWR or Net.WebClient
        • Identify Processes Launched by PowerShell Remoting (WSMProvHost.exe)
        • Detect DeviceNetworkEvents for LOLBAS with Download or Upload Functions
        • Detect Execution of PSEXESVC via Remote Systems
        • Identify Suspicious String in Service Creation ImagePath
        • Identify File with Double Extensions
        • Detect Potential Cleartext Credentials in Commandline
        • Detect When Large Number of Files Downloaded From OneDrive or SharePoint
        • Identify and Investigate Phishing Attacks with KQL
      • PowerShell for SecOps
        • Powershell Remoting
        • Reconnaissance Discovery
        • Initial Access Discovery
        • Execution Discovery
        • Persistence Discovery
        • Privilege Escalation Discovery
        • Defence Evasion Discovery
        • Credential Access Discovery
        • Discovery
        • Lateral Movement Discovery
        • Collection Discovery
        • Command & Control (C2) Discovery
        • Exfiltration Discovery
        • Impact Discovery
      • Packet Analysis (pcap)
        • Tcpdump
        • Tcpdump (Intermediate)
        • Tshark
        • Ngrep
      • Investigating Suspicious Emails Using KQL
    • Intermediate and Advanced Skills
      • Investigate Using MITRE ATT&CK Methodology
        • Reconnaissance (TA0043) Techniques
        • Resource Development (TA0042) Techniques
        • Initial Access (TA0001) Techniques
        • Command Execution (TA0002) Techniques
        • Persistence (TA0003) Techniques
        • Privilege Escalation (TA0004) Techniques
        • Defence Evasion (TA0005) Techniques
        • Credential Access (TA0006) Techniques
        • Discovery (TA0007) Techniques
        • Lateral Movement (TA0008) Techniques
        • Collection (TA0009) Techniques
        • Command and Control (C2) (TA0011) Techniques
        • Exfiltration (TA0010) Techniques
        • Impact (TA0040) Techniques
    • Vulnerability Management
    • Malware Analysis
  • DFIR
    • Incident Response
      • Incident Triage
        • Triage Types and Processes
        • PowerShell for Detection and Analysis
          • Malware or Compromise Investigation
          • Lateral Movement Discovery
        • Registry Analysis
        • Sysinternals Intrusion Analysis
        • PowerShell Intrusion Analysis
        • Velociraptor Intrusion Analysis
        • Zimmerman Tools Intrusion Analysis
      • KAPE Artifacts Analysis
      • Velociraptor Artifacts Analysis
      • Using The Unified Kill Chain Model to Analyse Individual Cyber Attacks
        • Phase 1 - Gaining an Initial Foothold
          • Gaining Access to the Network
          • Establishing a Foothold
          • Network Discovery
      • Response Strategies
        • Privilege Escalation Assessment
        • Command and Control Assessment
        • Command Execution Assessment
        • Defence Evasion Assessment
        • Detection Assessment
        • Discovery Assessment
        • Exfiltration Assessment
        • Initial Access Assessment
        • Initial Impact Assessment Techniques
        • Lateral Movement Assessment
        • Persistence Assessment
    • Windows Forensics
      • Evidence of Execution
      • Window Artifact Analysis
        • Account Usage
        • User Activity Tracking (Event Logs)
        • Program Execution
        • File and Folder Opening
        • File Download
        • Browser Usage
        • Deleted File or File Knowledge
        • External Device & USB Usage
    • Linux Forensics
      • Linux Commandline Basics
      • Host Compromise Assessment
    • KQL for Defender & Sentinel
      • MDO (Office)
      • MDI (Identity)
      • MDE (Endpoint)
    • Memory Forensics
      • Memory Forensics (Volatility 3)
    • Playbooks
      • First Responder DFIR Playbook
        • Device Isolation
        • Evidence Collection
          • Acquire Triage Image Using KAPE
          • Acquire Triage Data Using Velociraptor
          • Acquire Triage Data Using Powershell
          • Acquire Triage Memory Image
          • Acquire Image Using FTK
          • AXIOM Cyber Data Collection
        • Windows Forensic Artefacts
          • Application Execution
          • File & Folder Knowledge
          • External Device Usage
          • Network Activity
          • Windows Event Logs
        • Initial Analysis
          • Memory Analysis (Vol 3)
          • Axiom Cyber Examiner
  • Detection Engineering
    • AD Attack Detections & Mitigations
      • Kerberoasting
      • Authentication Server Response (AS-REP) Roasting
      • Password Spraying
      • MachineAccountQuota Compromise
      • Unconstrained Delegation
      • Password in Group Policy Preferences (GPP) Compromise
      • Active Directory Certificate Services (AD CS) Compromise
      • Golden Certificate
      • DCSync
      • Dumping ntds.dit
      • Golden Ticket
      • Silver Ticket
      • Golden Security Assertion Markup Language (SAML)
      • Microsoft Entra Connect Compromise
      • One-way Domain Trust Bypass
      • Security Identifier (SID) History Compromise
      • Skeleton Key
      • Active Directory Security Controls
      • Active Directory Events for Detecting Compromise
    • Attack Triage Playbooks (KQL Triage)
      • Windows Malware Detection Playbook
      • Linux Host Intrusion Detection Playbook (CLI)
      • Linux Intrusion Detection Playbook
      • Large-Scale Compromise Detection Playbook
      • Ransomware Detection Playbook
      • Phishing Email Compromise Detection Playbook
      • Scam Detection Playbook
      • Customer Phishing Detection Playbook
      • Insider Abuse Detection Playbook
      • Information Leakage Detection Playbook
      • Social Engineering Detection Playbook
      • Malicious Network Behaviour Detection Playbook
      • Windows Intrusion Detection Playbook
      • Vulnerability Detection Playbook
      • Business Email Compromise Detection Playbook
    • Process Execution (KQL Triage)
    • Threat Hunting
      • Hunting Ransomware Indicators
      • Hunting With KQL
        • Detecting Malware Infection (MITRE ATT&CK: T1566, T1059)
        • Discovery Activities (MITRE ATT&CK: T1016, T1083, T1046)
        • Credential Theft (MITRE ATT&CK: T1003, T1078)
        • Lateral Movement (MITRE ATT&CK: T1076, T1021)
        • Data Theft (MITRE ATT&CK: T1041, T1071)
        • Detecting CommandLine Executions (MITRE ATT&CK: T1059)
        • Windows Security Logs (Identity and Logon Activities)
      • Hunting With Splunk
Powered by GitBook
On this page
  • Introduction
  • Devices Accessed By Compromised Device
  • Identify All Suspicious Activities From The Compromised Accounts
  • Identify Failed Login Attempts From Users
  • Lateral Movement By Compromised Accounts
  • User Added To Sensitive Group
  • Anomalous Group Policy Discovery
  • SMB File Copy
  • Identify Suspicious SMB Activity
  • Reference
Edit on GitHub
  1. DFIR
  2. KQL for Defender & Sentinel

MDI (Identity)

Introduction

Microsoft Defender for Identity is a cloud-based security solution designed to protect on-premises and hybrid Active Directory environments from identity-based threats. It leverages behavioural analytics, machine learning, and advanced threat intelligence to detect suspicious activities, such as credential theft, lateral movement, and domain dominance. By analysing data collected from domain controllers, it identifies anomalies in user and entity behaviour, providing security teams with real-time alerts about potential compromises. Defender for Identity also helps organisations proactively secure their environments by identifying vulnerabilities, such as misconfigured accounts or exposed credentials.

Integrated with Microsoft's extended detection and response (XDR) suite, including tools like Microsoft 365 Defender and Azure Sentinel, Defender for Identity provides a unified view of identity-related security risks. It offers features such as lateral movement path analysis, which visualises potential attack routes, and advanced threat investigation tools to aid in incident response. This integration ensures comprehensive visibility and faster remediation across the enterprise. By focusing on protecting the backbone of IT infrastructure—identities—Microsoft Defender for Identity plays a crucial role in enhancing an organisation's overall security posture in a rapidly evolving threat landscape.

The following is a set of KQL queries that can be used to detect and analyse malicious or suspicious activities in your environment. The queries are designed to quickly grab the necessary information that will allow the investigator to determine whether the activity warrants deeper analysis or escalation.

Note: On some occasions, hopefully, at a minimum, the investigator will have to customise the queries for the environment where they are being used. Queries will only work if the data is available.

Devices Accessed By Compromised Device

Use Case: Query helpful for identifying lateral movement and suspicious activities stemming from the compromised device. It enables SOC analysts to correlate activity and prioritise mitigation steps effectively.

Defender

// Define the compromised device and search window
let CompromisedDevice = "PC01.exampledomain.com";
let SearchWindow = 48h; // Customizable: h = hours, d = days

// Query to investigate devices accessed by the compromised device
IdentityLogonEvents
| where TimeGenerated >= ago(SearchWindow) // Use Sentinel's default time field
| where DeviceName == CompromisedDevice // Filter for the compromised device
| extend 
    FormattedTimestamp = format_datetime(TimeGenerated, 'yyyy-MM-dd HH:mm:ss'), // Human-readable timestamp
    AccessDetails = strcat(ActionType, " via ", Protocol) // Combine action type and protocol for detailed context
| summarize
    TotalAccessedDevices = dcount(DestinationDeviceName), // Count unique destination devices accessed
    AccessedDevices = make_set(DestinationDeviceName), // List of destination devices accessed
    AccountsUsed = make_set(AccountName), // List of accounts used in the access
    AccountDomains = make_set(AccountDomain), // List of account domains
    ActionsPerformed = make_set(ActionType), // List of unique action types
    ProtocolsUsed = make_set(Protocol), // List of unique protocols
    IPAddressesInvolved = make_set(IPAddress), // List of unique IP addresses involved
    TargetDevices = make_set(TargetDeviceName), // List of target devices
    AccessEventCount = count() // Total number of access events
    by bin(TimeGenerated, 1h), DeviceName // Group by time bins and device
| project 
    FormattedTimestamp, // Include formatted timestamp
    DeviceName, // Compromised device
    TotalAccessedDevices, // Number of unique devices accessed
    AccessedDevices, // List of accessed devices
    AccountsUsed, // List of accounts used
    AccountDomains, // List of account domains
    ActionsPerformed, // List of actions performed
    ProtocolsUsed, // List of protocols used
    IPAddressesInvolved, // List of IP addresses
    TargetDevices, // List of target devices
    AccessEventCount // Count of access events
| order by FormattedTimestamp desc // Sort by the most recent events
// Define the compromised device and search window
let CompromisedDevice = "PC01.exampledomain.com";
let SearchWindow = 48h; // Customizable: h = hours, d = days
// Query to investigate devices accessed by the compromised device
IdentityLogonEvents
| where TimeGenerated >= ago(SearchWindow) // Use Sentinel's default time field
| where DeviceName == CompromisedDevice // Filter for the compromised device
| extend 
    FormattedTimestamp = format_datetime(TimeGenerated, 'yyyy-MM-dd HH:mm:ss'), // Human-readable timestamp
    AccessDetails = strcat(ActionType, " via ", Protocol) // Combine ActionType and Protocol for detailed context
| summarize
    TotalAccessedDevices = dcount(DestinationDeviceName), // Count unique destination devices accessed
    AccessedDevices = make_set(DestinationDeviceName), // List of destination devices accessed
    AccountsUsed = make_set(AccountName), // List of accounts used in the access
    AccountDomains = make_set(AccountDomain), // List of account domains
    ActionsPerformed = make_set(ActionType), // List of unique action types
    ProtocolsUsed = make_set(Protocol), // List of unique protocols
    IPAddressesInvolved = make_set(IPAddress), // List of unique IP addresses involved
    TargetDevices = make_set(TargetDeviceName), // List of target devices
    AccessEventCount = count() // Total number of access events
    by bin(TimeGenerated, 1h), DeviceName // Group by hourly time bins and device
| project 
    TimeGenerated, // Include formatted timestamp
    DeviceName, // Compromised device
    TotalAccessedDevices, // Number of unique devices accessed
    AccessedDevices, // List of accessed devices
    AccountsUsed, // List of accounts used
    AccountDomains, // List of account domains
    ActionsPerformed, // List of actions performed
    ProtocolsUsed, // List of protocols used
    IPAddressesInvolved, // List of IP addresses
    TargetDevices, // List of target devices
    AccessEventCount // Count of access events
| order by TimeGenerated desc // Sort by the most recent events

Identify All Suspicious Activities From The Compromised Accounts

Use Case: This query is useful for investigating potential lateral movement, unauthorised access, or malicious actions originating from compromised accounts. It provides actionable insights to guide further analysis and remediation.

// Define the compromised accounts and search window
let CompromisedAccounts = dynamic(["user1", "user2"]); // Add compromised account list
let SearchWindow = 48h; // Customizable: h = hours, d = days
// Query to investigate suspicious activities from compromised accounts
IdentityLogonEvents
| where TimeGenerated >= ago(SearchWindow) // Filter based on the search window
| where AccountName in (CompromisedAccounts) // Focus on compromised accounts
| extend 
    FormattedTimestamp = format_datetime(TimeGenerated, 'yyyy-MM-dd HH:mm:ss'), // Human-readable timestamp
    ActivityDetails = strcat(ActionType, " via ", Protocol, " on ", DeviceName) // Combine action details
| summarize
    TotalDevicesAccessed = dcount(DestinationDeviceName), // Count unique destination devices accessed
    AccessedDevices = make_set(DestinationDeviceName), // List of destination devices accessed
    ActionTypesPerformed = make_set(ActionType), // List of unique action types
    ProtocolsUsed = make_set(Protocol), // List of protocols used
    IPAddressesInvolved = make_set(IPAddress), // List of unique IP addresses
    TargetDevices = make_set(TargetDeviceName), // List of target devices
    TotalActivities = count() // Total number of suspicious activities
    by bin(TimeGenerated, 1h), AccountName // Group by hourly bins and account
| project 
    TimeGenerated, // Include formatted timestamp
    AccountName, // Compromised account
    TotalDevicesAccessed, // Number of devices accessed
    AccessedDevices, // List of accessed devices
    ActionTypesPerformed, // List of actions performed
    ProtocolsUsed, // List of protocols used
    IPAddressesInvolved, // List of IP addresses
    TargetDevices, // List of target devices
    TotalActivities // Count of total activities
| order by TimeGenerated desc // Sort by the most recent events

Description: Use the SecurityEvent table to Identify all suspicious activities from the compromised accounts

// Define the list of accounts to monitor
let MonitoredAccounts = dynamic(["User1", "User2", "User3"]); // Add the list of accounts
let ExcludedEventIDs = dynamic([8002, 4634]); // Add excluded Event IDs
// Query to filter security events
SecurityEvent
| where Account in (MonitoredAccounts) // Use dynamic list for account matching
| where EventID !in (ExcludedEventIDs) // Exclude unwanted Event IDs
| project 
    TimeGenerated, // Include timestamp
    Account, // Include account name
    Computer, // Include computer name
    EventID, // Include event ID
    Activity, // Include activity description
    CommandLine, // Include command-line details
    FileHash, // Include file hash
    FilePath, // Include file path
    Process, // Include process information
    WorkstationName, // Include workstation name
    EventData // Include additional event data
| order by TimeGenerated desc, Account asc // Sort by most recent events and account name

Identify Failed Login Attempts From Users

// Define search parameters
let SearchWindow = 30d; // Customizable time window
let TargetAccount = "UserName"; // Replace with the compromised username
// Query to analyze failed login attempts for a specific user
SecurityEvent
| where TimeGenerated >= ago(SearchWindow) // Filter events based on the search window
| where EventID == 4625 // Focus on failed login attempts
| where AccountType == "User" // Include only user accounts
| where Account contains TargetAccount // Filter for the specific username
| project 
    TimeGenerated, // Event timestamp
    Account, // Account name
    Computer, // Computer name
    FailureReason, // Reason for login failure
    IpAddress, // IP address of the source
    LogonProcessName, // Logon process used
    LogonTypeName, // Logon type description
    ProcessName // Name of the process involved
| order by TimeGenerated desc // Sort by most recent events

Use Case: This query is ideal for monitoring failed login attempts in cloud environments where Azure AD is the authentication provider. It provides detailed insights into failed attempts, aiding in detecting brute force attacks or identifying suspicious login activity. Let me know if you need further adjustments! Failed login attempts for one or multiple user accounts from the SigninLogs table

// Define search parameters
let SearchWindow = 48h; // Customizable time window
let TargetAccounts = dynamic(["user1@exampledomain.com", "user2@exampledomain.com"]); // Replace with one or more user accounts
// Query to identify failed login attempts for specified accounts
SigninLogs
| where TimeGenerated >= ago(SearchWindow) // Filter based on the search window
| where UserPrincipalName in (TargetAccounts) // Filter for specific user accounts
| where ResultType != "0" // Filter for failed sign-ins (ResultType "0" indicates success)
| extend 
    FailureReason = tostring(Status.errorCode), // Extract failure reason
    AppName = tostring(AppDisplayName), // Extract application name
    IPAddress = tostring(IPAddress), // Extract source IP address
    Device = tostring(DeviceDetail.operatingSystem), // Extract device OS
    Browser = tostring(DeviceDetail.browser), // Extract browser details
    Location = tostring(LocationDetails.state), // Extract location details (state-level)
    RiskLevel = tostring(RiskDetail), // Extract user risk level
    ConditionalAccessStatus = tostring(ConditionalAccessStatus), // Extract conditional access status
    MFARequired = tostring(AuthenticationRequirement), // Check if MFA was required
    CorrelationId = tostring(CorrelationId) // Extract correlation ID for tracing related events
| project 
    TimeGenerated, // Event timestamp
    UserPrincipalName, // User attempting the login
    FailureReason, // Reason for failure
    IPAddress, // IP address of the source
    Location, // Location of the sign-in
    Device, // Device OS
    Browser, // Browser details
    AppName, // Application being accessed
    RiskLevel, // User risk level
    ConditionalAccessStatus, // Conditional access policies status
    MFARequired, // Was MFA required
    CorrelationId, // Correlation ID
    ResultDescription // Description of the result
| summarize 
    FailedAttempts = count(), // Count of failed attempts
    SourceIPAddresses = make_set(IPAddress), // Unique IP addresses
    Devices = make_set(Device), // Unique devices
    Browsers = make_set(Browser), // Unique browsers
    FailureReasons = make_set(FailureReason), // Unique failure reasons
    RiskLevels = make_set(RiskLevel), // Unique risk levels
    ConditionalAccessOutcomes = make_set(ConditionalAccessStatus), // Conditional access outcomes
    MFAStatuses = make_set(MFARequired) // MFA statuses
    by UserPrincipalName, bin(TimeGenerated, 1h) // Group by user and hourly bins
| order by TimeGenerated desc // Sort by the most recent events

A query using the IdentityLogonEvents table to identify failed login attempts with additional insights for investigation:

Use Case: This query is useful for identifying failed login attempts, understanding their context (e.g., IPs, devices, failure reasons), and detecting anomalies like brute force attacks or misconfigurations. It provides detailed and actionable information for investigation and remediation.

// Define search parameters
let SearchWindow = 48d; // Set the time window for the query to the last 48 days
let TargetAccounts = dynamic(["user1", "user2"]); // Specify the list of accounts to monitor for failed logins
// Query to identify failed login attempts for specified accounts
IdentityLogonEvents
| where Timestamp >= ago(SearchWindow) // Filter events within the defined time window
| where AccountName in (TargetAccounts) // Filter events for specific target accounts
| where ActionType == "LogonFailed" // Focus only on failed logon attempts
| extend 
    FailureReason = tostring(FailureReason), // Extract the reason for the logon failure
    LogonType = tostring(LogonType), // Extract the type of logon (e.g., interactive, remote)
    IPAddress = tostring(IPAddress), // Extract the source IP address of the logon attempt
    Computer = tostring(DeviceName), // Extract the computer or device name where the event occurred
    SourceSystem = tostring(SourceSystem), // Extract the source system of the event (e.g., Windows, Azure AD)
    Location = tostring(Location), // Extract the geographic location of the login attempt
    DeviceName = tostring(DeviceName), // Extract the name of the device involved in the login attempt
    DestinationDeviceName = tostring(DestinationDeviceName), // Extract the destination device name (if applicable)
    DestinationIPAddress = tostring(DestinationIPAddress), // Extract the destination IP address (if applicable)
    TargetDeviceName = tostring(TargetDeviceName), // Extract the name of the target device
    Protocol = tostring(Protocol) // Extract the protocol used during the logon attempt
| project 
    Timestamp, // Include the timestamp of the event
    AccountName, // Include the user account that attempted the login
    DeviceName, // Include the name of the device involved in the login attempt
    DestinationDeviceName, // Include the name of the destination device
    DestinationIPAddress, // Include the IP address of the destination device
    TargetDeviceName, // Include the name of the target device
    Protocol, // Include the protocol used during the logon attempt
    FailureReason, // Include the reason for the logon failure
    IPAddress, // Include the source IP address of the login attempt
    Computer, // Include the name of the computer or device where the event occurred
    LogonType, // Include the type of logon (e.g., interactive, remote)
    SourceSystem, // Include the source system of the event
    Location, // Include the geographic location of the login attempt
    AdditionalFields // Include any additional event details
| summarize 
    FailedAttempts = count(), // Count the total number of failed login attempts
    DeviceNames = make_set(DeviceName), // Aggregate unique device names involved in the events
    DestinationDeviceNames = make_set(DestinationDeviceName), // Aggregate unique destination device names
    DestinationIPAddresses = make_set(DestinationIPAddress), // Aggregate unique destination IP addresses
    TargetDeviceNames = make_set(TargetDeviceName), // Aggregate unique target device names
    Protocols = make_set(Protocol), // Aggregate unique protocols used during the logon attempts
    SourceIPAddresses = make_set(IPAddress), // Aggregate unique source IP addresses
    Computers = make_set(Computer), // Aggregate unique computers or devices where events occurred
    FailureReasons = make_set(FailureReason), // Aggregate unique reasons for the logon failures
    LogonTypes = make_set(LogonType), // Aggregate unique logon types
    Locations = make_set(Location) // Aggregate unique geographic locations
    by AccountName, bin(Timestamp, 1h) // Group results by account and hourly time bins
| order by Timestamp desc // Sort the results by the most recent events

Lateral Movement By Compromised Accounts

Use Case: This query is tailored for detecting lateral movement by compromised accounts in your environment. By monitoring logon activity across devices, it helps identify patterns that could indicate attempts to expand access within the network.

// Define search parameters
let SearchWindow = 48h; // Set the time window for the query
let CompromisedAccounts = dynamic(["user1", "user2"]); // Replace with known or suspected compromised accounts
// Query to detect lateral movement by compromised accounts
IdentityLogonEvents
| where Timestamp >= ago(SearchWindow) // Filter events within the defined time window
| where AccountName in (CompromisedAccounts) // Filter for specific compromised accounts
| where ActionType in ("LogonSuccess", "LogonFailed") // Focus on logon events (both success and failure)
| extend 
    SourceIPAddress = tostring(IPAddress), // Extract the source IP address
    TargetDevice = tostring(DeviceName), // Extract the name of the target device
    DestinationDeviceName = tostring(DestinationDeviceName), // Extract destination device name
    DestinationIPAddress = tostring(DestinationIPAddress), // Extract destination IP address
    LogonType = tostring(LogonType), // Extract the logon type (e.g., interactive, remote)
    FailureReason = iff(ActionType == "LogonFailed", tostring(FailureReason), "N/A"), // Failure reason for failed logons
    Protocol = tostring(Protocol), // Extract the protocol used during the logon
    SourceSystem = tostring(SourceSystem) // Extract the source system (e.g., Windows, Azure AD)
| project 
    Timestamp, // Event timestamp
    AccountName, // Account attempting the logon
    SourceIPAddress, // Source IP address of the logon
    TargetDevice, // Name of the target device
    DestinationDeviceName, // Destination device name
    DestinationIPAddress, // Destination IP address
    LogonType, // Logon type (e.g., remote, interactive)
    Protocol, // Protocol used during the logon attempt
    SourceSystem, // Source system of the logon event
    FailureReason // Reason for logon failure (if applicable)
| summarize 
    TotalLogonAttempts = count(), // Total number of logon attempts
    TargetDevices = make_set(TargetDevice), // List of unique target devices
    DestinationDevices = make_set(DestinationDeviceName), // List of unique destination devices
    SourceIPAddresses = make_set(SourceIPAddress), // List of unique source IP addresses
    DestinationIPAddresses = make_set(DestinationIPAddress), // List of unique destination IP addresses
    LogonTypes = make_set(LogonType), // List of unique logon types
    Protocols = make_set(Protocol), // List of unique protocols used
    FailureReasons = make_set(FailureReason) // List of failure reasons (for failed attempts)
    by AccountName, bin(Timestamp, 1h) // Group by account and hourly time bins
| order by Timestamp desc // Sort by the most recent events

User Added To Sensitive Group

Use Case: This query provides a detailed audit of group membership changes involving sensitive groups, including the initiator of the change and the added user. It is particularly useful for identifying unauthorized or suspicious changes in group memberships. Let me know if further refinements are needed!

// Define sensitive groups to monitor
let SensitiveGroups = dynamic(['Domain Admins', 'Enterprise Admins', 'Exchange Admins']); // Add sensitive groups to this list
// Query to identify membership changes in sensitive groups
IdentityDirectoryEvents
| where Timestamp >= ago(30d) // Filter events within the last 30 days
| where ActionType == "Group Membership changed" // Focus on group membership change actions
| extend ParsedFields = parse_json(AdditionalFields) // Parse AdditionalFields once for efficiency
| extend 
    Group = tostring(ParsedFields['TO.GROUP']), // Extract the target group
    AddedUser = tostring(ParsedFields['TO.ACCOUNT']), // Extract the user added to the group
    InitiatorAccount = tostring(ParsedFields['ACTOR.ACCOUNT']) // Extract the account that initiated the change
| where isnotempty(Group) and Group in (SensitiveGroups) // Ensure the group is not empty and matches sensitive groups
| project 
    Timestamp, // Include the timestamp of the event
    Group, // The sensitive group whose membership was changed
    AddedUser, // The user added to the sensitive group
    InitiatorAccount, // Account used to initiate the group addition
    ActionType, // Action type for context
    AdditionalFields // Include all additional fields for further context if needed
| order by Timestamp desc // Sort results by the most recent changes

Anomalous Group Policy Discovery

Use Case: This query is ideal for detecting: Unauthorised enumeration of Group Policies. Suspicious activity from new or unexpected devices, accounts, or IP addresses. Potential reconnaissance or pre-attack activity.

// Define thresholds for anomaly detection
let HighFrequencyThreshold = 10; // Define a threshold for high-frequency queries
let LookbackPeriod = 7d; // Period to analyze regular activity
let RecentPeriod = 1d; // Recent period for detecting anomalies
// Identify normal activity for Group Policy discovery in the lookback period
let NormalActivity = materialize(
    IdentityQueryEvents
    | where TimeGenerated >= ago(LookbackPeriod) // Analyze activity over the lookback period
    | where QueryType == "AllGroupPolicies" // Focus on Group Policy discovery
    | summarize 
        Devices = make_set(DeviceName), // Collect devices performing regular queries
        Accounts = make_set(AccountName), // Collect accounts performing regular queries
        IPAddresses = make_set(IPAddress) // Collect IPs performing regular queries
);
// Detect recent anomalous Group Policy discovery
IdentityQueryEvents
| where TimeGenerated >= ago(RecentPeriod) // Focus on recent activity
| where QueryType == "AllGroupPolicies" // Focus on Group Policy discovery
| summarize 
    QueryCount = count(), // Count the number of queries
    Devices = make_set(DeviceName), // List unique devices
    Accounts = make_set(AccountName), // List unique accounts
    IPAddresses = make_set(IPAddress) // List unique IPs
    by bin(TimeGenerated, 1h), DeviceName, AccountName, IPAddress // Group by time and source details
| extend IsHighFrequency = QueryCount > HighFrequencyThreshold // Detect high-frequency querying
| project 
    TimeGenerated, // Event time
    DeviceName, // Device performing the query
    AccountName, // Account performing the query
    IPAddress, // IP performing the query
    QueryCount, // Number of queries
    IsHighFrequency // Flag for high-frequency queries
| order by TimeGenerated desc // Sort by the most recent events

SMB File Copy

Use Case: This query detects SMB file copy events that are initiated by suspect accounts. It helps identify unauthorised file transfers, providing relevant details for further investigation.

// Define suspect accounts
let CompromisedAccounts = dynamic(['account1', 'account2']); // Add suspect accounts here
// Query to detect unauthorized SMB file copy events
IdentityDirectoryEvents
| where ActionType == "SMB file copy" // Filter only SMB file copy events
| where AccountName in (CompromisedAccounts) // include events initiated by suspect accounts
| extend 
    SMBFileCopyCount = toint(parse_json(AdditionalFields).Count), // Extract and convert SMB file copy count
    FilePath = tostring(parse_json(AdditionalFields).FilePath), // Extract file path
    FileName = tostring(parse_json(AdditionalFields).FileName) // Extract file name
| project 
    Timestamp, // Event timestamp
    ActionType, // Action type for context
    SourceDeviceName = DeviceName, // Rename DeviceName to SourceDeviceName
    DestinationDeviceName, // Destination device name
    FilePath, // File path of the copied file
    FileName, // File name of the copied file
    SMBFileCopyCount, // Number of files copied
    AccountName // Accout name 
| order by Timestamp desc // Sort results by the most recent events

Identify Suspicious SMB Activity

// Query to detect shared folder access with specific permissions
SecurityEvent
| where TimeGenerated >= ago(61d) // Filter for events in the last day
| where EventID == 5140 // Focus on "A network share object was accessed" events
| where AccessMask in ("0x120089", "0x13019f", "0x1301bf", "0x1401bf", "0x1411ff") // Filter specific access levels
| summarize EventCount = count() // Count occurrences of each unique combination
    by Computer, AccountName, ShareName, ShareLocalPath
| order by EventCount desc // Sort by the highest number of events

Reference

PreviousMDO (Office)NextMDE (Endpoint)

Last updated 4 months ago

Bert Jan P -

Michalis Michalos -

Matt Zorich -

Alex Verboon -

Microsoft -

https://kqlquery.com/
https://www.michalos.net/
https://learnsentinel.blog/
https://github.com/alexverboon
/
https://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries
Microsoft Learn Blog