Table of Contents

Kusto Cheatsheet for SOC Analyst

This page provides practical Kusto Query Language (KQL) patterns that support SOC triage and investigations in the SOC analyst playbook.

How to Use This Cheatsheet

  • Start with a narrow time window, then expand only when needed
  • Filter early to reduce noise and cost
  • Keep output columns focused for case notes
  • Save high-value queries as reusable templates

Common Query Skeleton

Use this as a starting template for most investigations.

What it does:

  • Defines a base table
  • Applies time and investigation filters early
  • Returns only key fields for review

When to use it:

  • Building a new query from scratch
  • Simplifying noisy queries before tuning
<TableName>
| where TimeGenerated > ago(24h)
| where <important filters>
| project TimeGenerated, <key fields>
| order by TimeGenerated desc

Time Window Patterns

These filters control scope and performance.

What it does:

  • Provides common time constraints for triage and expanded hunts

When to use it:

  • 24h for default triage
  • Fixed datetime range for incident reconstruction
  • 7d for repeated or low-and-slow behavior
// Last 24 hours
| where TimeGenerated > ago(24h)

// Incident-centered window
| where TimeGenerated between (datetime(2026-05-19T00:00:00Z) .. datetime(2026-05-19T23:59:59Z))

// Expand to 7 days when needed
| where TimeGenerated > ago(7d)

Identity and Sign-In Queries

Failed to Success Chains

What it does:

  • Finds user and IP pairs with repeated authentication failures followed by success in the same window
  • Highlights potential password spray, brute force, or credential stuffing

When to use it:

  • Early identity triage
  • Validating suspicious sign-in alerts
SigninLogs
| where TimeGenerated > ago(24h)
| summarize Failed = countif(ResultType != 0), Success = countif(ResultType == 0)
    by UserPrincipalName, IPAddress
| where Failed >= 5 and Success > 0
| order by Failed desc

Suspicious IP Clustering Across Users

What it does:

  • Identifies IP and ASN sources authenticating multiple distinct users
  • Surfaces shared infrastructure that may indicate coordinated abuse

When to use it:

  • Investigating broad sign-in anomalies
  • Checking for spray infrastructure affecting multiple accounts
SigninLogs
| where TimeGenerated > ago(24h)
| summarize DistinctUsers = dcount(UserPrincipalName), Users = make_set(UserPrincipalName, 20)
    by IPAddress, AutonomousSystemNumber
| where DistinctUsers >= 3
| order by DistinctUsers desc

Conditional Access Fail Then Success

What it does:

  • Finds user and IP combinations where Conditional Access failed but access still succeeded
  • Helps reveal policy bypass paths or alternate sign-in routes

When to use it:

  • Investigating potential control evasion
  • Reviewing Conditional Access effectiveness
SigninLogs
| where TimeGenerated > ago(24h)
| summarize FailedCA = countif(ConditionalAccessStatus =~ "failure"),
    SuccessfulSignins = countif(ResultType == 0)
    by UserPrincipalName, IPAddress
| where FailedCA > 0 and SuccessfulSignins > 0
| order by FailedCA desc

Email and Messaging Queries

Inbox and Forwarding Rule Changes

What it does:

  • Detects mailbox and transport rule creation or modification events
  • Surfaces common persistence and exfiltration mechanisms in account takeover

When to use it:

  • Any compromised mailbox investigation
  • Internal phishing or business email compromise triage
CloudAppEvents
| where TimeGenerated > ago(24h)
| where ActionType in ("New-InboxRule", "Set-InboxRule", "New-TransportRule")
| project TimeGenerated, AccountDisplayName, AccountObjectId, ActionType, RawEventData
| order by TimeGenerated desc

Outbound Sender Volume Spike

What it does:

  • Compares last 24-hour sender volume against prior 7-day baseline
  • Flags statistically elevated outbound send patterns

When to use it:

  • Detecting spam bursts from compromised accounts
  • Prioritizing suspicious senders for deeper review
