Pick a table from the sidebar (or scroll) — every query is schema-validated against the canonical column list and follows the BluRaven house style: time-bound first (Timestamp), machine-account excluded, case-insensitive equality, indexed token matching.
25 tables curated96 queries25 schema tablesClick Copy on any query to paste into Defender Advanced Hunting.
DeviceNetworkEvents
| where Timestamp > ago(1h)
| where DeviceName =~ "<DeviceName>"
| where ActionType == "ConnectionSuccess"
| project Timestamp, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
Public-IP egress only
Strip out internal-LAN noise — focuses on internet-bound traffic.
DeviceNetworkEvents
| where Timestamp > ago(24h)
| where ActionType == "ConnectionSuccess"
| where RemoteIPType == "Public"
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessAccountName
| order by Timestamp desc
LOLBin reaching the internet
LOLBin + public destination = high signal. LOLBin alone is noise.
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ (
"certutil.exe","bitsadmin.exe","mshta.exe","regsvr32.exe","rundll32.exe",
"msbuild.exe","installutil.exe","wmic.exe","wscript.exe","cscript.exe",
"cmstp.exe","forfiles.exe","ftp.exe","tftp.exe","odbcconf.exe")
| where RemoteIPType == "Public"
| project Timestamp, DeviceName, InitiatingProcessFileName,
InitiatingProcessCommandLine, RemoteIP, RemoteUrl, RemotePort
| order by Timestamp desc
Beaconing detector — periodic outbound
Sustained, periodic connections to one destination from one process — C2 signature.
DeviceNetworkEvents
| where Timestamp > ago(2h)
| where RemoteIPType == "Public"
| summarize ConnCount = count(),
DistinctMinutes = dcount(bin(Timestamp, 1m))
by DeviceId, DeviceName, RemoteIP, RemotePort, InitiatingProcessFileName
| where ConnCount > 50 and DistinctMinutes > 30 // sustained, not bursty
| order by ConnCount desc
Connections by IP block-list
Bring your own IOC list.
let BadIPs = dynamic(["1.2.3.4","5.6.7.8"]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType in ("ConnectionSuccess","ConnectionAttempt")
| where RemoteIP in (BadIPs)
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
Rare destination (org-wide first-seen)
Domain seen for the first time across the org in the last hour.
let Baseline = DeviceNetworkEvents
| where Timestamp between (ago(30d) .. ago(1h))
| summarize BaselineHosts = dcount(DeviceName) by RemoteUrl
| where BaselineHosts > 2;
DeviceNetworkEvents
| where Timestamp > ago(1h)
| where isnotempty(RemoteUrl)
| join kind=leftanti Baseline on RemoteUrl
| summarize FirstSeen = min(Timestamp), HostsCount = dcount(DeviceName)
by RemoteUrl
| order by FirstSeen desc
Top destinations by data volume
If your tenant tracks byte counts. Useful for exfil triage.
DeviceNetworkEvents
| where Timestamp > ago(24h)
| where RemoteIPType == "Public"
| summarize TotalConnections = count(),
UniqueDevices = dcount(DeviceName)
by RemoteUrl, RemoteIP
| order by TotalConnections desc
| take 50
Most user-mode malware drops to temp/AppData; this is the first-look query.
DeviceFileEvents
| where Timestamp > ago(7d)
| where InitiatingProcessAccountName !endswith "$"
| where ActionType == "FileCreated"
| where FolderPath has_any (@"\AppData\Local\Temp\", @"\AppData\Roaming\",
@"\Windows\Temp\", @"\Users\Public\")
| where FileName endswith ".exe" or FileName endswith ".dll"
or FileName endswith ".ps1" or FileName endswith ".bat"
| project Timestamp, DeviceName, FolderPath, FileName, SHA256,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
Hash IOC match across files
Sweep file telemetry for known-bad hashes.
let BadHashes = dynamic(["<sha256-1>","<sha256-2>"]);
DeviceFileEvents
| where Timestamp > ago(7d)
| where SHA256 in~ (BadHashes) or SHA1 in~ (BadHashes) or MD5 in~ (BadHashes)
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256,
InitiatingProcessFileName, InitiatingProcessAccountName
| order by Timestamp desc
Mass file rename (ransomware)
200+ unique-file renames in a 1-min window from one process — encryption-stage signal.
DeviceFileEvents
| where Timestamp > ago(1d)
| where InitiatingProcessAccountName !endswith "$"
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName)
by DeviceName, InitiatingProcessAccountName,
InitiatingProcessFileName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
Browser cookie / login DB access by non-browser
Infostealer (RedLine, Lumma, Vidar) reading Login Data / cookies.
DeviceFileEvents
| where Timestamp > ago(7d)
| where InitiatingProcessAccountName !endswith "$"
| where FolderPath has_any (@"\Google\Chrome\User Data\",
@"\Microsoft\Edge\User Data\",
@"\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe",
"brave.exe","opera.exe")
| project Timestamp, DeviceName, InitiatingProcessAccountName,
InitiatingProcessFileName, FolderPath, FileName, ActionType
| order by Timestamp desc
Crypto-wallet keystore access by non-wallet process
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonFailed"
| where AccountName !endswith "$"
| summarize FailCount = count(),
DistinctTargets = dcount(DeviceName),
FirstAttempt = min(Timestamp),
LastAttempt = max(Timestamp)
by AccountName, RemoteIP, FailureReason
| where FailCount > 5
| order by FailCount desc
Successful RDP from public IP
RemoteInteractive (LogonType 10) from a public source — high-priority review.
DeviceLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where LogonType == "RemoteInteractive"
| where RemoteIPType == "Public"
| where AccountName !endswith "$"
| project Timestamp, DeviceName, AccountName, RemoteIP, RemoteDeviceName,
IsLocalAdmin, Protocol
| order by Timestamp desc
NTLM where Kerberos is expected
NTLM-over-RDP or NTLM-from-domain-joined-host = pivot signal for cred theft.
DeviceLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where Protocol == "NTLM"
| where LogonType in ("RemoteInteractive","Network")
| where AccountName !endswith "$"
| project Timestamp, DeviceName, AccountName, RemoteIP, LogonType,
IsLocalAdmin, RemoteDeviceName
| order by Timestamp desc
Local-admin interactive logons
Privilege use — every hit deserves review on production hosts.
DeviceLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where IsLocalAdmin == true
| where LogonType in ("Interactive","RemoteInteractive")
| where AccountName !endswith "$"
| project Timestamp, DeviceName, AccountName, LogonType, RemoteIP, Protocol
| order by Timestamp desc
After-hours interactive logon
Tune the hour bounds for your business. Default: 21:00–06:00 local.
DeviceLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where LogonType == "Interactive"
| where AccountName !endswith "$"
| extend Hour = datetime_part("hour", Timestamp)
| where Hour >= 21 or Hour < 6
| project Timestamp, Hour, DeviceName, AccountName, RemoteIP, IsLocalAdmin
| order by Timestamp desc
Logon-velocity anomaly per account
Account logging on to >5 distinct hosts in 1h — lateral-movement candidate.
DeviceLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where AccountName !endswith "$"
| summarize HostsCount = dcount(DeviceName)
by AccountName, bin(Timestamp, 1h)
| where HostsCount > 5
| order by HostsCount desc
AMSI providers scan in-memory script payloads; non-clean ScanResult is high-signal.
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "AmsiScanResult"
| extend Result = tostring(parse_json(AdditionalFields).ScanResult),
Content = tostring(parse_json(AdditionalFields).ContentName)
| where Result != "Clean"
| project Timestamp, DeviceName, InitiatingProcessFileName, Content, Result
| order by Timestamp desc
DNS queries to suspect TLDs
TLD-based heuristic.
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "DnsQueryResponse"
| extend Q = tostring(parse_json(AdditionalFields).QueryName)
| where Q endswith ".onion" or Q endswith ".ru" or Q endswith ".su" or Q endswith ".cn"
| project Timestamp, DeviceName, Q, InitiatingProcessFileName
| order by Timestamp desc
LSASS process access (credential dumping)
Anything opening LSASS that isn't a Microsoft signed component is suspect.
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
USB drive insertion / new external storage
Data-loss-prevention hunt.
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType in ("UsbDriveMount","UsbDriveDriveLetterChanged")
| project Timestamp, DeviceName, InitiatingProcessAccountName,
AdditionalFields
| order by Timestamp desc
PowerShell ScriptBlock log — suspicious content
Defender records ScriptBlock content for risky payloads.
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "PowerShellCommand"
| extend Cmd = tostring(parse_json(AdditionalFields).Command)
| where Cmd matches regex @"(?i)downloadstring|frombase64string|invoke-expression|new-object\s+net\.webclient"
| project Timestamp, DeviceName, InitiatingProcessAccountName,
InitiatingProcessCommandLine, Cmd
| order by Timestamp desc
DeviceInfo
| where Timestamp > ago(1d)
| summarize Devices = dcount(DeviceName) by OSPlatform, OSVersion
| order by Devices desc
Internet-facing devices
Pivot for prioritising vulnerability triage.
DeviceInfo
| where Timestamp > ago(1d)
| where IsInternetFacing == true
| project DeviceName, OSPlatform, OSVersion, PublicIP, MachineGroup
Find a device by partial name
Replace `<substring>`.
DeviceInfo
| where Timestamp > ago(1d)
| where DeviceName has "<substring>"
| project DeviceName, OSPlatform, OSVersion, MachineGroup, JoinType, IsAzureADJoined
Build dynamic device set for filtering
Use this snippet to scope another query to (e.g.) only Win10 hosts.
let win10 = DeviceInfo
| where Timestamp > ago(1d)
| where OSPlatform == "Windows10"
| summarize make_set(DeviceName);
DeviceProcessEvents
| where DeviceName in (win10)
| where FileName =~ "<binary>"
Useful for tracking dynamic-IP movement of a target host.
DeviceNetworkInfo
| where Timestamp > ago(7d)
| mv-expand IP = parse_json(IPAddresses)
| project Timestamp, DeviceName, NetworkAdapterName, IP = tostring(IP.IPAddress)
| order by Timestamp desc
VPN-tunnel adapters
Adapters indicating tunnel software in play.
DeviceNetworkInfo
| where Timestamp > ago(1d)
| where TunnelType !in ("None","")
| project DeviceName, NetworkAdapterName, TunnelType, NetworkAdapterStatus
Replace `<user@domain>` to triage one user's mailbox traffic.
EmailEvents
| where Timestamp > ago(24h)
| where EmailDirection == "Inbound"
| where RecipientEmailAddress =~ "<user@domain>"
| project Timestamp, SenderFromAddress, Subject, DeliveryAction,
DeliveryLocation, NetworkMessageId
| order by Timestamp desc
Phish blocked vs delivered for a sender
Sender-level triage. Replace `<sender@domain>`.
EmailEvents
| where Timestamp > ago(7d)
| where SenderFromAddress =~ "<sender@domain>"
| summarize Count = count() by DeliveryAction, EmailDirection
| order by Count desc
Senders with high recipient fan-out
Mass-mail / spam-spike pattern.
EmailEvents
| where Timestamp > ago(24h)
| where EmailDirection == "Inbound"
| where DeliveryAction == "Delivered"
| summarize Recipients = dcount(RecipientEmailAddress)
by SenderFromAddress, SenderFromDomain
| where Recipients > 25 // threshold tuneable
| order by Recipients desc
Authentication-fail mail (SPF/DKIM/DMARC)
Spoofed envelope vs header. Inspect the AuthenticationDetails JSON.
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| where AuthenticationDetails has_any ("SPF=fail","DKIM=fail","DMARC=fail")
| project Timestamp, SenderMailFromAddress, SenderFromAddress,
RecipientEmailAddress, Subject, AuthenticationDetails
| order by Timestamp desc
Phish + URL + click — full chain
Joins the three phishing tables for end-to-end correlation.
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound" and DeliveryAction == "Delivered"
| join kind=inner (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId
| join kind=inner (
UrlClickEvents
| where ActionType in ("ClickAllowed","ClickedThrough")
| project NetworkMessageId, ClickTime = Timestamp, AccountUpn
) on NetworkMessageId
| project Timestamp, ClickTime, SenderFromAddress, RecipientEmailAddress,
Subject, Url, UrlDomain, AccountUpn
Defender retroactively yanked a delivered message.
EmailPostDeliveryEvents
| where Timestamp > ago(7d)
| where ActionType == "ZAP"
| project Timestamp, NetworkMessageId, Action, ActionTrigger,
ActionResult, DeliveryLocation, RecipientEmailAddress
| order by Timestamp desc
User-initiated message deletes
Sometimes a user reports phish themselves; this surfaces those signals.
EmailPostDeliveryEvents
| where Timestamp > ago(7d)
| where ActionTrigger == "User"
| where Action in ("Delete","MoveToJunk")
| project Timestamp, NetworkMessageId, Action, RecipientEmailAddress
| order by Timestamp desc
Inherently rare and high-fidelity — Safe Links warned and the user proceeded.
UrlClickEvents
| where Timestamp > ago(30d)
| where ActionType == "ClickedThrough"
| project Timestamp, AccountUpn, IPAddress, Url, Workload, NetworkMessageId
| order by Timestamp desc
Clicks on a domain pattern
Hunt clicks on a brand-impersonation pattern.
UrlClickEvents
| where Timestamp > ago(30d)
| where Url has "<suspect-domain-pattern>"
| project Timestamp, AccountUpn, IPAddress, Url, ActionType, Workload, NetworkMessageId
| order by Timestamp desc
Clicks on URLs not from email (Teams / Office)
Clicks delivered via Teams chat or Office docs, not just mail.
UrlClickEvents
| where Timestamp > ago(7d)
| where Workload != "Email"
| project Timestamp, AccountUpn, IPAddress, Url, Workload, ActionType
| order by Timestamp desc
Repeated failures from one source = spray candidate.
IdentityLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonFailed"
| where Protocol == "Kerberos"
| where AccountName !endswith "$"
| summarize FailCount = count(),
Targets = dcount(AccountUpn)
by IPAddress, FailureReason
| where FailCount > 10
| order by FailCount desc
NTLM where Kerberos is expected
NTLM RemoteInteractive on a domain-joined estate is unusual and worth review.
IdentityLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where Protocol == "NTLM" and LogonType == "RemoteInteractive"
| where AccountName !endswith "$"
| project Timestamp, AccountUpn, DeviceName, IPAddress, LogonType, Protocol
| order by Timestamp desc
Service-account interactive logons
Service accounts shouldn't sit at a console. Tune the prefix list.
IdentityLogonEvents
| where Timestamp > ago(7d)
| where ActionType == "LogonSuccess"
| where LogonType in ("Interactive","RemoteInteractive")
| where AccountName startswith "svc-" or AccountName startswith "sa-"
| project Timestamp, AccountUpn, DeviceName, IPAddress, LogonType
| order by Timestamp desc
IdentityQueryEvents
| where Timestamp > ago(7d)
| where ActionType == "LDAPQuery"
| where Query has "(samAccountType=805306368)" // user objects
| summarize Count = count() by AccountName, IPAddress, DeviceName
| where Count > 1
| order by Count desc
Kerberoasting recon — SPN enumeration
Querying for accounts with servicePrincipalName.
IdentityQueryEvents
| where Timestamp > ago(7d)
| where ActionType == "LDAPQuery"
| where Query has "servicePrincipalName"
| project Timestamp, AccountName, DeviceName, IPAddress, Query
| order by Timestamp desc
LDAP query volume per source
Outlier-volume detection.
IdentityQueryEvents
| where Timestamp > ago(24h)
| where ActionType == "LDAPQuery"
| summarize Queries = count() by AccountName, DeviceName, IPAddress, bin(Timestamp, 1h)
| where Queries > 200
| order by Queries desc
AlertInfo
| where Timestamp > ago(24h)
| summarize FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Count = count()
by Title, Category, Severity
| order by Count desc
High / critical alerts only
Severity-scoped triage feed.
AlertInfo
| where Timestamp > ago(7d)
| where Severity in ("High","Medium")
| project Timestamp, AlertId, Title, Category, Severity, ServiceSource, DetectionSource
| order by Timestamp desc
Top MITRE techniques observed
Unique-technique frequency.
AlertInfo
| where Timestamp > ago(7d)
| mv-expand todynamic(AttackTechniques)
| extend Technique = tostring(AttackTechniques)
| summarize Count = count() by Technique
| order by Count desc
AlertEvidence
| where Timestamp > ago(7d)
| where DeviceName =~ "<DeviceName>"
| join kind=inner AlertInfo on AlertId
| project Timestamp, Title, Severity, EntityType, EvidenceRole,
FileName, RemoteUrl, AccountName
| order by Timestamp desc
All file IOCs across active alerts
Pivot for sweep against `DeviceFileEvents`.
AlertEvidence
| where Timestamp > ago(7d)
| where EntityType == "File" and isnotempty(SHA256)
| summarize Alerts = dcount(AlertId), FirstSeen = min(Timestamp)
by FileName, SHA256
| order by Alerts desc
DeviceTvmSoftwareVulnerabilities
| where Timestamp > ago(1d)
| where CveId =~ "<CVE-id>"
| project DeviceName, OSPlatform, SoftwareName, SoftwareVersion,
VulnerabilitySeverityLevel, RecommendedSecurityUpdate
Critical CVEs unpatched
Severity-driven priority list.
DeviceTvmSoftwareVulnerabilities
| where Timestamp > ago(1d)
| where VulnerabilitySeverityLevel == "Critical"
| summarize DeviceCount = dcount(DeviceName) by CveId, SoftwareName
| order by DeviceCount desc
DeviceTvmSoftwareVulnerabilitiesKB
| where PublishedDate > ago(30d)
| where VulnerabilitySeverityLevel == "Critical"
| project CveId, CvssScore, IsExploitAvailable, PublishedDate
| order by PublishedDate desc
CVEs with public exploit + active in env
Join KB with per-device coverage.
let KnownExploited = DeviceTvmSoftwareVulnerabilitiesKB
| where IsExploitAvailable == true
| project CveId;
DeviceTvmSoftwareVulnerabilities
| where Timestamp > ago(1d)
| where CveId in (KnownExploited)
| summarize DeviceCount = dcount(DeviceName), AffectedSoftware = make_set(SoftwareName)
by CveId, VulnerabilitySeverityLevel
| order by DeviceCount desc
Microsoft Sentinel · KQL Search
Sentinel speaks the same KQL but on a different schema — TimeGenerated instead of Timestamp, SecurityEvent/SigninLogs/OfficeActivity/ASIM Im* instead of the Defender Device* tables. Every query below is schema-validated against the canonical Sentinel column list.
16 tables curated56 queries33 schema tablesClick Copy on any query to paste into Log Analytics / Sentinel.
Replace `<user@domain>`. Useful for triaging an account-takeover ticket.
SigninLogs
| where TimeGenerated > ago(24h)
| where UserPrincipalName =~ "<user@domain>"
| where ResultType != 0
| project TimeGenerated, IPAddress, AppDisplayName, ResultType,
ResultDescription, RiskLevelDuringSignIn,
ConditionalAccessStatus, ClientAppUsed
| order by TimeGenerated desc
Impossible travel — same user, two countries within 60 minutes
Successful sign-ins from geographically impossible locations — pivot for compromised credentials.
let WindowMinutes = 60;
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| extend Country = tostring(parse_json(LocationDetails).countryOrRegion)
| where isnotempty(Country)
| project TimeGenerated, UserPrincipalName, Country, IPAddress, AppDisplayName
| order by UserPrincipalName asc, TimeGenerated asc
| extend Prev = prev(Country),
PrevTime = prev(TimeGenerated),
PrevAcct = prev(UserPrincipalName)
| where UserPrincipalName == PrevAcct
and Country != Prev
and datetime_diff('minute', TimeGenerated, PrevTime) <= WindowMinutes
| project UserPrincipalName, From = Prev, FromTime = PrevTime,
To = Country, ToTime = TimeGenerated,
MinutesDelta = datetime_diff('minute', TimeGenerated, PrevTime),
IPAddress, AppDisplayName
| order by ToTime desc
Legacy authentication still in use
`Other clients` = POP/IMAP/SMTP basic-auth. Should be zero in modern tenants.
SigninLogs
| where TimeGenerated > ago(7d)
| where ClientAppUsed == "Other clients"
| where ResultType == 0
| summarize Sessions = count() by UserPrincipalName, IPAddress, AppDisplayName
| order by Sessions desc
Risky sign-ins (Identity Protection)
Medium / high risk surfaced by AAD risk scoring.
SigninLogs
| where TimeGenerated > ago(7d)
| where RiskLevelDuringSignIn in ("medium","high")
| where ResultType == 0
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName,
RiskLevelDuringSignIn, RiskState, RiskEventTypes_V2
| order by TimeGenerated desc
Conditional-Access blocked sign-ins
CA Failures — useful for spotting attacker workflows that hit your gates.
SigninLogs
| where TimeGenerated > ago(7d)
| where ConditionalAccessStatus == "Failure"
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName,
ResultDescription, ConditionalAccessPolicies
| order by TimeGenerated desc
MFA challenge failures (push-bombing detector)
Repeated MFA challenges to the same user from the same source.
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 50158 // MFA Challenge required / failed
| summarize Attempts = count(), FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by UserPrincipalName, IPAddress, bin(TimeGenerated, 5m)
| where Attempts > 3
| order by Attempts desc
First sign-in to a new SaaS app
App with zero history that suddenly gets a successful sign-in — illicit-consent risk.
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| where AppDisplayName !in~ ("Microsoft Authenticator","Office 365","Microsoft Office",
"Microsoft Teams","Microsoft Edge","Outlook")
| summarize FirstSeen = min(TimeGenerated), Users = dcount(UserPrincipalName),
UserList = make_set(UserPrincipalName, 20)
by AppDisplayName, AppId
| where FirstSeen > ago(2h)
| order by FirstSeen desc
Sign-ins from anonymous proxy IPs
Tor / VPN / abuse-listed sources.
SigninLogs
| where TimeGenerated > ago(7d)
| where RiskEventTypes_V2 has_any ("anonymizedIPAddress","tor")
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName,
ResultType, RiskLevelDuringSignIn
| order by TimeGenerated desc
Adds to Global Admin / Privileged Role Admin / App Admin.
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName has "Add member to role"
| extend RoleName = tostring(parse_json(tostring(TargetResources))[0].displayName),
Member = tostring(parse_json(tostring(TargetResources))[1].displayName),
Initiator = tostring(parse_json(tostring(InitiatedBy)).user.userPrincipalName)
| where RoleName has_any ("Global Administrator","Privileged Role Administrator",
"Application Administrator","User Access Administrator",
"Cloud Application Administrator")
| project TimeGenerated, Initiator, Member, RoleName, Result
New OAuth app consent grants
User consenting to a third-party app — illicit-consent attack precursor.
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName has "Consent to application"
| where Result =~ "success"
| extend AppName = tostring(parse_json(tostring(TargetResources))[0].displayName),
User = tostring(parse_json(tostring(InitiatedBy)).user.userPrincipalName)
| project TimeGenerated, OperationName, User, AppName, Result, AdditionalDetails
| order by TimeGenerated desc
Password resets (forced or self-service)
Common pre-impersonation step.
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName in~ ("Reset password (by admin)","Reset user password",
"Self-service password reset")
| extend Initiator = tostring(parse_json(tostring(InitiatedBy)).user.userPrincipalName),
Target = tostring(parse_json(tostring(TargetResources))[0].userPrincipalName)
| project TimeGenerated, OperationName, Initiator, Target, Result
New service principal / application created
Pivot for OAuth-app-based persistence.
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName in~ ("Add service principal","Add application")
| extend AppName = tostring(parse_json(tostring(TargetResources))[0].displayName),
User = tostring(parse_json(tostring(InitiatedBy)).user.userPrincipalName)
| project TimeGenerated, OperationName, User, AppName, Result
OfficeActivity
| where TimeGenerated > ago(7d)
| where OfficeWorkload == "Exchange"
| where Operation in~ ("New-InboxRule","Set-InboxRule")
| where ResultStatus == "Succeeded"
| project TimeGenerated, UserId, ClientIP, Operation, Parameters
| order by TimeGenerated desc
Mass file download from SharePoint / OneDrive
Exfil via file-share. Tune the > 100 threshold to estate baseline.
OfficeActivity
| where TimeGenerated > ago(7d)
| where OfficeWorkload in ("SharePoint","OneDrive")
| where Operation =~ "FileDownloaded"
| summarize Files = dcount(SourceFileName), Sites = make_set(Site_Url, 10)
by UserId, bin(TimeGenerated, 5m)
| where Files > 100
| order by Files desc
External-domain user accessing internal SharePoint
External-collab risk.
OfficeActivity
| where TimeGenerated > ago(7d)
| where OfficeWorkload == "SharePoint"
| where Operation in~ ("FileAccessed","FileDownloaded","ListItemViewed")
| where UserId !endswith "@yourdomain.com"
| project TimeGenerated, UserId, Operation, Site_Url, SourceFileName, ClientIP
AzureActivity
| where TimeGenerated > ago(7d)
| where OperationNameValue =~ "Microsoft.Authorization/roleAssignments/write"
| where ActivityStatusValue == "Succeeded"
| project TimeGenerated, Caller, CallerIpAddress, ResourceId,
OperationNameValue, Properties
Key Vault secret reads
Frequent reads from a single caller can indicate credential-harvesting.
AzureActivity
| where TimeGenerated > ago(7d)
| where OperationNameValue =~ "Microsoft.KeyVault/vaults/secrets/read"
| where ActivityStatusValue == "Succeeded"
| summarize Reads = count(), Vaults = dcount(ResourceId),
Secrets = dcount(tostring(parse_json(tostring(Properties)).resource))
by Caller, CallerIpAddress
| order by Reads desc
Storage account public-access changes
Storage-container public-blob exposure.
AzureActivity
| where TimeGenerated > ago(7d)
| where OperationNameValue has "Microsoft.Storage/storageAccounts"
| where OperationNameValue has "write"
| project TimeGenerated, Caller, CallerIpAddress, ResourceId,
OperationNameValue, ActivityStatusValue, Properties
| order by TimeGenerated desc
Syslog
| where TimeGenerated > ago(24h)
| where Facility =~ "authpriv"
| where SyslogMessage has "sudo:" and SyslogMessage has "FAILED"
| project TimeGenerated, Computer, HostIP, SyslogMessage
| order by TimeGenerated desc
SSH key-based logon
Tracks publickey auth — useful for pivoting on lateral movement.
Syslog
| where TimeGenerated > ago(7d)
| where ProcessName =~ "sshd"
| where SyslogMessage has "Accepted publickey"
| extend SrcIp = extract(@"from\s+(\S+)", 1, SyslogMessage),
User = extract(@"for\s+(\S+)\s+from", 1, SyslogMessage)
| project TimeGenerated, Computer, User, SrcIp, SyslogMessage
Audit-policy / sudoers modifications
Tampering with audit infrastructure.
Syslog
| where TimeGenerated > ago(7d)
| where Facility =~ "authpriv" or Facility =~ "auth"
| where SyslogMessage has_any ("/etc/sudoers","/etc/audit","auditctl",
"auditd","sudoers.d")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
DnsEvents
| where TimeGenerated > ago(7d)
| where Name endswith ".onion" or Name endswith ".duckdns.org"
| project TimeGenerated, Computer, ClientIP, Name, QueryTypeName, ResultCodeName
DNS-tunnel candidate (TXT-heavy)
Frequent TXT lookups from one source — DNS-tunneling exfil.
DnsEvents
| where TimeGenerated > ago(24h)
| where QueryTypeName == "TXT"
| summarize Queries = count(), DistinctNames = dcount(Name)
by ClientIP, bin(TimeGenerated, 5m)
| where Queries > 200
| order by Queries desc
CommonSecurityLog
| where TimeGenerated > ago(24h)
| where Activity has "deny" or DeviceAction has "deny"
| where ipv4_is_private(SourceIP) == false
| summarize Hits = count() by DeviceVendor, DeviceProduct,
SourceIP, DestinationIP, DestinationPort, DeviceAction
| order by Hits desc
Web-proxy threat-categorised hits
Vendor-specific category mapping — adjust to your tenant's connector.
CommonSecurityLog
| where TimeGenerated > ago(24h)
| where DeviceVendor =~ "Zscaler"
| where DeviceCustomString1 has_any ("malware","phishing","botnet","spyware")
| project TimeGenerated, SourceIP, SourceUserName,
DestinationHostName, RequestURL, DeviceCustomString1
Pulls all active TI indicators and sweeps against CommonSecurityLog destinations.
let Active = ThreatIntelligenceIndicator
| where TimeGenerated > ago(30d)
| where Active == true and ExpirationDateTime > now()
| summarize arg_max(TimeGenerated, *) by IndicatorId
| where isnotempty(NetworkIP);
CommonSecurityLog
| where TimeGenerated > ago(7d)
| join kind=inner (Active | project NetworkIP, ThreatType, ConfidenceScore)
on $left.DestinationIP == $right.NetworkIP
| project TimeGenerated, SourceIP, SourceUserName, DestinationIP,
ThreatType, ConfidenceScore, Activity
File-hash IOC sweep
TI indicator hashes vs. SecurityEvent / ImFileEvent file telemetry.
let BadHashes = ThreatIntelligenceIndicator
| where TimeGenerated > ago(30d)
| where Active == true
| where isnotempty(FileHashValue)
| distinct FileHashValue;
ImFileEvent
| where TimeGenerated > ago(7d)
| where TargetFileSHA256 in (BadHashes) or TargetFileSHA1 in (BadHashes)
or TargetFileMD5 in (BadHashes)
| project TimeGenerated, DvcHostname, ActorUsername, TargetFileName,
TargetFilePath, TargetFileSHA256
ImFileEvent
| where TimeGenerated > ago(7d)
| where ActorUsername !endswith "$"
| where EventType == "FileCreated"
| where TargetFilePath has_any (@"\AppData\Local\Temp\",
@"\AppData\Roaming\",
@"\Windows\Temp\")
| where TargetFileName endswith ".exe" or TargetFileName endswith ".dll"
or TargetFileName endswith ".ps1"
| project TimeGenerated, DvcHostname, ActorUsername, TargetFilePath,
TargetFileName, TargetFileSHA256
Cross-vendor mass file rename (ransomware)
Threshold-based encryption signal across MDE / Sysmon / AuditD.
ImFileEvent
| where TimeGenerated > ago(1d)
| where ActorUsername !endswith "$"
| where EventType in ("FileRenamed","FileModified")
| summarize files = dcount(TargetFileName)
by DvcHostname, ActorUsername, ActingProcessName, bin(TimeGenerated, 1m)
| where files > 200
| order by files desc
Pick a log source from the sidebar — every query is verbatim Datadog Logs Explorer / Cloud SIEM syntax: source: first, @field.path:value filters, uppercase boolean operators, CIDR(@ip, range) for IP filtering. Time windows are configured at rule level in Datadog so they're absent from the query body.
Reference Tables let you maintain large IOC / known-bad / allowlist tables in Datadog and pivot via them in queries — handy for IP-block / hash-list / domain-list filters that grow over time. Syntax: CIDR(@network.client.ip, *RefTable:bad_ips_v2*) or @process.name IN *RefTable:lolbins*. Build / upload tables under Logs → Configuration → Reference Tables; queries below use literal lists for clarity but swap in a Reference Table any time a list grows past ~100 entries.
12 sources curated65 queriesClick Copy on any query to paste into Datadog Logs / Cloud SIEM rule editor.
source:kubernetes.audit @verb:create @objectRef.resource:pods
(@requestObject.spec.containers.securityContext.privileged:true
OR @requestObject.spec.hostPID:true
OR @requestObject.spec.hostNetwork:true)
kubectl exec sessions
Live shells into running pods — operationally normal for engineers, anomalous in prod namespaces.
New bindings of high-privilege cluster roles — full-takeover risk.
source:kubernetes.audit
@verb:create
@objectRef.resource:(clusterrolebindings OR rolebindings)
@requestObject.roleRef.name:(cluster-admin OR admin OR edit)
Sigma is a generic, YAML-based signature format for SIEM detections. Each rule below is a self-contained Sigma YAML, pre-compiled to Defender KQL, Splunk SPL, and Elastic Lucene at build time — copy whichever format your SIEM speaks. Source rules live under sigma_rules/; re-run python build_soc_cheatsheet.py after editing to refresh.
Sigma syntax cheat sheet — click to expand
Top-level fields:
title: # Short, descriptive
id: # UUID — gives the rule a stable identity
status: # stable | test | experimental | deprecated | unsupported
description: # 1-3 sentences on what it catches
references: # URLs to relevant intel / vendor advisories
author: # Free-form
date: # 2026/05/07
tags: # attack.t1059, attack.execution, etc — MITRE ATT&CK
logsource: # Where the rule applies
product: # windows / linux / macos / aws / azure / etc
service: # security / sysmon / cloudtrail / ...
category: # process_creation / file_event / network_connection / ...
detection: # The actual matching logic
selection: # Match condition — ANDed by default within a block
filter: # Optional — fields to exclude
condition: # selection and not filter / 1 of selection_* / etc.
falsepositives: [...]
level: # informational | low | medium | high | critical
Field modifiers (suffix after the field name):
field|contains: value — substring match
field|startswith: value / |endswith: value
field|re: regex — Sigma regex (PCRE-flavoured)
field|all: [a, b] — list AND (must contain all)
field|cased — case-sensitive (default is case-insensitive)
field|windash — accept either - or / as the prefix to a Windows-style flag (e.g. -encodedcommand or /encodedcommand)
field|expand — expand placeholders defined under placeholders:
Compilation cheat sheet — what each Sigma construct becomes:
field|contains: x → KQL field has "x" · SPL field="*x*" · Lucene field:*x*
field|startswith: x → KQL field startswith "x" · SPL field="x*" · Lucene field:x*
field: [a, b, c] → KQL field in ("a","b","c") · SPL field IN ("a","b","c")
condition: 1 of selection_* → OR across every block whose name matches
condition: all of selection_* → AND across every matching block
timeframe: 5m + aggregation — only some backends support; Splunk does, Defender doesn't
When Sigma is the right tool: single-event-shape detections (one log line matches → fire). When it isn't: multi-stage temporal correlation, threshold-over-window aggregations, joins across data sources — those don't compile cleanly to all backends. Author those directly in KQL or SPL instead.
Reference: SigmaHQ/sigma — the canonical rule repo + the latest spec.
15 rules3 backends pre-compiled (KQL · SPL · Lucene)Add new rules to sigma_rules/<kill_chain>/ and rebuild.
actions
5 rules · platform-neutral, compiled at build time
Infostealer - Non-Browser Process Accessing Browser Credential Stores
high
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies
SQLite databases from Chrome/Edge/Firefox profile directories. Any
non-browser process touching these files is a high-fidelity signal.
title: Infostealer - Non-Browser Process Accessing Browser Credential Stores
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d01
status: experimental
description: |
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies
SQLite databases from Chrome/Edge/Firefox profile directories. Any
non-browser process touching these files is a high-fidelity signal.
references:
- https://attack.mitre.org/techniques/T1539/
- https://attack.mitre.org/techniques/T1555/003/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1539
- attack.t1555.003
- attack.credential-access
logsource:
category: file_access
product: windows
detection:
selection_path:
TargetFilename|contains:
- '\Google\Chrome\User Data\'
- '\Microsoft\Edge\User Data\'
- '\Mozilla\Firefox\Profiles\'
selection_file:
TargetFilename|endswith:
- '\Login Data'
- '\Cookies'
- '\logins.json'
- '\cookies.sqlite'
filter_browser:
Image|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\firefox.exe'
- '\brave.exe'
- '\opera.exe'
condition: selection_path and selection_file and not filter_browser
falsepositives:
- Backup utilities reading user profile files
- Browser sync agents from non-stock vendors
level: high
(TargetFilename contains "\\Google\\Chrome\\User Data\\" or TargetFilename contains "\\Microsoft\\Edge\\User Data\\" or TargetFilename contains "\\Mozilla\\Firefox\\Profiles\\") and (TargetFilename endswith "\\Login Data" or TargetFilename endswith "\\Cookies" or TargetFilename endswith "\\logins.json" or TargetFilename endswith "\\cookies.sqlite") and (not((Image endswith "\\chrome.exe" or Image endswith "\\msedge.exe" or Image endswith "\\firefox.exe" or Image endswith "\\brave.exe" or Image endswith "\\opera.exe")))
TargetFilename IN ("*\\Google\\Chrome\\User Data\\*", "*\\Microsoft\\Edge\\User Data\\*", "*\\Mozilla\\Firefox\\Profiles\\*") TargetFilename IN ("*\\Login Data", "*\\Cookies", "*\\logins.json", "*\\cookies.sqlite") NOT (Image IN ("*\\chrome.exe", "*\\msedge.exe", "*\\firefox.exe", "*\\brave.exe", "*\\opera.exe"))
(TargetFilename:(*\\Google\\Chrome\\User\ Data\\* OR *\\Microsoft\\Edge\\User\ Data\\* OR *\\Mozilla\\Firefox\\Profiles\\*)) AND (TargetFilename:(*\\Login\ Data OR *\\Cookies OR *\\logins.json OR *\\cookies.sqlite)) AND (NOT (Image:(*\\chrome.exe OR *\\msedge.exe OR *\\firefox.exe OR *\\brave.exe OR *\\opera.exe)))
Crypto-Wallet Keystore Access by Non-Wallet Process
high
Stealers / wallet drainers read keystore files from MetaMask,
Exodus, Atomic, Phantom, Bitcoin, Electrum, Ethereum-cli wallets.
Filtering out the legitimate wallet binaries leaves only attacker
activity.
title: Crypto-Wallet Keystore Access by Non-Wallet Process
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d02
status: experimental
description: |
Stealers / wallet drainers read keystore files from MetaMask,
Exodus, Atomic, Phantom, Bitcoin, Electrum, Ethereum-cli wallets.
Filtering out the legitimate wallet binaries leaves only attacker
activity.
references:
- https://attack.mitre.org/techniques/T1005/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1005
- attack.collection
logsource:
category: file_access
product: windows
detection:
selection_path:
TargetFilename|contains:
- '\Ethereum\keystore\'
- '\Bitcoin\'
- '\Exodus\'
- '\Electrum\wallets\'
- '\MetaMask\'
- '\Phantom\'
- '\Atomic\Local Storage\'
filter_legit:
Image|endswith:
- '\MetaMask.exe'
- '\Exodus.exe'
- '\Atomic.exe'
- '\electrum.exe'
- '\Bitcoin.exe'
- '\Phantom.exe'
condition: selection_path and not filter_legit
falsepositives:
- Backup software
level: high
(TargetFilename contains "\\Ethereum\\keystore\\" or TargetFilename contains "\\Bitcoin\\" or TargetFilename contains "\\Exodus\\" or TargetFilename contains "\\Electrum\\wallets\\" or TargetFilename contains "\\MetaMask\\" or TargetFilename contains "\\Phantom\\" or TargetFilename contains "\\Atomic\\Local Storage\\") and (not((Image endswith "\\MetaMask.exe" or Image endswith "\\Exodus.exe" or Image endswith "\\Atomic.exe" or Image endswith "\\electrum.exe" or Image endswith "\\Bitcoin.exe" or Image endswith "\\Phantom.exe")))
TargetFilename IN ("*\\Ethereum\\keystore\\*", "*\\Bitcoin\\*", "*\\Exodus\\*", "*\\Electrum\\wallets\\*", "*\\MetaMask\\*", "*\\Phantom\\*", "*\\Atomic\\Local Storage\\*") NOT (Image IN ("*\\MetaMask.exe", "*\\Exodus.exe", "*\\Atomic.exe", "*\\electrum.exe", "*\\Bitcoin.exe", "*\\Phantom.exe"))
(TargetFilename:(*\\Ethereum\\keystore\\* OR *\\Bitcoin\\* OR *\\Exodus\\* OR *\\Electrum\\wallets\\* OR *\\MetaMask\\* OR *\\Phantom\\* OR *\\Atomic\\Local\ Storage\\*)) AND (NOT (Image:(*\\MetaMask.exe OR *\\Exodus.exe OR *\\Atomic.exe OR *\\electrum.exe OR *\\Bitcoin.exe OR *\\Phantom.exe)))
PsExec / SMB Lateral Movement Tooling Execution
high
PsExec, paexec, smbexec, csexec, remcom — admin-grade tooling that
pivots over SMB. Pair with `wmic /node:` for the full cluster.
title: PsExec / SMB Lateral Movement Tooling Execution
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d03
status: experimental
description: |
PsExec, paexec, smbexec, csexec, remcom — admin-grade tooling that
pivots over SMB. Pair with `wmic /node:` for the full cluster.
references:
- https://attack.mitre.org/techniques/T1021/002/
- https://attack.mitre.org/techniques/T1569/002/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1021.002
- attack.t1569.002
- attack.lateral-movement
- attack.execution
logsource:
category: process_creation
product: windows
detection:
selection_psexec:
Image|endswith:
- '\psexec.exe'
- '\psexec64.exe'
- '\psexesvc.exe'
- '\paexec.exe'
- '\csexec.exe'
- '\remcom.exe'
- '\smbexec.py'
selection_wmic_remote:
Image|endswith:
- '\wmic.exe'
CommandLine|contains:
- '/node:'
condition: selection_psexec or selection_wmic_remote
falsepositives:
- Sysadmins running approved remote-management tooling — wrap with
a known-admin-host watchlist
level: high
(Image endswith "\\psexec.exe" or Image endswith "\\psexec64.exe" or Image endswith "\\psexesvc.exe" or Image endswith "\\paexec.exe" or Image endswith "\\csexec.exe" or Image endswith "\\remcom.exe" or Image endswith "\\smbexec.py") or (Image endswith "\\wmic.exe" and CommandLine contains "/node:")
Image IN ("*\\psexec.exe", "*\\psexec64.exe", "*\\psexesvc.exe", "*\\paexec.exe", "*\\csexec.exe", "*\\remcom.exe", "*\\smbexec.py") OR (Image="*\\wmic.exe" CommandLine="*/node:*")
(Image:(*\\psexec.exe OR *\\psexec64.exe OR *\\psexesvc.exe OR *\\paexec.exe OR *\\csexec.exe OR *\\remcom.exe OR *\\smbexec.py)) OR (Image:*\\wmic.exe AND CommandLine:*\/node\:*)
LSASS Process Access Outside Microsoft-Signed Tooling
high
Anything opening lsass.exe with read-memory access that isn't a
Microsoft signed component (Defender, csrss, etc.) is suspect.
Mimikatz / Pypykatz / nanodump signature.
title: LSASS Process Access Outside Microsoft-Signed Tooling
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d04
status: experimental
description: |
Anything opening lsass.exe with read-memory access that isn't a
Microsoft signed component (Defender, csrss, etc.) is suspect.
Mimikatz / Pypykatz / nanodump signature.
references:
- https://attack.mitre.org/techniques/T1003/001/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1003.001
- attack.credential-access
logsource:
category: process_access
product: windows
detection:
selection:
TargetImage|endswith: '\lsass.exe'
filter_known_good:
SourceImage|endswith:
- '\MsSense.exe'
- '\MsMpEng.exe'
- '\csrss.exe'
- '\svchost.exe'
- '\wininit.exe'
- '\services.exe'
- '\lsm.exe'
- '\SearchProtocolHost.exe'
condition: selection and not filter_known_good
falsepositives:
- Third-party EDR / forensic tools — extend the filter list
level: high
TargetImage endswith "\\lsass.exe" and (not((SourceImage endswith "\\MsSense.exe" or SourceImage endswith "\\MsMpEng.exe" or SourceImage endswith "\\csrss.exe" or SourceImage endswith "\\svchost.exe" or SourceImage endswith "\\wininit.exe" or SourceImage endswith "\\services.exe" or SourceImage endswith "\\lsm.exe" or SourceImage endswith "\\SearchProtocolHost.exe")))
TargetImage="*\\lsass.exe" NOT (SourceImage IN ("*\\MsSense.exe", "*\\MsMpEng.exe", "*\\csrss.exe", "*\\svchost.exe", "*\\wininit.exe", "*\\services.exe", "*\\lsm.exe", "*\\SearchProtocolHost.exe"))
TargetImage:*\\lsass.exe AND (NOT (SourceImage:(*\\MsSense.exe OR *\\MsMpEng.exe OR *\\csrss.exe OR *\\svchost.exe OR *\\wininit.exe OR *\\services.exe OR *\\lsm.exe OR *\\SearchProtocolHost.exe)))
Entra ID Consent to Third-Party OAuth Application
medium
User-driven consent to a third-party OAuth application — illicit-
consent attack precursor. Pair with first-time-seen-app heuristic
in your SIEM for higher fidelity.
title: Entra ID Consent to Third-Party OAuth Application
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d05
status: experimental
description: |
User-driven consent to a third-party OAuth application — illicit-
consent attack precursor. Pair with first-time-seen-app heuristic
in your SIEM for higher fidelity.
references:
- https://attack.mitre.org/techniques/T1528/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1528
- attack.persistence
logsource:
product: azure
service: auditlogs
detection:
selection:
OperationName: 'Consent to application'
Result: 'success'
condition: selection
falsepositives:
- Legitimate first-time SaaS app onboarding
level: medium
OperationName =~ "Consent to application" and Result =~ "success"
OperationName="Consent to application" Result="success"
OperationName:Consent\ to\ application AND Result:success
c2
1 rules · platform-neutral, compiled at build time
Outbound Connection to Article-Named IP / Domain
critical
Replace `__IP_LIST__` / `__DOMAIN_LIST__` placeholders with
article-specific destinations. For dynamic IOC management, use a
watchlist or external feed.
title: Outbound Connection to Article-Named IP / Domain
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d0f
status: experimental
description: |
Replace `__IP_LIST__` / `__DOMAIN_LIST__` placeholders with
article-specific destinations. For dynamic IOC management, use a
watchlist or external feed.
references:
- https://attack.mitre.org/tactics/TA0011/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.command-and-control
logsource:
category: network_connection
product: windows
detection:
selection_ip:
DestinationIp:
- '__IP_PLACEHOLDER_1__'
- '__IP_PLACEHOLDER_2__'
selection_domain:
DestinationHostname:
- '__DOMAIN_PLACEHOLDER_1__'
- '__DOMAIN_PLACEHOLDER_2__'
condition: selection_ip or selection_domain
falsepositives:
- None on a true-positive IOC match
level: critical
(DestinationIp in~ ("__IP_PLACEHOLDER_1__", "__IP_PLACEHOLDER_2__")) or (DestinationHostname in~ ("__DOMAIN_PLACEHOLDER_1__", "__DOMAIN_PLACEHOLDER_2__"))
DestinationIp IN ("__IP_PLACEHOLDER_1__", "__IP_PLACEHOLDER_2__") OR DestinationHostname IN ("__DOMAIN_PLACEHOLDER_1__", "__DOMAIN_PLACEHOLDER_2__")
(DestinationIp:(__IP_PLACEHOLDER_1__ OR __IP_PLACEHOLDER_2__)) OR (DestinationHostname:(__DOMAIN_PLACEHOLDER_1__ OR __DOMAIN_PLACEHOLDER_2__))
exploit
4 rules · platform-neutral, compiled at build time
FakeCaptcha / ClickFix - Clipboard-Injected PowerShell from Explorer
high
"FakeCaptcha" / "ClickFix" social-engineering: malicious site asks
user to press Win+R and paste a payload. The result is
powershell/mshta launched directly from explorer.exe with iex /
base64 / downloadstring in the command line.
title: FakeCaptcha / ClickFix - Clipboard-Injected PowerShell from Explorer
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d06
status: experimental
description: |
"FakeCaptcha" / "ClickFix" social-engineering: malicious site asks
user to press Win+R and paste a payload. The result is
powershell/mshta launched directly from explorer.exe with iex /
base64 / downloadstring in the command line.
references:
- https://attack.mitre.org/techniques/T1059/001/
- https://attack.mitre.org/techniques/T1204/002/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1059.001
- attack.t1204.002
- attack.execution
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\explorer.exe'
- '\RuntimeBroker.exe'
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\mshta.exe'
selection_cmd:
CommandLine|contains:
- 'iex'
- 'invoke-expression'
- 'frombase64'
- 'downloadstring'
- 'hxxp'
- 'curl '
- 'wget '
condition: selection_parent and selection_child and selection_cmd
falsepositives:
- IT-driven scripted automation that admins paste-launch
level: high
(ParentImage endswith "\\explorer.exe" or ParentImage endswith "\\RuntimeBroker.exe") and (Image endswith "\\powershell.exe" or Image endswith "\\pwsh.exe" or Image endswith "\\mshta.exe") and (CommandLine contains "iex" or CommandLine contains "invoke-expression" or CommandLine contains "frombase64" or CommandLine contains "downloadstring" or CommandLine contains "hxxp" or CommandLine contains "curl " or CommandLine contains "wget ")
ParentImage IN ("*\\explorer.exe", "*\\RuntimeBroker.exe") Image IN ("*\\powershell.exe", "*\\pwsh.exe", "*\\mshta.exe") CommandLine IN ("*iex*", "*invoke-expression*", "*frombase64*", "*downloadstring*", "*hxxp*", "*curl *", "*wget *")
(ParentImage:(*\\explorer.exe OR *\\RuntimeBroker.exe)) AND (Image:(*\\powershell.exe OR *\\pwsh.exe OR *\\mshta.exe)) AND (CommandLine:(*iex* OR *invoke\-expression* OR *frombase64* OR *downloadstring* OR *hxxp* OR *curl\ * OR *wget\ *))
(ParentImage endswith "\\winword.exe" or ParentImage endswith "\\excel.exe" or ParentImage endswith "\\powerpnt.exe" or ParentImage endswith "\\outlook.exe" or ParentImage endswith "\\onenote.exe" or ParentImage endswith "\\mspub.exe" or ParentImage endswith "\\visio.exe") and (Image endswith "\\cmd.exe" or Image endswith "\\powershell.exe" or Image endswith "\\pwsh.exe" or Image endswith "\\wscript.exe" or Image endswith "\\cscript.exe" or Image endswith "\\mshta.exe" or Image endswith "\\rundll32.exe" or Image endswith "\\regsvr32.exe")
ParentImage IN ("*\\winword.exe", "*\\excel.exe", "*\\powerpnt.exe", "*\\outlook.exe", "*\\onenote.exe", "*\\mspub.exe", "*\\visio.exe") Image IN ("*\\cmd.exe", "*\\powershell.exe", "*\\pwsh.exe", "*\\wscript.exe", "*\\cscript.exe", "*\\mshta.exe", "*\\rundll32.exe", "*\\regsvr32.exe")
(ParentImage:(*\\winword.exe OR *\\excel.exe OR *\\powerpnt.exe OR *\\outlook.exe OR *\\onenote.exe OR *\\mspub.exe OR *\\visio.exe)) AND (Image:(*\\cmd.exe OR *\\powershell.exe OR *\\pwsh.exe OR *\\wscript.exe OR *\\cscript.exe OR *\\mshta.exe OR *\\rundll32.exe OR *\\regsvr32.exe))
(Image endswith "\\powershell.exe" or Image endswith "\\pwsh.exe") and (CommandLine contains "-enc " or CommandLine contains "EncodedCommand" or CommandLine contains "FromBase64String" or CommandLine contains "Invoke-Expression" or CommandLine contains "IEX(" or CommandLine contains "DownloadString" or CommandLine contains "Net.WebClient" or CommandLine contains "-w hidden" or CommandLine contains "-WindowStyle Hidden" or CommandLine contains "-nop" or CommandLine contains "-NoProfile" or CommandLine contains "-ep bypass" or CommandLine contains "-ExecutionPolicy Bypass")
(Image:(*\\powershell.exe OR *\\pwsh.exe)) AND (CommandLine:(*\-enc\ * OR *EncodedCommand* OR *FromBase64String* OR *Invoke\-Expression* OR *IEX\(* OR *DownloadString* OR *Net.WebClient* OR *\-w\ hidden* OR *\-WindowStyle\ Hidden* OR *\-nop* OR *\-NoProfile* OR *\-ep\ bypass* OR *\-ExecutionPolicy\ Bypass*))
Trusted Vendor Binary Launching Unusual Children
medium
A signed vendor binary (in the article's named-vendors list)
spawning script-host or LOLBin children. Strongest signal when
paired with a recent CVE / supply-chain advisory naming the same
vendor.
title: Trusted Vendor Binary Launching Unusual Children
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d09
status: experimental
description: |
A signed vendor binary (in the article's named-vendors list)
spawning script-host or LOLBin children. Strongest signal when
paired with a recent CVE / supply-chain advisory naming the same
vendor.
references:
- https://attack.mitre.org/techniques/T1195/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1195
- attack.initial-access
logsource:
category: process_creation
product: windows
detection:
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\rundll32.exe'
- '\regsvr32.exe'
- '\mshta.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\wmic.exe'
- '\bitsadmin.exe'
selection_parent_vendor:
ParentImage|contains:
- '\Program Files\'
- '\Program Files (x86)\'
condition: selection_child and selection_parent_vendor
falsepositives:
- Many — installers and updaters legitimately spawn children. Pin
the parent path to specific vendor binaries observed in the
article before alerting.
level: medium
(Image endswith "\\powershell.exe" or Image endswith "\\pwsh.exe" or Image endswith "\\cmd.exe" or Image endswith "\\rundll32.exe" or Image endswith "\\regsvr32.exe" or Image endswith "\\mshta.exe" or Image endswith "\\wscript.exe" or Image endswith "\\cscript.exe" or Image endswith "\\wmic.exe" or Image endswith "\\bitsadmin.exe") and (ParentImage contains "\\Program Files\\" or ParentImage contains "\\Program Files (x86)\\")
Image IN ("*\\powershell.exe", "*\\pwsh.exe", "*\\cmd.exe", "*\\rundll32.exe", "*\\regsvr32.exe", "*\\mshta.exe", "*\\wscript.exe", "*\\cscript.exe", "*\\wmic.exe", "*\\bitsadmin.exe") ParentImage IN ("*\\Program Files\\*", "*\\Program Files (x86)\\*")
(Image:(*\\powershell.exe OR *\\pwsh.exe OR *\\cmd.exe OR *\\rundll32.exe OR *\\regsvr32.exe OR *\\mshta.exe OR *\\wscript.exe OR *\\cscript.exe OR *\\wmic.exe OR *\\bitsadmin.exe)) AND (ParentImage:(*\\Program\ Files\\* OR *\\Program\ Files\ \(x86\)\\*))
install
5 rules · platform-neutral, compiled at build time
Browser Extension Installation via Registry
medium
Registry writes under the browser extension policy keys —
administrative install of an extension. Often used by adware /
info-stealers to bypass user-level extension controls.
title: Browser Extension Installation via Registry
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d0a
status: experimental
description: |
Registry writes under the browser extension policy keys —
administrative install of an extension. Often used by adware /
info-stealers to bypass user-level extension controls.
references:
- https://attack.mitre.org/techniques/T1176/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1176
- attack.persistence
logsource:
category: registry_event
product: windows
detection:
selection:
TargetObject|contains:
- '\Software\Google\Chrome\Extensions\'
- '\Software\Microsoft\Edge\Extensions\'
- '\Software\Mozilla\Firefox\Extensions\'
condition: selection
falsepositives:
- Group-policy-managed extension whitelists
level: medium
TargetObject contains "\\Software\\Google\\Chrome\\Extensions\\" or TargetObject contains "\\Software\\Microsoft\\Edge\\Extensions\\" or TargetObject contains "\\Software\\Mozilla\\Firefox\\Extensions\\"
TargetObject IN ("*\\Software\\Google\\Chrome\\Extensions\\*", "*\\Software\\Microsoft\\Edge\\Extensions\\*", "*\\Software\\Mozilla\\Firefox\\Extensions\\*")
TargetObject:(*\\Software\\Google\\Chrome\\Extensions\\* OR *\\Software\\Microsoft\\Edge\\Extensions\\* OR *\\Software\\Mozilla\\Firefox\\Extensions\\*)
File or Process Matching Article Hash IOCs
critical
Replace `__HASH_LIST__` placeholder with article-specific SHA256
values. Use a watchlist or external indicator file for management
at scale.
AnyDesk / TeamViewer / ScreenConnect / Atera / Splashtop /
RustDesk — RMM tools attackers commonly install for hands-on-
keyboard access. Filter against the IT estate's known tool.
title: Remote-Management (RMM) Tool Execution
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d0c
status: experimental
description: |
AnyDesk / TeamViewer / ScreenConnect / Atera / Splashtop /
RustDesk — RMM tools attackers commonly install for hands-on-
keyboard access. Filter against the IT estate's known tool.
references:
- https://attack.mitre.org/techniques/T1219/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1219
- attack.command-and-control
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith:
- '\AnyDesk.exe'
- '\TeamViewer.exe'
- '\TeamViewer_Service.exe'
- '\ScreenConnect.ClientService.exe'
- '\ConnectWiseControl.ClientService.exe'
- '\atera_agent.exe'
- '\SplashtopStreamer.exe'
- '\RustDesk.exe'
- '\NinjaOne.exe'
condition: selection
falsepositives:
- IT-approved RMM in use across the estate. Maintain a known-IT-
tool exclusion list per environment.
level: high
Image endswith "\\AnyDesk.exe" or Image endswith "\\TeamViewer.exe" or Image endswith "\\TeamViewer_Service.exe" or Image endswith "\\ScreenConnect.ClientService.exe" or Image endswith "\\ConnectWiseControl.ClientService.exe" or Image endswith "\\atera_agent.exe" or Image endswith "\\SplashtopStreamer.exe" or Image endswith "\\RustDesk.exe" or Image endswith "\\NinjaOne.exe"
Image:(*\\AnyDesk.exe OR *\\TeamViewer.exe OR *\\TeamViewer_Service.exe OR *\\ScreenConnect.ClientService.exe OR *\\ConnectWiseControl.ClientService.exe OR *\\atera_agent.exe OR *\\SplashtopStreamer.exe OR *\\RustDesk.exe OR *\\NinjaOne.exe)
Scheduled Task Creation with Suspicious Image / Encoded Args
high
schtasks.exe /create with payload-shaped action — points at
powershell, cmd, rundll32, encoded-base64, or user-writable paths.
(Image endswith "\\schtasks.exe" and CommandLine contains "/create") and (CommandLine contains "powershell" or CommandLine contains "cmd.exe" or CommandLine contains "rundll32" or CommandLine contains "-enc" or CommandLine contains "FromBase64" or CommandLine contains "\\Users\\Public" or CommandLine contains "\\AppData\\")
(Image:*\\schtasks.exe AND CommandLine:*\/create*) AND (CommandLine:(*powershell* OR *cmd.exe* OR *rundll32* OR *\-enc* OR *FromBase64* OR *\\Users\\Public* OR *\\AppData\\*))
Service Persistence via sc.exe with User-Writable Path
high
sc.exe create with the binary path under \Users\, \AppData\,
\ProgramData\, or \Temp\ — service-install persistence outside
legitimate program directories.
title: Service Persistence via sc.exe with User-Writable Path
id: 7c8b2e10-4d4f-4a8c-9c1f-bf6e2c4a8d0e
status: experimental
description: |
sc.exe create with the binary path under \Users\, \AppData\,
\ProgramData\, or \Temp\ — service-install persistence outside
legitimate program directories.
references:
- https://attack.mitre.org/techniques/T1543/003/
author: Clankerusecase
date: 2026/05/02
tags:
- attack.t1543.003
- attack.persistence
logsource:
category: process_creation
product: windows
detection:
selection_sc:
Image|endswith: '\sc.exe'
CommandLine|contains: 'create'
selection_path:
CommandLine|contains:
- '\Users\'
- '\AppData\'
- '\ProgramData\'
- '\Temp\'
condition: selection_sc and selection_path
falsepositives:
- Internal install scripts dropping a service from %ProgramData%
level: high
(Image endswith "\\sc.exe" and CommandLine contains "create") and (CommandLine contains "\\Users\\" or CommandLine contains "\\AppData\\" or CommandLine contains "\\ProgramData\\" or CommandLine contains "\\Temp\\")
Image="*\\sc.exe" CommandLine="*create*" CommandLine IN ("*\\Users\\*", "*\\AppData\\*", "*\\ProgramData\\*", "*\\Temp\\*")
(Image:*\\sc.exe AND CommandLine:*create*) AND (CommandLine:(*\\Users\\* OR *\\AppData\\* OR *\\ProgramData\\* OR *\\Temp\\*))
Splunk · CIM datamodels (SPL)
Pick a CIM datamodel from the sidebar — every query is verbatim Splunk SPL using tstats against accelerated datamodels. House style: summariesonly macro for fast tstats; drop_dm_object_name(<DM>) to flatten object-prefixed fields; <placeholder> fields the analyst replaces during a shift.
tstats vs | from datamodel: vs raw search.tstats queries against an accelerated CIM model are the fastest path — they read the precomputed summary index. If your env has no CIM acceleration, every tstats query below has a Non-summarised toggle that switches to summariesonly=false (slower but works without the summary). Raw search index=... queries always work but won't benefit from any acceleration.
8 CIM datamodels curated40 queriesClick Copy on any query to paste into Splunk Search.
User triage — every process exec tied to one identity in the last 24h. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Processes.process) AS process, values(Processes.parent_process_name) AS parent
from datamodel=Endpoint.Processes
where Processes.user=<UserName> earliest=-24h@h
by Processes.dest, Processes.process_name, _time span=10m
| `drop_dm_object_name(Processes)`
| sort -_time
| tstats summariesonly=false count, values(Processes.process) AS process, values(Processes.parent_process_name) AS parent
from datamodel=Endpoint.Processes
where Processes.user=<UserName> earliest=-24h@h
by Processes.dest, Processes.process_name, _time span=10m
| `drop_dm_object_name(Processes)`
| sort -_time
All processes on a host
Host triage — every process exec on one device. Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Processes.process) AS process, values(Processes.parent_process_name) AS parent
from datamodel=Endpoint.Processes
where Processes.dest=<DeviceName> earliest=-24h@h
by Processes.user, Processes.process_name, _time span=1m
| `drop_dm_object_name(Processes)`
| sort -_time
| tstats summariesonly=false count, values(Processes.process) AS process, values(Processes.parent_process_name) AS parent
from datamodel=Endpoint.Processes
where Processes.dest=<DeviceName> earliest=-24h@h
by Processes.user, Processes.process_name, _time span=1m
| `drop_dm_object_name(Processes)`
| sort -_time
Children of a specific process
What did `<parent.exe>` spawn? Replace the parent process name.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Processes.process) AS cmdline
from datamodel=Endpoint.Processes
where Processes.parent_process_name=<parent.exe> earliest=-24h@h
by Processes.dest, Processes.user, Processes.process_name
| tstats summariesonly=false count, values(Processes.process) AS cmdline
from datamodel=Endpoint.Processes
where Processes.parent_process_name=<parent.exe> earliest=-24h@h
by Processes.dest, Processes.user, Processes.process_name
Find every host that ran a binary
Binary-name pivot. Replace `<binary.exe>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, min(_time) AS first_seen, max(_time) AS last_seen
from datamodel=Endpoint.Processes
where Processes.process_name=<binary.exe> earliest=-7d@d
by Processes.dest, Processes.user, Processes.process
| tstats summariesonly=false count, min(_time) AS first_seen, max(_time) AS last_seen
from datamodel=Endpoint.Processes
where Processes.process_name=<binary.exe> earliest=-7d@d
by Processes.dest, Processes.user, Processes.process
Find every host that ran a hash
IOC pivot — every host that ran a binary with this SHA256. Replace `<sha256>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, min(_time) AS first_seen, max(_time) AS last_seen
from datamodel=Endpoint.Processes
where Processes.process_hash=<sha256> earliest=-30d@d
by Processes.dest, Processes.user, Processes.process_name, Processes.process
| tstats summariesonly=false count, min(_time) AS first_seen, max(_time) AS last_seen
from datamodel=Endpoint.Processes
where Processes.process_hash=<sha256> earliest=-30d@d
by Processes.dest, Processes.user, Processes.process_name, Processes.process
Process tree for a user during a time window
Reconstruct what a user was doing between `<start>` and `<end>` (use `-2h@h`, `-30m@m`, etc).
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.user=<UserName> earliest=<start> latest=<end>
by _time, Processes.dest, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| sort _time
| tstats summariesonly=false count
from datamodel=Endpoint.Processes
where Processes.user=<UserName> earliest=<start> latest=<end>
by _time, Processes.dest, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| sort _time
Host triage — every create / modify / delete on one device. Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.dest=<DeviceName> earliest=-24h@h
by Filesystem.user, Filesystem.action, Filesystem.process_name, Filesystem.file_path, Filesystem.file_name, _time span=10m
| sort -_time
| tstats summariesonly=false count
from datamodel=Endpoint.Filesystem
where Filesystem.dest=<DeviceName> earliest=-24h@h
by Filesystem.user, Filesystem.action, Filesystem.process_name, Filesystem.file_path, Filesystem.file_name, _time span=10m
| sort -_time
All file activity for a user
User triage. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.user=<UserName> earliest=-24h@h
by Filesystem.dest, Filesystem.action, Filesystem.process_name, Filesystem.file_path, Filesystem.file_name
| tstats summariesonly=false count
from datamodel=Endpoint.Filesystem
where Filesystem.user=<UserName> earliest=-24h@h
by Filesystem.dest, Filesystem.action, Filesystem.process_name, Filesystem.file_path, Filesystem.file_name
Files written by a process
What did `<binary.exe>` create? Replace the process name.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action="created" Filesystem.process_name=<binary.exe> earliest=-24h@h
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name
| tstats summariesonly=false count
from datamodel=Endpoint.Filesystem
where Filesystem.action="created" Filesystem.process_name=<binary.exe> earliest=-24h@h
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Filesystem.process_name) AS writers
from datamodel=Endpoint.Filesystem
where Filesystem.action="created" Filesystem.file_path="*<path-fragment>*" earliest=-24h@h
by Filesystem.dest, Filesystem.user, Filesystem.file_name
| tstats summariesonly=false count, values(Filesystem.process_name) AS writers
from datamodel=Endpoint.Filesystem
where Filesystem.action="created" Filesystem.file_path="*<path-fragment>*" earliest=-24h@h
by Filesystem.dest, Filesystem.user, Filesystem.file_name
Hash-IOC sweep across the fleet
Was this hash ever written? Replace `<sha256>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Filesystem.dest) AS hosts
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash=<sha256> earliest=-30d@d
by Filesystem.file_name
| tstats summariesonly=false count, values(Filesystem.dest) AS hosts
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash=<sha256> earliest=-30d@d
by Filesystem.file_name
Host triage — every registry write / modify / delete on one device. Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Registry
where Registry.dest=<DeviceName> earliest=-24h@h
by Registry.user, Registry.action, Registry.process_name, Registry.registry_path, Registry.registry_value_name
| tstats summariesonly=false count
from datamodel=Endpoint.Registry
where Registry.dest=<DeviceName> earliest=-24h@h
by Registry.user, Registry.action, Registry.process_name, Registry.registry_path, Registry.registry_value_name
Registry activity for a user
User triage. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Registry
where Registry.user=<UserName> earliest=-24h@h
by Registry.dest, Registry.action, Registry.process_name, Registry.registry_path
| tstats summariesonly=false count
from datamodel=Endpoint.Registry
where Registry.user=<UserName> earliest=-24h@h
by Registry.dest, Registry.action, Registry.process_name, Registry.registry_path
Registry writes under a key path
Key-path pivot. Replace `<path-fragment>` (e.g. `\\Run\\` or `\\Services\\`).
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Endpoint.Registry
where Registry.action in ("created","modified")
and Registry.registry_path="*<path-fragment>*" earliest=-24h@h
by Registry.dest, Registry.user, Registry.process_name, Registry.registry_path, Registry.registry_value_data
| tstats summariesonly=false count
from datamodel=Endpoint.Registry
where Registry.action in ("created","modified")
and Registry.registry_path="*<path-fragment>*" earliest=-24h@h
by Registry.dest, Registry.user, Registry.process_name, Registry.registry_path, Registry.registry_value_data
Find which process wrote a registry value
Pivot for an interesting key — who set it? Replace `<value-name>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Registry.registry_value_data) AS data
from datamodel=Endpoint.Registry
where Registry.registry_value_name=<value-name> Registry.action in ("created","modified")
earliest=-7d@d
by Registry.dest, Registry.user, Registry.process_name, Registry.registry_path
| tstats summariesonly=false count, values(Registry.registry_value_data) AS data
from datamodel=Endpoint.Registry
where Registry.registry_value_name=<value-name> Registry.action in ("created","modified")
earliest=-7d@d
by Registry.dest, Registry.user, Registry.process_name, Registry.registry_path
Host triage — every allowed outbound connection. Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, sum(All_Traffic.bytes_out) AS bytes_out
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.action="allowed"
earliest=-24h@h
by All_Traffic.dest, All_Traffic.dest_port, _time span=10m
| `drop_dm_object_name(All_Traffic)`
| sort -_time
| tstats summariesonly=false count, sum(All_Traffic.bytes_out) AS bytes_out
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.action="allowed"
earliest=-24h@h
by All_Traffic.dest, All_Traffic.dest_port, _time span=10m
| `drop_dm_object_name(All_Traffic)`
| sort -_time
All connections to an IP
IP IOC pivot — every host that talked to it. Replace `<IP>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports, sum(All_Traffic.bytes_out) AS bytes_out
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest_ip=<IP> earliest=-7d@d
by All_Traffic.src, All_Traffic.user
| tstats summariesonly=false count, values(All_Traffic.dest_port) AS ports, sum(All_Traffic.bytes_out) AS bytes_out
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest_ip=<IP> earliest=-7d@d
by All_Traffic.src, All_Traffic.user
All connections on a port from a host
Port-specific triage. Replace `<DeviceName>` and `<port>` (e.g. 3389, 22, 445).
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.dest_port=<port> earliest=-24h@h
by All_Traffic.dest, All_Traffic.action, _time span=5m
| tstats summariesonly=false count
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.dest_port=<port> earliest=-24h@h
by All_Traffic.dest, All_Traffic.action, _time span=5m
Top destinations for a host
What's a host been talking to most? Useful for first-look beaconing review.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, sum(All_Traffic.bytes_out) AS bytes_out
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.action="allowed" earliest=-24h@h
by All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| sort - count
| tstats summariesonly=false count, sum(All_Traffic.bytes_out) AS bytes_out
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.action="allowed" earliest=-24h@h
by All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| sort - count
Failed / denied connections from a host
What's the firewall blocking from this host? Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.action!="allowed" earliest=-24h@h
by All_Traffic.dest, All_Traffic.dest_port, All_Traffic.action
| tstats summariesonly=false count
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.src=<DeviceName>
and All_Traffic.action!="allowed" earliest=-24h@h
by All_Traffic.dest, All_Traffic.dest_port, All_Traffic.action
User triage — every login (success + fail) for one identity. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Authentication.Authentication
where Authentication.user=<UserName> earliest=-24h@h
by Authentication.action, Authentication.src, Authentication.dest, _time span=10m
| `drop_dm_object_name(Authentication)`
| sort -_time
| tstats summariesonly=false count
from datamodel=Authentication.Authentication
where Authentication.user=<UserName> earliest=-24h@h
by Authentication.action, Authentication.src, Authentication.dest, _time span=10m
| `drop_dm_object_name(Authentication)`
| sort -_time
All logins from an IP
IP pivot — every account this address has authenticated against. Replace `<IP>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Authentication.Authentication
where Authentication.src=<IP> earliest=-7d@d
by Authentication.user, Authentication.action, Authentication.dest
| tstats summariesonly=false count
from datamodel=Authentication.Authentication
where Authentication.src=<IP> earliest=-7d@d
by Authentication.user, Authentication.action, Authentication.dest
Logins to a specific host
Host pivot — who's logged on (and tried to log on) here? Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Authentication.Authentication
where Authentication.dest=<DeviceName> earliest=-24h@h
by Authentication.user, Authentication.src, Authentication.action
| tstats summariesonly=false count
from datamodel=Authentication.Authentication
where Authentication.dest=<DeviceName> earliest=-24h@h
by Authentication.user, Authentication.src, Authentication.action
Failed authentications for a user
Fast triage on a user's failures. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Authentication.Authentication
where Authentication.action="failure" Authentication.user=<UserName> earliest=-24h@h
by Authentication.src, Authentication.dest, Authentication.reason, _time span=10m
| tstats summariesonly=false count
from datamodel=Authentication.Authentication
where Authentication.action="failure" Authentication.user=<UserName> earliest=-24h@h
by Authentication.src, Authentication.dest, Authentication.reason, _time span=10m
All source IPs a user has used
Find the geographic / network footprint for one identity. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` values(Authentication.src) AS src_ips, count
from datamodel=Authentication.Authentication
where Authentication.action="success" Authentication.user=<UserName> earliest=-30d@d
by Authentication.user
| tstats summariesonly=false values(Authentication.src) AS src_ips, count
from datamodel=Authentication.Authentication
where Authentication.action="success" Authentication.user=<UserName> earliest=-30d@d
by Authentication.user
User triage — every web request for one identity. Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Web.Web
where Web.user=<UserName> earliest=-24h@h
by Web.url_domain, Web.http_method, Web.status, _time span=10m
| tstats summariesonly=false count
from datamodel=Web.Web
where Web.user=<UserName> earliest=-24h@h
by Web.url_domain, Web.http_method, Web.status, _time span=10m
All HTTP traffic from a source
Source pivot. Replace `<src>` with hostname or IP.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, sum(Web.bytes_out) AS bytes_out
from datamodel=Web.Web
where Web.src=<src> earliest=-24h@h
by Web.url_domain, Web.http_method, Web.status
| tstats summariesonly=false count, sum(Web.bytes_out) AS bytes_out
from datamodel=Web.Web
where Web.src=<src> earliest=-24h@h
by Web.url_domain, Web.http_method, Web.status
All requests to a domain
Domain pivot — every host that fetched it. Replace `<domain>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, values(Web.url) AS urls
from datamodel=Web.Web
where Web.url_domain=<domain> earliest=-7d@d
by Web.src, Web.user, Web.http_method, Web.status
| tstats summariesonly=false count, values(Web.url) AS urls
from datamodel=Web.Web
where Web.url_domain=<domain> earliest=-7d@d
by Web.src, Web.user, Web.http_method, Web.status
Requests with a User-Agent
UA pivot — find every host using a specific UA. Replace `<UA-fragment>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Web.Web
where Web.http_user_agent="*<UA-fragment>*" earliest=-24h@h
by Web.src, Web.url_domain, Web.http_user_agent
| tstats summariesonly=false count
from datamodel=Web.Web
where Web.http_user_agent="*<UA-fragment>*" earliest=-24h@h
by Web.src, Web.url_domain, Web.http_user_agent
Top domains for a user
What's a user been browsing most? Replace `<UserName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Web.Web
where Web.user=<UserName> earliest=-24h@h
by Web.url_domain
| `drop_dm_object_name(Web)`
| sort - count
| tstats summariesonly=false count
from datamodel=Web.Web
where Web.user=<UserName> earliest=-24h@h
by Web.url_domain
| `drop_dm_object_name(Web)`
| sort - count
Host triage — every name resolution from one device. Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Network_Resolution.DNS
where DNS.src=<DeviceName> earliest=-24h@h
by DNS.query, DNS.query_type, DNS.reply_code
| tstats summariesonly=false count
from datamodel=Network_Resolution.DNS
where DNS.src=<DeviceName> earliest=-24h@h
by DNS.query, DNS.query_type, DNS.reply_code
Hosts that resolved a domain
Domain pivot — every device that asked for it. Replace `<domain>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count, min(_time) AS first_seen, max(_time) AS last_seen
from datamodel=Network_Resolution.DNS
where DNS.query=<domain> earliest=-7d@d
by DNS.src
| tstats summariesonly=false count, min(_time) AS first_seen, max(_time) AS last_seen
from datamodel=Network_Resolution.DNS
where DNS.query=<domain> earliest=-7d@d
by DNS.src
DNS queries that resolved to an IP
IP-back-pivot — what names point to this address? Replace `<IP>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Network_Resolution.DNS
where DNS.answer=<IP> earliest=-7d@d
by DNS.query, DNS.src
| tstats summariesonly=false count
from datamodel=Network_Resolution.DNS
where DNS.answer=<IP> earliest=-7d@d
by DNS.query, DNS.src
Top domains for a host
What's a host been resolving most? Replace `<DeviceName>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Network_Resolution.DNS
where DNS.src=<DeviceName> earliest=-24h@h
by DNS.query
| `drop_dm_object_name(DNS)`
| sort - count
| tstats summariesonly=false count
from datamodel=Network_Resolution.DNS
where DNS.src=<DeviceName> earliest=-24h@h
by DNS.query
| `drop_dm_object_name(DNS)`
| sort - count
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Network_Resolution.DNS
where DNS.src=<DeviceName>
and DNS.reply_code in ("NXDOMAIN","SERVFAIL","REFUSED") earliest=-24h@h
by DNS.query, DNS.reply_code
| tstats summariesonly=false count
from datamodel=Network_Resolution.DNS
where DNS.src=<DeviceName>
and DNS.reply_code in ("NXDOMAIN","SERVFAIL","REFUSED") earliest=-24h@h
by DNS.query, DNS.reply_code
Recipient triage — every email this user received. Replace `<email>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Email.All_Email
where Email.recipient=<email> earliest=-7d@d
by Email.src_user, Email.subject, Email.action, _time span=1h
| sort -_time
| tstats summariesonly=false count
from datamodel=Email.All_Email
where Email.recipient=<email> earliest=-7d@d
by Email.src_user, Email.subject, Email.action, _time span=1h
| sort -_time
All emails from a sender
Sender pivot. Replace `<email-or-domain>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Email.All_Email
where Email.src_user=<email-or-domain> earliest=-7d@d
by Email.recipient, Email.subject, Email.action
| tstats summariesonly=false count
from datamodel=Email.All_Email
where Email.src_user=<email-or-domain> earliest=-7d@d
by Email.recipient, Email.subject, Email.action
Emails by subject
Subject-line pivot. Replace `<subject-fragment>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Email.All_Email
where Email.subject="*<subject-fragment>*" earliest=-7d@d
by Email.src_user, Email.recipient, Email.action, Email.filename
| tstats summariesonly=false count
from datamodel=Email.All_Email
where Email.subject="*<subject-fragment>*" earliest=-7d@d
by Email.src_user, Email.recipient, Email.action, Email.filename
Emails with an attachment name / extension
Filename pivot. Replace `<filename-or-ext>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Email.All_Email
where Email.filename="*<filename-or-ext>*" earliest=-7d@d
by Email.recipient, Email.src_user, Email.filename, Email.subject
| tstats summariesonly=false count
from datamodel=Email.All_Email
where Email.filename="*<filename-or-ext>*" earliest=-7d@d
by Email.recipient, Email.src_user, Email.filename, Email.subject
Emails with a URL
URL pivot — every recipient who got a message containing this link. Replace `<url-fragment>`.
Toggle if your env has no CIM data-model acceleration.
| tstats `summariesonly` count
from datamodel=Email.All_Email
where Email.url="*<url-fragment>*" earliest=-7d@d
by Email.recipient, Email.src_user, Email.subject
| tstats summariesonly=false count
from datamodel=Email.All_Email
where Email.url="*<url-fragment>*" earliest=-7d@d
by Email.recipient, Email.src_user, Email.subject