Copyright (c) 2026 MindMesh Academy. All rights reserved. This content is proprietary and may not be reproduced or distributed without permission.

1.5.2. Core Operators for Security Questions

💡 First Principle: Most security questions reduce to a handful of verbs: find the matching events, shape them into readable columns, count or group them to spot outliers, and connect them to other data. KQL has an operator for each verb, and recognizing which verb a question needs tells you which operator completes the query.

VerbOperatorSecurity example
Findwherewhere ActionType == "LogonFailed"
Find by timeago(), betweenwhere Timestamp > ago(1d)
Shape (keep columns)projectproject Timestamp, DeviceName, AccountName
Shape (add columns)extendextend Hour = bin(Timestamp, 1h)
Count and groupsummarize ... bysummarize Failures = count() by AccountName
Collect valuesmake_set(), dcount()summarize Devices = dcount(DeviceName) by AccountName
Rankorder by / sort by, top, taketop 10 by Failures desc
ConnectjoinCorrelate sign-ins with process events by account
CombineunionSearch several tables at once

String matching deserves special care because it's where exam distractors hide:

OperatorMatchesCaseNotes
==Exact valueSensitive"PowerShell.exe" == "powershell.exe" is false
=~Exact valueInsensitiveSafer for file and account names
hasWhole term within a stringInsensitiveUses the term index, so it's fast
containsAny substringInsensitiveSlower; matches inside words
startswith / endswithPrefix or suffixInsensitiveCommon for paths and domains
in / in~Any value in a listSensitive / insensitivewhere FileName in~ ("psexec.exe", "wmic.exe")

A brute-force detection pattern shows several verbs working together:

SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType != "0"
| summarize FailedAttempts = count(), TargetedAccounts = dcount(UserPrincipalName) by IPAddress
| where TargetedAccounts > 10
| order by FailedAttempts desc

Failed sign-ins (ResultType other than "0") from the last hour are grouped per source IP, and any IP that failed against more than ten distinct accounts surfaces as a likely password spray. Note the second where after summarize: it filters on a column that only exists because summarize created it. That's the pipeline model working as intended.

join deserves one more warning. Its default kind in KQL is innerunique, which removes duplicate keys from the left table before matching. When a question needs every matching pair, specify kind=inner explicitly. You'll build joins across email, identity, and endpoint tables in Phase 4.

⚠️ Exam Trap: has and contains are not interchangeable. has "admin" matches the term admin in "admin login" but not inside "administrator"; contains "admin" matches both. When a hotspot asks for the most efficient operator to match a whole word, choose has; when it must match part of a word, choose contains.

Reflection Question: You need to find every device where a user ran any of five known credential-dumping tools, regardless of how the file name is capitalized. Which operator matches a list of values case-insensitively, and which aggregation would show how many distinct devices each account touched?

See how it connects
Alvin Varughese
Written byAlvin Varughese
Founder•20 professional certifications