EmailEvents
| where TimeGenerated > ago(8d)
| summarize DailyCount = count() by SenderFromAddress, bin(TimeGenerated, 1d)
| summarize Last24h = maxif(DailyCount, TimeGenerated > ago(24h)),
    PriorAvg = avgif(DailyCount, TimeGenerated between (ago(8d) .. ago(24h)))
    by SenderFromAddress
| where Last24h > PriorAvg * 3 and Last24h > 20
| order by Last24h desc

Internal Phish Candidate Messages

What it does:

  • Filters internal-domain senders with suspicious phishing-like subject terms
  • Returns message IDs for pivoting into detailed email analysis

When to use it:

  • Rapid hunting for internal phishing waves
  • Building an initial message triage queue
EmailEvents
| where TimeGenerated > ago(24h)
| where SenderFromDomain has "yourdomain.edu"
| where Subject has_any ("urgent", "verify", "password", "invoice")
| project TimeGenerated, SenderFromAddress, RecipientEmailAddress, Subject, NetworkMessageId
| order by TimeGenerated desc

Privilege and Audit Queries

Role Assignment Changes

What it does:

  • Finds privileged role membership or activation changes in directory audit logs
  • Highlights potential privilege escalation activity

When to use it:

  • Privileged account investigations
  • Verifying whether elevated access changed near incident time
AuditLogs
| where TimeGenerated > ago(24h)
| where ActivityDisplayName has_any (
    "Add member to role",
    "Add eligible member to role",
    "Activate eligible role"
  )
| project TimeGenerated, ActivityDisplayName, InitiatedBy, TargetResources
| order by TimeGenerated desc

Sensitive Directory Changes

What it does:

  • Detects high-impact identity and policy modifications
  • Helps identify account takeover or persistence-related admin actions

When to use it:

  • Identity incident scope assessment
  • Post-compromise change review
AuditLogs
| where TimeGenerated > ago(24h)
| where ActivityDisplayName has_any (
    "Update user",
    "Reset user password",
    "Update application",
    "Update policy"
  )
| project TimeGenerated, ActivityDisplayName, InitiatedBy, TargetResources
| order by TimeGenerated desc

Investigation Helpers

Build a User Timeline

What it does:

  • Combines sign-in and audit events into a chronological user-centric timeline
  • Enables fast reconstruction of a single identity investigation narrative

When to use it:

  • Case timeline generation
  • Confirming event sequence before containment or closure
union isfuzzy=true SigninLogs, AuditLogs
| where TimeGenerated > ago(24h)
| where tostring(UserPrincipalName) =~ "user@yourdomain.edu"
    or tostring(InitiatedBy) has "user@yourdomain.edu"
| project TimeGenerated,
    SourceTable = $table,
    UserPrincipalName,
    InitiatedBy,
    IPAddress,
    ResultType,
    ActivityDisplayName
| order by TimeGenerated asc

Correlate by IP and Device

What it does:

  • Groups sign-ins by IP and maps associated users and devices
  • Surfaces shared-source activity that may indicate lateral or coordinated use

When to use it:

  • Multi-user anomaly review
  • Prioritizing IP clusters for containment decisions
SigninLogs
| where TimeGenerated > ago(24h)
| summarize Users = make_set(UserPrincipalName, 20),
    Devices = make_set(DeviceDetail.deviceId, 20),
    Count = count()
    by IPAddress
| where array_length(Users) > 1 and Count >= 5
| order by Count desc

Case Note Minimum Fields

For each query result saved to a case, capture:

  • Timestamp in UTC
  • User principal name and object ID
  • Source IP and location
  • Device ID and compliance state
  • Alert ID or message ID
  • Action taken and approver

Tuning Tips

  • Raise thresholds for known high-volume service accounts
  • Exclude approved scanner or integration accounts using allow-lists
  • Separate student and staff baselines when behavior differs materially
  • Validate query assumptions after major identity or mail policy changes