1
0
Форкнуть 0

Update github queries to use the new advanced hunting device schema

This commit is contained in:
Maayan Kislev 2020-01-05 15:46:07 +02:00
Родитель 00dabae92e
Коммит d3d93746b3
60 изменённых файлов: 420 добавлений и 420 удалений

Просмотреть файл

@ -1,8 +1,8 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_babyshark.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine =~ @"reg query ""HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default"""
or ProcessCommandLine startswith "powershell.exe mshta.exe http"
or ProcessCommandLine =~ "cmd.exe /c taskkill /im cmd.exe"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,6 +1,6 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_apt29_thinktanks.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has "-noni -ep bypass $"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,9 +1,9 @@
// Sample query that search for .settingcontent-ms that has been downloaded from the web
// through Microsoft Edge, Internet Explorer, Google Chrome, Mozilla Firefox, Microsoft Outlook
// For questions @MiladMSFT on Twitter or milad.aslaner@microsoft.com
FileCreationEvents
DeviceFileEvents
| where InitiatingProcessFileName in~ ("browser_broker.exe", "chrome.exe", "iexplore.exe", "firefox.exe", "outlook.exe")
| where FileName endswith ".settingcontent-ms"
// The FileOrigin* columns are available only on Edge and Chrome and from Windows 10 version 1703
// https://techcommunity.microsoft.com/t5/Threat-Intelligence/Hunting-tip-of-the-month-Browser-downloads/td-p/220454
| project EventTime, ComputerName, FileName, FolderPath, FileOriginUrl, FileOriginReferrerUrl, FileOriginIP
| project Timestamp, DeviceName, FileName, FolderPath, FileOriginUrl, FileOriginReferrerUrl, FileOriginIP

Просмотреть файл

@ -1,7 +1,7 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_bear_activity_gtr19.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where (FileName =~ "xcopy.exe" and ProcessCommandLine has @" /S /E /C /Q /H \")
or (FileName =~ "adexplorer.exe" and ProcessCommandLine has @" -snapshot """" c:\users\")
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,6 +1,6 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_cloudhopper.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ @"cscript.exe" and ProcessCommandLine has ".vbs /shell "
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,10 +1,10 @@
// This is a query to retrieve last 30 days network connections to known Dofoil NameCoin servers
// The full article is available here: https://cloudblogs.microsoft.com/microsoftsecure/2018/04/04/hunting-down-dofoil-with-windows-defender-atp/
NetworkCommunicationEvents
DeviceNetworkEvents
| where RemoteIP in (
"139.59.208.246","130.255.73.90","31.3.135.232","52.174.55.168","185.121.177.177","185.121.177.53",
"62.113.203.55","144.76.133.38","169.239.202.202","5.135.183.146","142.0.68.13","103.253.12.18",
"62.112.8.85","69.164.196.21","107.150.40.234","162.211.64.20","217.12.210.54","89.18.27.34",
"193.183.98.154","51.255.167.0","91.121.155.13","87.98.175.85","185.97.7.7")
| project ComputerName, InitiatingProcessCreationTime, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort
| project DeviceName, InitiatingProcessCreationTime, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort

Просмотреть файл

@ -1,6 +1,6 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_dragonfly.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "crackmapexec.exe"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,7 +1,7 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_elise.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where (FolderPath =~ @"C:\Windows\SysWOW64\cmd.exe" and ProcessCommandLine has @"\Windows\Caches\NavShExt.dll")
or (ProcessCommandLine endswith @"\AppData\Roaming\MICROS~1\Windows\Caches\NavShExt.dll,Setting")
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,7 +1,7 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_equationgroup_c2.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where (FolderPath endswith @"\rundll32.exe" and ProcessCommandLine endswith ",dll_u")
or ProcessCommandLine has " -export dll_u "
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,7 +1,7 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_hurricane_panda.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine endswith " localgroup administrators admin /add"
or ProcessCommandLine has @"\Win64.exe"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,7 +1,7 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_judgement_panda_gtr19.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has @"\ldifde.exe -f -n "
or ProcessCommandLine has @"\7za.exe a 1.7z "
or ProcessCommandLine endswith @" eprod.ldf"
@ -11,4 +11,4 @@ ProcessCreationEvents
or ProcessCommandLine has @"copy .\1.7z \"
or ProcessCommandLine has @"copy \client\c$\aaaa\"
or FolderPath == @"C:\Users\Public\7za.exe"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -3,8 +3,8 @@
// https://blog.trendmicro.com/trendlabs-security-intelligence/new-macos-backdoor-linked-to-oceanlotus-found/
//
// OS platforms: Macintosh
ProcessCreationEvents
| where EventTime > ago(14d)
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName in~ ("screenassistantd","spellagentd")
| top 100 by EventTime
| top 100 by Timestamp

Просмотреть файл

@ -2,9 +2,9 @@
// References:
// https://blog.trendmicro.com/trendlabs-security-intelligence/new-macos-backdoor-linked-to-oceanlotus-found/
// OS Platforms: Macintosh
ProcessCreationEvents
| where EventTime > ago(14d)
DeviceProcessEvents
| where Timestamp > ago(14d)
| where ProcessCommandLine contains "theme0"
| project EventTime, MachineId , ComputerName, AccountName , AccountSid , InitiatingProcessCommandLine , ProcessCommandLine
| top 100 by EventTime
| project Timestamp, DeviceId , DeviceName, AccountName , AccountSid , InitiatingProcessCommandLine , ProcessCommandLine
| top 100 by Timestamp

Просмотреть файл

@ -1,7 +1,7 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_oceanlotus_registry.yml
// Questions via Twitter: @janvonkirchheim
RegistryEvents
| where EventTime > ago(7d)
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where ActionType == "RegistryValueSet"
| where RegistryKey endswith @"\SOFTWARE\Classes\CLSID\{E08A0F4B-1F65-4D4D-9A09-BD4625B9C5A1}\Model"
or RegistryKey endswith @"\SOFTWARE\App\AppXbf13d4ea2945444d8b13e2121cb6b663\Application"

Просмотреть файл

@ -1,6 +1,6 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_sofacy_zebrocy.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine endswith "cmd.exe /c SYSTEMINFO & TASKLIST"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,6 +1,6 @@
// Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/apt/apt_tropictrooper.yml
// Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine contains "abCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCc"
| top 100 by EventTime desc
| top 100 by Timestamp desc

Просмотреть файл

@ -1,13 +1,13 @@
// This query looks for Tor client, or for a common Tor plugin called Meek.
// We query for active Tor connections, but could have alternatively looked for active Tor runs (ProcessCreateEvents) or Tor downloads (FileCreationEvents)
// We query for active Tor connections, but could have alternatively looked for active Tor runs (ProcessCreateEvents) or Tor downloads (DeviceFileEvents)
// To read more about this technique, see:
// Tor: https://attack.mitre.org/wiki/Software/S0183#Techniques_Used
// Meek plugin: https://attack.mitre.org/wiki/Software/S0175
// Multi-hop proxy technique: https://attack.mitre.org/wiki/Technique/T1188
// Tags: #Tor, #MultiHopProxy, #CnC
NetworkCommunicationEvents
| where EventTime < ago(3d) and InitiatingProcessFileName in~ ("tor.exe", "meek-client.exe")
DeviceNetworkEvents
| where Timestamp < ago(3d) and InitiatingProcessFileName in~ ("tor.exe", "meek-client.exe")
// Returns MD5 hashes of files used by Tor, to enable you to block them.
// We count how prevalent each file is (by machines) and show examples for some of them (up to 5 machine names per hash).
| summarize MachineCount=dcount(ComputerName), MachineNames=makeset(ComputerName, 5) by InitiatingProcessMD5
| summarize MachineCount=dcount(DeviceName), MachineNames=makeset(DeviceName, 5) by InitiatingProcessMD5
| order by MachineCount desc

Просмотреть файл

@ -5,8 +5,8 @@
// Implementation comment #1: Matching events by time
// Matching the 3 different events (saving attachment, clicking on link, downloading file) is done purely by time difference - so could sometimes link together unrelated events.
// Doing a more exact lookup would create a much more complex query due to
// Implementation comment #2: Deduping FileCreationEvents
// Oftentimes there are multiple FileCreationEvents for a single file - e.g. if the file keeps being appended into before being closed.
// Implementation comment #2: Deduping DeviceFileEvents
// Oftentimes there are multiple DeviceFileEvents for a single file - e.g. if the file keeps being appended into before being closed.
// So, we query only for the last reported file state to ignore intermediate file states.
// Explaining the underlying data:
// BrowserLaunchedToOpenUrl event:
@ -14,33 +14,33 @@
// For this event, RemoteUrl contains the opened URL.
let minTimeRange = ago(7d);
let wordLinks =
MiscEvents
DeviceEvents
// Filter on click on links from WinWord
| where EventTime > minTimeRange and ActionType == "BrowserLaunchedToOpenUrl" and isnotempty(RemoteUrl) and InitiatingProcessFileName =~ "winword.exe"
| project ClickTime=EventTime, MachineId, ComputerName, ClickUrl=RemoteUrl;
| where Timestamp > minTimeRange and ActionType == "BrowserLaunchedToOpenUrl" and isnotempty(RemoteUrl) and InitiatingProcessFileName =~ "winword.exe"
| project ClickTime=Timestamp, DeviceId, DeviceName, ClickUrl=RemoteUrl;
let docAttachments =
FileCreationEvents
| where EventTime > minTimeRange
DeviceFileEvents
| where Timestamp > minTimeRange
// Query for common document file extensions
and (FileName endswith ".docx" or FileName endswith ".docm" or FileName endswith ".doc")
// Query for files saved from email clients such as the Office Outlook app or the Windows Mail app
and InitiatingProcessFileName in~ ("outlook.exe", "hxoutlook.exe")
| summarize AttachmentSaveTime=min(EventTime) by AttachmentName=FileName, MachineId;
| summarize AttachmentSaveTime=min(Timestamp) by AttachmentName=FileName, DeviceId;
let browserDownloads =
FileCreationEvents
| where EventTime > minTimeRange
DeviceFileEvents
| where Timestamp > minTimeRange
// Query for files created by common browsers
and InitiatingProcessFileName in~ ("browser_broker.exe", "chrome.exe", "iexplore.exe", "firefox.exe")
// Exclude JS files that are used for loading sites (but still query for JS files that are known to be downloaded)
and not (FileName endswith ".js" and isempty(FileOriginUrl))
// Further filter to exclude file extensions that are less indicative of an attack (when there were already previously a doc attachment that included a link)
| where FileName !endswith ".partial" and FileName !endswith ".docx"
| summarize (EventTime, SHA1) = argmax(EventTime, SHA1) by FileName, MachineId, FileOriginUrl;
| summarize (Timestamp, SHA1) = argmax(Timestamp, SHA1) by FileName, DeviceId, FileOriginUrl;
// Perf tip: start the joins from the smallest table (put it on the left-most side of the joins)
wordLinks
| join kind= inner (docAttachments) on MachineId | where ClickTime - AttachmentSaveTime between (0min..3min)
| join kind= inner (browserDownloads) on MachineId | where EventTime - ClickTime between (0min..3min)
| join kind= inner (docAttachments) on DeviceId | where ClickTime - AttachmentSaveTime between (0min..3min)
| join kind= inner (browserDownloads) on DeviceId | where Timestamp - ClickTime between (0min..3min)
// Aggregating multiple "attachments" together - because oftentimes the same file is stored multiple times under different names
| summarize Attachments=makeset(AttachmentName), AttachmentSaveTime=min(AttachmentSaveTime), ClickTime=min(ClickTime)
by // Downloaded file details
bin(EventTime, 1tick), FileName, FileOriginUrl, ClickUrl, SHA1, ComputerName, MachineId
bin(Timestamp, 1tick), FileName, FileOriginUrl, ClickUrl, SHA1, DeviceName, DeviceId

Просмотреть файл

@ -3,9 +3,9 @@
// Read more about download URL data and about this attack vector in this blog post:
// https://techcommunity.microsoft.com/t5/Threat-Intelligence/Hunting-tip-of-the-month-Browser-downloads/td-p/220454
// Tags: #DownloadUrl, #Referer, #Dropbox
FileCreationEvents
DeviceFileEvents
| where
EventTime > ago(7d)
Timestamp > ago(7d)
and FileOriginUrl startswith "https://dl.dropboxusercontent.com/"
and isnotempty(FileOriginReferrerUrl)
and FileOriginReferrerUrl !startswith "https://www.dropbox.com/"

Просмотреть файл

@ -4,29 +4,29 @@
// Tags: #EmailLink, #BrowserDownload, #SmartScreen
let smartscreenAppWarnings =
// Query for SmartScreen warnings of unknown executed applications
MiscEvents
DeviceEvents
| where ActionType == "SmartScreenAppWarning"
| project WarnTime=EventTime, ComputerName, WarnedFileName=FileName, WarnedSHA1=SHA1, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string))
| project WarnTime=Timestamp, DeviceName, WarnedFileName=FileName, WarnedSHA1=SHA1, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string))
// Select only warnings that the user has decided to ignore and has executed the app.
| join kind=leftsemi (
MiscEvents
DeviceEvents
| where ActionType == "SmartScreenUserOverride"
| project ComputerName, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string)))
on ComputerName, ActivityId
| project DeviceName, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string)))
on DeviceName, ActivityId
| project-away ActivityId;
// Query for links opened from outlook, that are close in time to a SmartScreen warning
let emailLinksNearSmartScreenWarnings =
MiscEvents
DeviceEvents
| where ActionType == "BrowserLaunchedToOpenUrl" and isnotempty(RemoteUrl) and InitiatingProcessFileName =~ "outlook.exe"
| extend WasOutlookSafeLink=(tostring(parse_url(RemoteUrl).Host) endswith "safelinks.protection.outlook.com")
| project ComputerName, MailLinkTime=EventTime,
| project DeviceName, MailLinkTime=Timestamp,
MailLink=iff(WasOutlookSafeLink, url_decode(tostring(parse_url(RemoteUrl)["Query Parameters"]["url"])), RemoteUrl)
| join kind=inner smartscreenAppWarnings on ComputerName | where (WarnTime-MailLinkTime) between (0min..4min);
| join kind=inner smartscreenAppWarnings on DeviceName | where (WarnTime-MailLinkTime) between (0min..4min);
// Add the browser download event to tie in all the dots
FileCreationEvents
DeviceFileEvents
| where isnotempty(FileOriginUrl) and InitiatingProcessFileName in~ ("chrome.exe", "browser_broker.exe")
| project FileName, FileOriginUrl, FileOriginReferrerUrl, ComputerName, EventTime, SHA1
| join kind=inner emailLinksNearSmartScreenWarnings on ComputerName
| where (EventTime-MailLinkTime) between (0min..3min) and (WarnTime-EventTime) between (0min..1min)
| project FileName, MailLink, FileOriginUrl, FileOriginReferrerUrl, WarnedFileName, ComputerName, SHA1, WarnedSHA1, EventTime
| project FileName, FileOriginUrl, FileOriginReferrerUrl, DeviceName, Timestamp, SHA1
| join kind=inner emailLinksNearSmartScreenWarnings on DeviceName
| where (Timestamp-MailLinkTime) between (0min..3min) and (WarnTime-Timestamp) between (0min..1min)
| project FileName, MailLink, FileOriginUrl, FileOriginReferrerUrl, WarnedFileName, DeviceName, SHA1, WarnedSHA1, Timestamp
| distinct *

Просмотреть файл

@ -9,28 +9,28 @@
// For this event, RemoteUrl contains the opened URL.
let minTimeRange = ago(7d);
let outlookLinks =
MiscEvents
DeviceEvents
// Filter on click on links from outlook
| where EventTime > minTimeRange and ActionType == "BrowserLaunchedToOpenUrl" and isnotempty(RemoteUrl)
| where Timestamp > minTimeRange and ActionType == "BrowserLaunchedToOpenUrl" and isnotempty(RemoteUrl)
| where
// outlook.exe is the Office Outlook app
InitiatingProcessFileName =~ "outlook.exe"
// RuntimeBroker.exe opens links for all apps from the Windows store, including the Windows Mail app (HxOutlook.exe).
// However, it will also include some links opened from other apps.
or InitiatingProcessFileName =~ "runtimebroker.exe"
| project EventTime, MachineId, ComputerName, RemoteUrl, InitiatingProcessFileName, ParsedUrl=parse_url(RemoteUrl)
| project Timestamp, DeviceId, DeviceName, RemoteUrl, InitiatingProcessFileName, ParsedUrl=parse_url(RemoteUrl)
// When applicable, parse the link sent via email from the clicked O365 ATP SafeLink
| extend WasOutlookSafeLink=(tostring(ParsedUrl.Host) endswith "safelinks.protection.outlook.com")
| project EventTime, MachineId, ComputerName, WasOutlookSafeLink, InitiatingProcessFileName,
| project Timestamp, DeviceId, DeviceName, WasOutlookSafeLink, InitiatingProcessFileName,
OpenedLink=iff(WasOutlookSafeLink, url_decode(tostring(ParsedUrl["Query Parameters"]["url"])), RemoteUrl);
let alerts =
AlertEvents
| summarize (FirstDetectedActivity, Title)=argmin(EventTime, Title) by AlertId, MachineId
DeviceAlertEvents
| summarize (FirstDetectedActivity, Title)=argmin(Timestamp, Title) by AlertId, DeviceId
// Filter alerts that include events from before the queried time period
| where FirstDetectedActivity > minTimeRange;
// Join the two together - looking for alerts that are right after an abnormal network logon
alerts | join kind=inner (outlookLinks) on MachineId | where FirstDetectedActivity - EventTime between (0min..3min)
alerts | join kind=inner (outlookLinks) on DeviceId | where FirstDetectedActivity - Timestamp between (0min..3min)
// If there are multiple alerts close to a single click-on-link, aggregate them together to a single row
// Note: bin(EventTime, 1tick) is used because when summarizing by a datetime field, the default "bin" used is 1-hour.
| summarize FirstDetectedActivity=min(FirstDetectedActivity), AlertTitles=makeset(Title) by OpenedLink, InitiatingProcessFileName, EventTime=bin(EventTime, 1tick), ComputerName, MachineId, WasOutlookSafeLink
// Note: bin(Timestamp, 1tick) is used because when summarizing by a datetime field, the default "bin" used is 1-hour.
| summarize FirstDetectedActivity=min(FirstDetectedActivity), AlertTitles=makeset(Title) by OpenedLink, InitiatingProcessFileName, Timestamp=bin(Timestamp, 1tick), DeviceName, DeviceId, WasOutlookSafeLink

Просмотреть файл

@ -2,9 +2,9 @@
// To learn more about the download URL info that is available and see other sample queries,
// check out this blog post: https://techcommunity.microsoft.com/t5/Threat-Intelligence/Hunting-tip-of-the-month-Browser-downloads/td-p/220454
let detectedDownloads =
MiscEvents
DeviceEvents
| where ActionType == "AntivirusDetection" and isnotempty(FileOriginUrl)
| project EventTime, FileOriginUrl, FileName, MachineId,
| project Timestamp, FileOriginUrl, FileName, DeviceId,
ThreatName=tostring(parse_json(AdditionalFields).ThreatName)
// Filter out less severe threat categories on which we do not want to pivot
| where ThreatName !startswith "PUA"
@ -19,21 +19,21 @@ let detectedDownloadsSummary =
ThreatNames=makeset(ThreatName, 4)
by Host=tostring(parse_url(FileOriginUrl).Host);
// Query for downloads from sites from which other downloads were detected by Windows Defender Antivirus
FileCreationEvents
DeviceFileEvents
| where isnotempty(FileOriginUrl)
| project FileName, FileOriginUrl, MachineId, EventTime,
| project FileName, FileOriginUrl, DeviceId, Timestamp,
Host=tostring(parse_url(FileOriginUrl).Host)
// Filter downloads from hosts serving detected files
| join kind=inner(detectedDownloadsSummary) on Host
// Filter out download file create events that were also detected.
// This is needed because sometimes both of these events will be reported,
// and sometimes only the AntivirusDetection event - depending on timing.
| join kind=leftanti(detectedDownloads) on MachineId, FileOriginUrl
| join kind=leftanti(detectedDownloads) on DeviceId, FileOriginUrl
// Summarize a single row per host - with the machines count
// and an example event for a missed download (select the last event)
| summarize MachineCount=dcount(MachineId), arg_max(EventTime, *) by Host
| summarize MachineCount=dcount(DeviceId), arg_max(Timestamp, *) by Host
// Filter out common hosts, as they probably ones that also serve benign files
| where MachineCount < 20
| project Host, MachineCount, MachineId, FileName, DetectedFiles,
FileOriginUrl, DetectedUrl, ThreatNames, EventTime, SHA1
| project Host, MachineCount, DeviceId, FileName, DetectedFiles,
FileOriginUrl, DetectedUrl, ThreatNames, Timestamp, SHA1
| order by MachineCount desc

Просмотреть файл

@ -1,9 +1,9 @@
// Looking for high volume queries against a given RemoteIP, per ComputerName, RemotePort and Process
// Please change the EventTime window according your preference/objective, as also the subnet ranges that you want to analyze against
// Looking for high volume queries against a given RemoteIP, per DeviceName, RemotePort and Process
// Please change the Timestamp window according your preference/objective, as also the subnet ranges that you want to analyze against
let remotePortCountThreshold = 10; // Please change the min value, for a host reaching out to remote ports on a remote IP, that you consider to be threshold for a suspicious behavior
NetworkCommunicationEvents
| where EventTime > ago(1d) and RemoteIP startswith "172.16" or RemoteIP startswith "192.168"
DeviceNetworkEvents
| where Timestamp > ago(1d) and RemoteIP startswith "172.16" or RemoteIP startswith "192.168"
| summarize
by ComputerName, RemoteIP, RemotePort, InitiatingProcessFileName
| summarize RemotePortCount=dcount(RemotePort) by ComputerName, RemoteIP, InitiatingProcessFileName
by DeviceName, RemoteIP, RemotePort, InitiatingProcessFileName
| summarize RemotePortCount=dcount(RemotePort) by DeviceName, RemoteIP, InitiatingProcessFileName
| where RemotePortCount > remotePortCountThreshold

Просмотреть файл

@ -1,8 +1,8 @@
// The query finds attempts to list users or groups using Net commands
ProcessCreationEvents
| where EventTime > ago(14d)
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName == 'net.exe' and AccountName != "" and ProcessCommandLine !contains '\\' and ProcessCommandLine !contains '/add'
| where (ProcessCommandLine contains ' user ' or ProcessCommandLine contains ' group ') and (ProcessCommandLine endswith ' /do' or ProcessCommandLine endswith ' /domain')
| extend Target = extract("(?i)[user|group] (\"*[a-zA-Z0-9-_ ]+\"*)", 1, ProcessCommandLine) | filter Target != ''
| project AccountName, Target, ProcessCommandLine, ComputerName, EventTime
| project AccountName, Target, ProcessCommandLine, DeviceName, Timestamp
| sort by AccountName, Target

Просмотреть файл

@ -1,14 +1,14 @@
// Query for processes that accessed more than 10 IP addresses over port 445 (SMB) - possibly scanning for network shares.
// To read more about Network Share Discovery, see: https://attack.mitre.org/wiki/Technique/T1135
// Tags: #SMB, #NetworkScanning, #UniqueProcessId
NetworkCommunicationEvents
| where RemotePort == 445 and EventTime > ago(7d)
DeviceNetworkEvents
| where RemotePort == 445 and Timestamp > ago(7d)
// Exclude Kernel processes, as they are too noisy in this query
and InitiatingProcessId !in (0, 4)
| summarize RemoteIPCount=dcount(RemoteIP) by ComputerName, InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime
| summarize RemoteIPCount=dcount(RemoteIP) by DeviceName, InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime
| where RemoteIPCount > 10
// Implementation comment:
// Process IDs are recycled and reused, so are not a unique identifier for a process.
// For this reason we use a combination of ProcessId and ProcessCreationTime together with the ComputerName or MachineId.
// For this reason we use a combination of ProcessId and ProcessCreationTime together with the DeviceName or DeviceId.
// Read more here: https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-atp/advanced-hunting-best-practices-windows-defender-advanced-threat-protection

Просмотреть файл

@ -2,8 +2,8 @@
// Please note that in line #7 it filters RemoteUrl using has operator, which looks for a "whole term" and runs faster.
// Example: RemoteUrl has "microsoft" matches "www.microsoft.com" but not "microsoftonline.com"
let partialRemoteUrlToDetect = "microsoft.com"; // Change this to a URL you'd like to find machines connecting to
NetworkCommunicationEvents
| where EventTime > ago(7d)
DeviceNetworkEvents
| where Timestamp > ago(7d)
and RemoteUrl has partialRemoteUrlToDetect // Can be changed to "contains" operator as explained above
| project EventTime, ComputerName, MachineId, ReportId
| top 100 by EventTime desc
| project Timestamp, DeviceName, DeviceId, ReportId
| top 100 by Timestamp desc

Просмотреть файл

@ -1,6 +1,6 @@
// Finding base64 encoded PE files header seen in the command line parameters
// Tags: #fileLess #powershell
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine contains "TVqQAAMAAAAEAAA"
| top 1000 by EventTime
| top 1000 by Timestamp

Просмотреть файл

@ -3,13 +3,13 @@
// The first and second ProcessCommandLine component is looking for Python decoding base64
// The third ProcesssCommandLine component is looking for the Bash/sh commandline base64 decoding tool
// The fourth one is looking for Ruby decoding base64
ProcessCreationEvents
| where EventTime > ago(14d)
DeviceProcessEvents
| where Timestamp > ago(14d)
| where ProcessCommandLine contains ".decode('base64')"
or ProcessCommandLine contains ".b64decode("
or ProcessCommandLine contains "base64 --decode"
or ProcessCommandLine contains ".decode64("
| project EventTime , ComputerName , FileName , FolderPath , ProcessCommandLine , InitiatingProcessCommandLine
| top 100 by EventTime
| project Timestamp , DeviceName , FileName , FolderPath , ProcessCommandLine , InitiatingProcessCommandLine
| top 100 by Timestamp

Просмотреть файл

@ -1,8 +1,8 @@
// Finding attackers hiding malware in the recycle bin.
// Read more here: https://azure.microsoft.com/en-us/blog/how-azure-security-center-helps-reveal-a-cyberattack/
// Tags: #execution #SuspiciousPath
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~('cmd.exe','ftp.exe','schtasks.exe','powershell.exe','rundll32.exe','regsvr32.exe','msiexec.exe')
| where ProcessCommandLine contains ":\\recycler"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName
| project Timestamp, DeviceName, ProcessCommandLine, InitiatingProcessFileName

Просмотреть файл

@ -1,6 +1,6 @@
// Finds PowerShell execution events that could involve a download.
ProcessCreationEvents
| where EventTime > ago(7d)
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe", "powershell_ise.exe")
| where ProcessCommandLine has "Net.WebClient"
or ProcessCommandLine has "DownloadFile"
@ -8,5 +8,5 @@ ProcessCreationEvents
or ProcessCommandLine has "Invoke-Shellcode"
or ProcessCommandLine contains "http:"
or ProcessCommandLine has "IEX"
| project EventTime, ComputerName, InitiatingProcessFileName, FileName, ProcessCommandLine
| top 100 by EventTime
| project Timestamp, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine
| top 100 by Timestamp

Просмотреть файл

@ -1,18 +1,18 @@
// Find which uncommon Powershell Cmdlets were executed on that machine in a certain time period.
// This covers all Powershell commands executed in the Powershell engine by any process.
let machineId = "474908f457a1dc4c1fab568f808d5f77bf3bb951";
let DeviceId = "474908f457a1dc4c1fab568f808d5f77bf3bb951";
let timestamp = datetime(2018-06-09T02:23:26.6832917Z);
// Query for Powershell cmdlets
let powershellCommands =
MiscEvents
DeviceEvents
| where ActionType == "PowerShellCommand"
// Extract the powershell command name from the Command field in the AdditionalFields JSON column
| project PowershellCommand=extractjson("$.Command", AdditionalFields, typeof(string)), InitiatingProcessCommandLine, InitiatingProcessParentFileName, EventTime, MachineId
| project PowershellCommand=extractjson("$.Command", AdditionalFields, typeof(string)), InitiatingProcessCommandLine, InitiatingProcessParentFileName, Timestamp, DeviceId
| where PowershellCommand !endswith ".ps1" and PowershellCommand !endswith ".exe";
// Filter Powershell cmdlets executed on relevant machine and time period
powershellCommands | where MachineId == machineId and EventTime between ((timestamp-5min) .. 10min)
powershellCommands | where DeviceId == DeviceId and Timestamp between ((timestamp-5min) .. 10min)
// Filter out common powershell cmdlets
| join kind=leftanti (powershellCommands | summarize MachineCount=dcount(MachineId) by PowershellCommand | where MachineCount > 20) on PowershellCommand
| join kind=leftanti (powershellCommands | summarize MachineCount=dcount(DeviceId) by PowershellCommand | where MachineCount > 20) on PowershellCommand
// To learn more about queries on Powershell commands, take a look this post: https://techcommunity.microsoft.com/t5/Threat-Intelligence/Hunting-tip-of-the-month-PowerShell-commands/m-p/210898#M30
// Related queries:

Просмотреть файл

@ -1,12 +1,12 @@
// Find all machines running a given Powersehll cmdlet.
// This covers all Powershell commands executed in the Powershell engine by any process.
let powershellCommandName = "Invoke-RickAscii";
MiscEvents
DeviceEvents
| where ActionType == "PowerShellCommand"
// This filter improves query performance, as it avoids needing to parse Command from all rows and only then applying a filter
| where AdditionalFields contains powershellCommandName
// Extract the powershell command name from the Command field in the AdditionalFields JSON column
| project PowershellCommand=extractjson("$.Command", AdditionalFields, typeof(string)), InitiatingProcessCommandLine, InitiatingProcessParentFileName, EventTime, MachineId
| project PowershellCommand=extractjson("$.Command", AdditionalFields, typeof(string)), InitiatingProcessCommandLine, InitiatingProcessParentFileName, Timestamp, DeviceId
// Do an exact case-insensitive match on the command name field
| where PowershellCommand =~ powershellCommandName

Просмотреть файл

@ -2,10 +2,10 @@
// read more online on event 6416: https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-6416
// Query #1: look for rare one-time devices connected to a specific machine
let computerNameParam = "<replace this with full computer name>";
let DeviceNameParam = "<replace this with full computer name>";
// Query for device connection events
let devices =
MiscEvents
DeviceEvents
| where ActionType == "PnpDeviceConnected"
| extend parsed=parse_json(AdditionalFields)
| project
@ -13,11 +13,11 @@ let devices =
ClassName=tostring(parsed.ClassName),
DeviceId=tostring(parsed.VendorIds),
VendorIds=tostring(parsed.VendorIds),
ComputerName, EventTime ;
DeviceName, Timestamp ;
// Filter devices seen on the suspected machine
devices | where ComputerName == computerNameParam
devices | where DeviceName == DeviceNameParam
// Get some stats on the device connections to that machine
| summarize TimesConnected=count(), FirstTime=min(EventTime), LastTime=max(EventTime) by DeviceId, DeviceDescription, ClassName, VendorIds, ComputerName
| summarize TimesConnected=count(), FirstTime=min(Timestamp), LastTime=max(Timestamp) by DeviceId, DeviceDescription, ClassName, VendorIds, DeviceName
// Optional filter - looking for devices used in only within 24h
| where LastTime - FirstTime < 1d
// Filter out (antijoin) devices that are common in the organization.
@ -25,12 +25,12 @@ devices | where ComputerName == computerNameParam
// So, a specific disk-on-key device which model is common in the org will still be shown in the results,
// while built-in software devices (often have constant device ID) as well as common network devices (e.g. printer queues) will be excluded.
| join kind=leftanti
(devices | summarize Machines=dcount(ComputerName) by DeviceId, DeviceDescription, VendorIds | where Machines > 5)
(devices | summarize Machines=dcount(DeviceName) by DeviceId, DeviceDescription, VendorIds | where Machines > 5)
on DeviceId, DeviceDescription, VendorIds
// Query #2: map uncommon storage devices across the org
// This is a noisy query - but it can serve as reference for working with this event
MiscEvents
DeviceEvents
| where ActionType == "PnpDeviceConnected"
| extend parsed=parse_json(AdditionalFields)
| extend
@ -41,5 +41,5 @@ MiscEvents
or ClassName contains "nas"
or ClassName contains "SCSI"
or (ClassName == "USB" and DeviceDescription contains "storage")
| summarize ComputerCount=dcount(ComputerName) by ClassName, DeviceDescription
| summarize ComputerCount=dcount(DeviceName) by ClassName, DeviceDescription
| where ComputerCount < 5

Просмотреть файл

@ -2,8 +2,8 @@
// Search for persistence in Statup folder that's done by Adobe Acrobat Reader.
// Normally, this behavior is not expected.
FileCreationEvents
DeviceFileEvents
| where
InitiatingProcessFileName =~ "acrord32.exe"
and FolderPath contains "\\Start Menu\\Programs\\Startup"
| project FolderPath, ComputerName, EventTime, FileName, InitiatingProcessCommandLine, SHA1
| project FolderPath, DeviceName, Timestamp, FileName, InitiatingProcessCommandLine, SHA1

Просмотреть файл

@ -12,9 +12,9 @@
//
// Tags: #exploit #CVE-2018-1000006 #Electron
ProcessCreationEvents
| where EventTime > ago(14d)
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName in~ ("code.exe", "skype.exe", "slack.exe", "teams.exe")
| where InitiatingProcessFileName in~ ("iexplore.exe", "runtimebroker.exe", "chrome.exe")
| where ProcessCommandLine has "--gpu-launcher"
| summarize FirstEvent=min(EventTime), LastEvent=max(EventTime) by ComputerName, ProcessCommandLine, FileName, InitiatingProcessFileName
| summarize FirstEvent=min(Timestamp), LastEvent=max(Timestamp) by DeviceName, ProcessCommandLine, FileName, InitiatingProcessFileName

Просмотреть файл

@ -9,9 +9,9 @@
//
// Tags: #exploit #CVE-2018-4878 #0day #Korea #Flash
NetworkCommunicationEvents
| where EventTime > ago(14d)
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName =~ "cmd.exe" and InitiatingProcessParentFileName =~ "excel.exe"
| where RemoteUrl endswith ".kr"
| project EventTime, ComputerName, RemoteIP, RemoteUrl
| top 100 by EventTime
| project Timestamp, DeviceName, RemoteIP, RemoteUrl
| top 100 by Timestamp

Просмотреть файл

@ -10,9 +10,9 @@
//
// Tags: #exploit #CVE-2018-1111 #DynoRoot
ProcessCreationEvents
DeviceProcessEvents
| where InitiatingProcessCommandLine contains "/etc/NetworkManager/dispatcher.d/"
and InitiatingProcessCommandLine contains "-dhclient"
and isnotempty(ProcessCommandLine)
and FileName !endswith ".exe"
| project EventTime, ComputerName , FileName, ProcessCommandLine, InitiatingProcessCommandLine
| project Timestamp, DeviceName , FileName, ProcessCommandLine, InitiatingProcessCommandLine

Просмотреть файл

@ -4,7 +4,7 @@
// You might be amused by the results, or perhaps angry if one of your systems or scripts was broken by this...
// Note: this query will also return some machines with non-English charcters that are not Emojis
// Credit for this query goes to miflower - thanks for bringing joy to our lives! :)
ProcessCreationEvents
| distinct ComputerName
| extend fakeescape=replace("%5f", "_", replace("%2d", "-", url_encode(ComputerName)))
| where fakeescape != ComputerName
DeviceProcessEvents
| distinct DeviceName
| extend fakeescape=replace("%5f", "_", replace("%2d", "-", url_encode(DeviceName)))
| where fakeescape != DeviceName

Просмотреть файл

@ -1,17 +1,17 @@
// Determines MachineId from internal IP address and outputs all alerts in events table associated to the MachineId
// Determines DeviceId from internal IP address and outputs all alerts in events table associated to the DeviceId
// Example use case is Firewall determines Internal IP with suspicious network activity. Query WDATP based on date/time and Internal IP and see associated alerts for the endpoint.
let PivotTime = datetime(2018-08-02 20:57:02); //Fill out time
let TimeRangeStart = PivotTime-15m; // 15 Minutes Prior to Pivot Time
let TimeRangeEnd = PivotTime+15m; // 15 Minutes After Pivot Time
let IPAddress = "10.0.0.5"; // internal IP address to search
// Locate MachineIDs associated with IP
let FindMachineIDbyIP = MachineNetworkInfo
| where EventTime between ((TimeRangeStart) ..TimeRangeEnd)
// Locate DeviceIds associated with IP
let FindDeviceIdbyIP = DeviceNetworkInfo
| where Timestamp between ((TimeRangeStart) ..TimeRangeEnd)
and IPAddresses contains strcat("\"", IPAddress, "\"")
and NetworkAdapterStatus == "Up"
| project ComputerName, MachineId, EventTime, IPAddresses;
// Query Alerts matching MachineIDs
AlertEvents
| join kind = leftsemi FindMachineIDbyIP on MachineId
| project DeviceName, DeviceId, Timestamp, IPAddresses;
// Query Alerts matching DeviceIds
DeviceAlertEvents
| join kind = leftsemi FindDeviceIdbyIP on DeviceId
// Summarizes alerts by AlertId with min and max event times
| summarize Title=any(Title), min(EventTime), max(EventTime), ComputerName=any(ComputerName) by AlertId
| summarize Title=any(Title), min(Timestamp), max(Timestamp), DeviceName=any(DeviceName) by AlertId

Просмотреть файл

@ -5,22 +5,22 @@
// It brings deltas between a baseline and another machine quickly to the analyst's view
// This query supports multiple suspected bad machines and multiple "known good" machines
// It also supports providing a timeframe for how far back in time to build a baseline as well as how far back in time to evaluate the suspected bad machines
// Each of the links provided by machineid/computername will go to the most recent entry for whatever entity is listed
// Each of the links provided by DeviceId/DeviceName will go to the most recent entry for whatever entity is listed
// Average results for the pre-defined settings below with a single good host and a single bad host on a 'huge' tenant (300k+ machines):
// Compute Time: ~10-20 seconds
// Result Set Size: ~500 rows
// The workflow is as follows:
// 1. Establish Variables that are editable on a per-query basis
// 2. Define functions for reuse
// 3. Calculate MachineIds for all machines in scope
// 3. Calculate DeviceIds for all machines in scope
// 4. Derive deltas using the aforementioned functions
// 5. Union together all results into a single view
// The following datasets are returned:
// 1. Alerts on the suspected bad machines (ignores known good machines, because...they're alerts, additional data has the triggered file)
// 2. Connected Networks (from MachineNetworkInfo table, additional data has full Connected Network details)
// 2. Connected Networks (from DeviceNetworkInfo table, additional data has full Connected Network details)
// 3. File Creations (disabled by default due to volume, enable at your own risk, additional data has initiating processes)
// 4. Image Loads (disabled by default due to volume, enable at your own risk, additional data has initiating processes)
// 5. Logon (derived from LogonEvents for the unique users logged on, additional data has logon types)
// 5. Logon (derived from DeviceLogonEvents for the unique users logged on, additional data has logon types)
// 6. Network communication (grouped by 2nd level-domain, ie 'microsoft.com' in 'www.microsoft.com' and 'web.microsoft.com', additional data has the full list of URLs)
// 7. Process creation (additional data has the full paths of the files)
// 8. Powershell Commands (grouped by the cmdlet that was ran, additional data has the processes that ran the cmdlet)
@ -50,213 +50,213 @@ let ReturnSets=pack_array(
);
// -------------End of variables, changing below this line will change query logic----------
// Function to get a mapping of machine IDs given a list of computer names
let GetMachineId=(InComputerName: dynamic) {
MachineInfo
| where ComputerName in~ (InComputerName)
| distinct ComputerName, MachineId
let GetDeviceId=(InDeviceName: dynamic) {
DeviceInfo
| where DeviceName in~ (InDeviceName)
| distinct DeviceName, DeviceId
};
// Function to consolidate all machine IDs into a single set
let ConsolidateMachineId=(T:(MachineId: string)) {
let ConsolidateDeviceId=(T:(DeviceId: string)) {
T
| summarize makeset(MachineId)
| summarize makeset(DeviceId)
};
// Function to get network communications given a list of computer names and how far back to look
let GetNetworkEvents=(InMachineId: dynamic, LeftEventTime: datetime) {
NetworkCommunicationEvents
let GetNetworkEvents=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceNetworkEvents
| where "Network Communication" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
| where isnotempty(RemoteUrl)
| summarize EventTime=max(EventTime), count() by RemoteUrl, MachineId
| summarize Timestamp=max(Timestamp), count() by RemoteUrl, DeviceId
| extend UrlSplit=split(RemoteUrl, ".") // Split the levels of the URL
// If there is only one level (for an internal communication that uses your DNS search suffix), then only use that level
// Otherwise combine the top two levels and use those as the URLRoot
| extend UrlRoot=iff(UrlSplit[-2] == "", UrlSplit[0], strcat(tostring(UrlSplit[-2]), ".", tostring(UrlSplit[-1])))
| summarize EventTime=max(EventTime), Count=sum(count_), AdditionalData=makeset(RemoteUrl, 5) by UrlRoot, MachineId
| project EventTime, Entity=UrlRoot, Count, AdditionalData=tostring(AdditionalData), MachineId, DataType="Network Communication"
| summarize Timestamp=max(Timestamp), Count=sum(count_), AdditionalData=makeset(RemoteUrl, 5) by UrlRoot, DeviceId
| project Timestamp, Entity=UrlRoot, Count, AdditionalData=tostring(AdditionalData), DeviceId, DataType="Network Communication"
};
// Function to get process creates given a list of computer names and how far back to look
let GetProcessCreates=(InMachineId: dynamic, LeftEventTime: datetime) {
ProcessCreationEvents
let GetProcessCreates=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceProcessEvents
| where "Process Creation" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
// Replace known path for mpam files as they are dynamically named and likely to be unique on each machine
| extend FileName=iff(FolderPath matches regex @"([A-Z]:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\Temp\\mpam-)[a-z0-9]{7,8}\.exe", "mpam-RANDOM.exe", FileName)
// Replace known path for AM delta patch files as they jump frequently and not likely to be exact on each machine
| extend FileName=iff(FolderPath matches regex @"([A-Z]:\\Windows\\SoftwareDistribution\\Download\\Install\\AM_Delta_Patch_)[0-9\.]+\.exe", "AM_Delta_Patch_Version.exe", FileName)
| summarize EventTime=max(EventTime), Count=count(), AdditionalData=makeset(FolderPath) by FileName, MachineId
| summarize Timestamp=max(Timestamp), Count=count(), AdditionalData=makeset(FolderPath) by FileName, DeviceId
// Replace various mbam executables that are semiunique-generated with some text to help reduce noise
| project EventTime, Entity=FileName, Count, AdditionalData=tostring(AdditionalData), MachineId, DataType="Process Creation"
| project Timestamp, Entity=FileName, Count, AdditionalData=tostring(AdditionalData), DeviceId, DataType="Process Creation"
};
// Function to get powershell commands given a list of computer names and how far back to look
let GetPSCommands=(InMachineId: dynamic, LeftEventTime: datetime) {
MiscEvents
let GetPSCommands=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceEvents
| where "PowerShell Command" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
| where ActionType == 'PowerShellCommand'
// Remove two different signatures for scripts being executed which cause a lot of noise
// The first signature matches scripts generated as part of testing execution policy
// The second signature matches scripts generated by SCCM
| where not(AdditionalFields matches regex @"Script_[0-9a-f]{20}" and InitiatingProcessFileName =~ 'monitoringhost.exe')
| where not(AdditionalFields matches regex @"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.ps1" and InitiatingProcessFileName =~ 'powershell.exe')
| summarize EventTime=max(EventTime), count(), IPFN_Set=makeset(InitiatingProcessFileName) by AdditionalFields, MachineId
| project EventTime, Entity=tostring(extractjson("$.Command", AdditionalFields)), Count=count_, AdditionalData=tostring(IPFN_Set), MachineId, DataType="PowerShell Command"
| summarize Timestamp=max(Timestamp), count(), IPFN_Set=makeset(InitiatingProcessFileName) by AdditionalFields, DeviceId
| project Timestamp, Entity=tostring(extractjson("$.Command", AdditionalFields)), Count=count_, AdditionalData=tostring(IPFN_Set), DeviceId, DataType="PowerShell Command"
};
// Function to get file creations given a list of computer names and how far back to look
let GetFileCreates=(InMachineId: dynamic, LeftEventTime: datetime) {
FileCreationEvents
let GetFileCreates=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceFileEvents
| where "File Creation" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
// Remove temporary files created by office products
| where not(FileName matches regex @"~.*\.(doc[xm]?|ppt[xm]?|xls[xm]?|dotm|rtf|xlam|lnk)")
// Replace two different signatures for PS scripts being created which cause a lot of noise
| extend iff(FileName matches regex @"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.ps1" or
FileName matches regex @"[0-9a-z]{8}\.[0-9a-z]{3}\.ps1", "RANDOM.ps1", FileName)
| summarize EventTime=max(EventTime), FP_Set=makeset(FolderPath), count() by FileName, MachineId
| project EventTime, Entity=FileName, Count=count_, AdditionalData=tostring(FP_Set), MachineId, DataType="File Creation"
| summarize Timestamp=max(Timestamp), FP_Set=makeset(FolderPath), count() by FileName, DeviceId
| project Timestamp, Entity=FileName, Count=count_, AdditionalData=tostring(FP_Set), DeviceId, DataType="File Creation"
};
// Function to get logon events given a list of computer names and how far back to look
let GetLogonEvents=(InMachineId: dynamic, LeftEventTime: datetime) {
LogonEvents
let GetDeviceLogonEvents=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceLogonEvents
| where "Logon" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
// Remove logons made by WDM or UMFD
| where AccountDomain !in ('font driver host', 'window manager')
| summarize EventTime=max(EventTime), Count=count(), LT_Set=makeset(LogonType) by AccountName, AccountDomain, MachineId
| project EventTime, Entity=iff(AccountDomain == "", AccountName, strcat(AccountDomain, @"\", AccountName)), Count, AdditionalData=tostring(LT_Set), MachineId, DataType="Logon"
| summarize Timestamp=max(Timestamp), Count=count(), LT_Set=makeset(LogonType) by AccountName, AccountDomain, DeviceId
| project Timestamp, Entity=iff(AccountDomain == "", AccountName, strcat(AccountDomain, @"\", AccountName)), Count, AdditionalData=tostring(LT_Set), DeviceId, DataType="Logon"
};
// Function to get registry events given a list of computer names and how far back to look
let GetRegistryEvents=(InMachineId: dynamic, LeftEventTime: datetime) {
RegistryEvents
let GetDeviceRegistryEvents=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceRegistryEvents
| where "Registry Event" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
| extend RegistryKey=iff(RegistryKey matches regex @"HKEY_CURRENT_USER\\S-[^\\]+\\", replace(@"(HKEY_CURRENT_USER\\)S-[^\\]+\\", @"\1SID\\", RegistryKey), RegistryKey)
| summarize EventTime=max(EventTime), RVD_Set=makeset(RegistryValueData), Count=count() by MachineId, RegistryKey
| project EventTime, Entity=RegistryKey, Count, AdditionalData=tostring(RVD_Set), MachineId, DataType="Registry Event"
| summarize Timestamp=max(Timestamp), RVD_Set=makeset(RegistryValueData), Count=count() by DeviceId, RegistryKey
| project Timestamp, Entity=RegistryKey, Count, AdditionalData=tostring(RVD_Set), DeviceId, DataType="Registry Event"
};
// Function to get connected networks given a list of computer names and how far back to look
let GetConnectedNetworks=(InMachineId: dynamic, LeftEventTime: datetime) {
MachineNetworkInfo
let GetConnectedNetworks=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceNetworkInfo
| where "Connected Networks" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| summarize EventTime=max(EventTime), Count=count() by MachineId, ConnectedNetworks
| project EventTime, Entity=tostring(extractjson("$[0].Name", ConnectedNetworks)), Count, AdditionalData=ConnectedNetworks, MachineId, DataType="Connected Networks"
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
| summarize Timestamp=max(Timestamp), Count=count() by DeviceId, ConnectedNetworks
| project Timestamp, Entity=tostring(extractjson("$[0].Name", ConnectedNetworks)), Count, AdditionalData=ConnectedNetworks, DeviceId, DataType="Connected Networks"
};
// Function to get image load events given a list of computer names and how far back to look
let GetImageLoads=(InMachineId: dynamic, LeftEventTime: datetime) {
ImageLoadEvents
let GetImageLoads=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceImageLoadEvents
| where "Image Loads" in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| summarize EventTime=max(EventTime), Set_FN=makeset(InitiatingProcessFileName), Count=count() by MachineId, FolderPath
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
| summarize Timestamp=max(Timestamp), Set_FN=makeset(InitiatingProcessFileName), Count=count() by DeviceId, FolderPath
// Replace various native windows DLL's that are guid-generated with some text to help reduce noise
| extend Entity=replace(@"([wW]indows\\assembly\\NativeImages.*\\)[0-9a-f]{32}", @"\1GUID", FolderPath)
| project EventTime, Entity, Count, AdditionalData=tostring(Set_FN), MachineId, DataType="Image Loads"
| project Timestamp, Entity, Count, AdditionalData=tostring(Set_FN), DeviceId, DataType="Image Loads"
};
// Function to get raw IP address network communications given a list of computer names and how far back to look
let GetRawIPCommunications=(InMachineId: dynamic, LeftEventTime: datetime) {
NetworkCommunicationEvents
let GetRawIPCommunications=(InDeviceId: dynamic, LeftTimestamp: datetime) {
DeviceNetworkEvents
| where 'Raw IP Communication' in (ReturnSets)
| where EventTime > LeftEventTime
| where MachineId in~ (InMachineId)
| where Timestamp > LeftTimestamp
| where DeviceId in~ (InDeviceId)
// Replace all v4 to v6 addresses with their v4 equivalent
| extend RemoteIP=replace("^::ffff:", "", RemoteIP)
| summarize EventTime=max(EventTime), Set_RPort=makeset(RemotePort), Set_LPort=makeset(LocalPort), Set_FN=makeset(InitiatingProcessFileName), Set_URL=makeset(RemoteUrl), Count=count() by MachineId, RemoteIP
| summarize Timestamp=max(Timestamp), Set_RPort=makeset(RemotePort), Set_LPort=makeset(LocalPort), Set_FN=makeset(InitiatingProcessFileName), Set_URL=makeset(RemoteUrl), Count=count() by DeviceId, RemoteIP
// Only include any IP addresses that do not have a resolved URL as resolved URLs are handled in network communications
| where tostring(Set_URL) == '[""]'
// Do not include machines that are only doing WUDO
| where tostring(Set_RPort) != '[7680]' and tostring(Set_RPort) != '[7680]'
| project EventTime, Entity=RemoteIP, Count, AdditionalData=tostring(Set_FN), MachineId, DataType='Raw IP Communication'
| project Timestamp, Entity=RemoteIP, Count, AdditionalData=tostring(Set_FN), DeviceId, DataType='Raw IP Communication'
};
// Calculate the left event time for "good" machines
let GoodLeftEventTime=ago(GoodTimeRange);
let GoodLeftTimestamp=ago(GoodTimeRange);
// Calculate the left event time for suspected bad machines
let SuspectedBadLeftEventTime=ago(SuspectedBadTimeRange);
let SuspectedBadLeftTimestamp=ago(SuspectedBadTimeRange);
// Calculate the machine IDs for "good" machines
let GoodHostNameMapping=GetMachineId(GoodHosts);
let GoodHostNameMapping=GetDeviceId(GoodHosts);
// Reduce all of the good machine IDs into a single variable
let GoodHostMachineId=toscalar(ConsolidateMachineId(GoodHostNameMapping));
let GoodHostDeviceId=toscalar(ConsolidateDeviceId(GoodHostNameMapping));
// Calculate the machine IDs for suspected bad machines
let SuspectedBadHostNameMapping=GetMachineId(SuspectedBadHosts);
let SuspectedBadHostNameMapping=GetDeviceId(SuspectedBadHosts);
// Reduce all of the suspected bad machine IDs into a single variable
let SuspectedBadHostMachineId=toscalar(ConsolidateMachineId(SuspectedBadHostNameMapping));
let SuspectedBadHostDeviceId=toscalar(ConsolidateDeviceId(SuspectedBadHostNameMapping));
// Calculate the delta in network events, keeping the bad ones
let NetworkDelta=GetNetworkEvents(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let NetworkDelta=GetNetworkEvents(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetNetworkEvents(GoodHostMachineId, GoodLeftEventTime)
GetNetworkEvents(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in process create events, keeping the bad ones
let ProcessDelta=GetProcessCreates(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let ProcessDelta=GetProcessCreates(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetProcessCreates(GoodHostMachineId, GoodLeftEventTime)
GetProcessCreates(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in powershell events, keeping the bad ones
let PSDelta=GetPSCommands(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let PSDelta=GetPSCommands(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetPSCommands(GoodHostMachineId, GoodLeftEventTime)
GetPSCommands(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in file create events, keeping the bad ones
let FileDelta=GetFileCreates(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let FileDelta=GetFileCreates(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetFileCreates(GoodHostMachineId, GoodLeftEventTime)
GetFileCreates(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in logon events, keeping the bad ones
let LogonDelta=GetLogonEvents(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let LogonDelta=GetDeviceLogonEvents(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetLogonEvents(GoodHostMachineId, GoodLeftEventTime)
GetDeviceLogonEvents(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in registry events, keeping the bad ones
let RegistryDelta=GetRegistryEvents(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let RegistryDelta=GetDeviceRegistryEvents(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetRegistryEvents(GoodHostMachineId, GoodLeftEventTime)
GetDeviceRegistryEvents(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in connected network events, keeping the bad ones
let ConnectedNetworkDelta=GetConnectedNetworks(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let ConnectedNetworkDelta=GetConnectedNetworks(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetConnectedNetworks(GoodHostMachineId, GoodLeftEventTime)
GetConnectedNetworks(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in image load events, keeping the bad ones
let ImageLoadDelta=GetImageLoads(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let ImageLoadDelta=GetImageLoads(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetImageLoads(GoodHostMachineId, GoodLeftEventTime)
GetImageLoads(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Calculate the delta in raw IP address communications, keeping the bad ones
let RawIPCommunicationDelta=GetRawIPCommunications(SuspectedBadHostMachineId, SuspectedBadLeftEventTime)
let RawIPCommunicationDelta=GetRawIPCommunications(SuspectedBadHostDeviceId, SuspectedBadLeftTimestamp)
| join kind=leftanti (
GetRawIPCommunications(GoodHostMachineId, GoodLeftEventTime)
GetRawIPCommunications(GoodHostDeviceId, GoodLeftTimestamp)
) on Entity;
// Get the alerts for the bad machines (no delta, we care about all alerts)
let Alerts=AlertEvents
let Alerts=DeviceAlertEvents
| where "Alert" in (ReturnSets)
| where EventTime > SuspectedBadLeftEventTime
| where MachineId in (SuspectedBadHostMachineId)
| summarize EventTime=max(EventTime), Count=count() by Title, MachineId, FileName, RemoteUrl
| project EventTime, Entity=Title, Count, AdditionalData=coalesce(FileName, RemoteUrl), MachineId, DataType="Alert";
| where Timestamp > SuspectedBadLeftTimestamp
| where DeviceId in (SuspectedBadHostDeviceId)
| summarize Timestamp=max(Timestamp), Count=count() by Title, DeviceId, FileName, RemoteUrl
| project Timestamp, Entity=Title, Count, AdditionalData=coalesce(FileName, RemoteUrl), DeviceId, DataType="Alert";
// String everything together
let ResultDataWithoutMachineCount=union NetworkDelta, ProcessDelta, PSDelta, FileDelta, Alerts, LogonDelta, RegistryDelta,
ConnectedNetworkDelta, ImageLoadDelta, RawIPCommunicationDelta
// Join back against the machine info so the Computer Names can be reassociated
| join kind=leftouter (
SuspectedBadHostNameMapping
) on MachineId
) on DeviceId
// Remove duplicated column
| project-away MachineId1;
| project-away DeviceId1;
// This is the start of the final result set that is shown
// Calculate the number of machines that each entity/datatype pair have and join that data back into the data to add
// an additional column for the number of bad machines
ResultDataWithoutMachineCount
| join kind=leftouter (
ResultDataWithoutMachineCount
| summarize BadMachinesCount=dcount(MachineId) by Entity, DataType
| summarize BadMachinesCount=dcount(DeviceId) by Entity, DataType
) on Entity, DataType
// Remove duplicated columns
| project-away Entity1, DataType1
// and sort by Machine, DataType, Entity
| order by BadMachinesCount desc, MachineId asc, DataType asc, Entity asc
| order by BadMachinesCount desc, DeviceId asc, DataType asc, Entity asc
//| where BadMachinesCount > 1

Просмотреть файл

@ -3,23 +3,23 @@
// This is useful when you have queries that you run often - e.g. as part of your regular investigation of an alert.
// Original query: filter for network logon events right before some timestamp
let machineId = "474908f457a1dc4c1fab568f808d5f77bf3bb951";
let DeviceId = "474908f457a1dc4c1fab568f808d5f77bf3bb951";
let timestamp = datetime(2018-06-09T02:23:26.6832917Z);
let lookupPeriod = 10m;
LogonEvents
| where EventTime between ((timestamp - lookupPeriod) .. lookupPeriod)
and MachineId == machineId
DeviceLogonEvents
| where Timestamp between ((timestamp - lookupPeriod) .. lookupPeriod)
and DeviceId == DeviceId
and LogonType == "Network"
// Modified query: instead of copy-pasting the timestamp, get the timestamp of some event you can filter
// In this example, take the time of the first detected event in an alert.
// We filter on alertId - which you can get from all our APIs (SIEM, Graph API, PowerBI, AlertEvents table) or from the UI (the last part of the link to the alert page)
// We filter on alertId - which you can get from all our APIs (SIEM, Graph API, PowerBI, DeviceAlertEvents table) or from the UI (the last part of the link to the alert page)
let alertId = "636641078490537577_-1905871543";
let alert = AlertEvents | where AlertId == alertId | summarize AlertFirstEventTime=min(EventTime) by MachineId;
let machineId = toscalar(alert | project MachineId);
let timestamp = toscalar(alert | project AlertFirstEventTime);
let alert = DeviceAlertEvents | where AlertId == alertId | summarize AlertFirstTimestamp=min(Timestamp) by DeviceId;
let DeviceId = toscalar(alert | project DeviceId);
let timestamp = toscalar(alert | project AlertFirstTimestamp);
let lookupPeriod = 10m;
LogonEvents
| where EventTime between ((timestamp - lookupPeriod) .. lookupPeriod)
and MachineId == machineId
DeviceLogonEvents
| where Timestamp between ((timestamp - lookupPeriod) .. lookupPeriod)
and DeviceId == DeviceId
and LogonType == "Network"

Просмотреть файл

@ -1,10 +1,10 @@
// Sample query to detect If there are more then 3 failed logon authentications on high value assets.
// Update COMPUTERNAME to reflect your high value assets.
// Update DeviceName to reflect your high value assets.
// For questions @MiladMSFT on Twitter or milad.aslaner@microsoft.com
LogonEvents
| where ComputerName in ("COMPUTERNAME1","COMPUTERNAME2")
DeviceLogonEvents
| where DeviceName in ("DeviceName1","DeviceName2")
| where ActionType == "LogonFailed"
| summarize LogonFailures=count() by ComputerName, LogonType, InitiatingProcessCommandLine
| summarize LogonFailures=count() by DeviceName, LogonType, InitiatingProcessCommandLine
| where LogonFailures > 3
| project LogonFailures, ComputerName, LogonType, InitiatingProcessCommandLine
| project LogonFailures, DeviceName, LogonType, InitiatingProcessCommandLine
| sort by LogonFailures desc

Просмотреть файл

@ -1,22 +1,22 @@
// Query #1 - Find the machines on which this file was seen
// TODO - set file hash to be a SHA1 hash of your choice...
let fileHash = "e152f7ce2d3a4349ac583580c2caf8f72fac16ba";
find in (FileCreationEvents, ProcessCreationEvents, MiscEvents, RegistryEvents, NetworkCommunicationEvents, ImageLoadEvents)
find in (DeviceFileEvents, DeviceProcessEvents, DeviceEvents, DeviceRegistryEvents, DeviceNetworkEvents, DeviceImageLoadEvents)
where SHA1 == fileHash or InitiatingProcessSHA1 == fileHash
project ComputerName, ActionType, FileName, InitiatingProcessFileName, EventTime, SHA1, InitiatingProcessSHA1
| project ComputerName, ActionType, EventTime,
project DeviceName, ActionType, FileName, InitiatingProcessFileName, Timestamp, SHA1, InitiatingProcessSHA1
| project DeviceName, ActionType, Timestamp,
FileName = iff(SHA1 == fileHash, FileName, InitiatingProcessFileName),
MatchedSide=iff(SHA1 == fileHash, iff(InitiatingProcessSHA1 == fileHash, "Both", "Child"), "Parent")
| summarize makeset(ActionType), FirstEventTime=min(EventTime), (LastEventTime, LastActionType)=arg_max(EventTime, ActionType) by FileName, MatchedSide, ComputerName
| top 1000 by LastEventTime desc
| sort by ComputerName, LastEventTime desc
| summarize makeset(ActionType), FirstTimestamp=min(Timestamp), (LastTimestamp, LastActionType)=arg_max(Timestamp, ActionType) by FileName, MatchedSide, DeviceName
| top 1000 by LastTimestamp desc
| sort by DeviceName, LastTimestamp desc
// Query # 2 - Shows you a list of distinct IP addresses and DNS names the endpoint had network communication with through a specific file.
// Use this list to whitelist/blacklist IP addresses or understand if there are communication with IP you are not aware of.
// Update the filename to the name you wish to investigate network communication.
let filename = "FILENAME GOES HERE";
// Builds table for distinct URLs based off filename
NetworkCommunicationEvents
DeviceNetworkEvents
| where InitiatingProcessFileName =~ filename and ( isnotempty(RemoteIP) or isnotempty(RemoteUrl) )
| project DNS=RemoteUrl, IP=RemoteIP
| distinct IP, DNS

Просмотреть файл

@ -4,14 +4,14 @@
// Query #1: get machines that have used a given local IP address at a given time - as configured on their network adapters
let pivotTimeParam = datetime(2018-07-15 19:51:00);
let ipAddressParam = "192.168.1.5";
MachineNetworkInfo
| where EventTime between ((pivotTimeParam-15m) ..30m) and IPAddresses contains strcat("\"", ipAddressParam, "\"") and NetworkAdapterStatus == "Up"
DeviceNetworkInfo
| where Timestamp between ((pivotTimeParam-15m) ..30m) and IPAddresses contains strcat("\"", ipAddressParam, "\"") and NetworkAdapterStatus == "Up"
//// Optional - add filters to make sure machine is part of the relevant network (and not using that IP address as part of another private network).
//// For example:
// and ConnectedNetworks contains "corp.contoso.com"
// and IPv4Dhcp == "10.164.3.12"
// and DefaultGateways contains "\"10.164.3.1\""
| project ComputerName, EventTime, IPAddresses, TimeDifference=abs(EventTime-pivotTimeParam)
| project DeviceName, Timestamp, IPAddresses, TimeDifference=abs(Timestamp-pivotTimeParam)
// In case multiple machines have reported from that IP address arround that time, start with the ones reporting closest to pivotTimeParam
| sort by TimeDifference asc
@ -20,28 +20,28 @@ MachineNetworkInfo
let pivotTimeParam = datetime(2018-07-15 19:51:00);
let ipAddressParam = "192.168.1.5";
let matchingMachines =
MachineNetworkInfo
| where EventTime between ((pivotTimeParam-15m) ..30m) and IPAddresses contains strcat("\"", ipAddressParam, "\"") and NetworkAdapterStatus == "Up"
DeviceNetworkInfo
| where Timestamp between ((pivotTimeParam-15m) ..30m) and IPAddresses contains strcat("\"", ipAddressParam, "\"") and NetworkAdapterStatus == "Up"
//// Optional - add filters to make sure machine is part of the relevant network (and not using that IP address as part of another private network).
//// For example:
// and ConnectedNetworks contains "corp.contoso.com"
// and IPv4Dhcp == "10.164.3.12"
// and DefaultGateways contains "\"10.164.3.1\""
| project ComputerName, EventTime, IPAddresses, TimeDifference=abs(EventTime-pivotTimeParam);
MachineInfo
| where EventTime between ((pivotTimeParam-15m) ..30m)
| project ComputerName, EventTime, LoggedOnUsers
| join kind=inner (matchingMachines) on ComputerName, EventTime
| project EventTime, ComputerName, LoggedOnUsers, TimeDifference, IPAddresses
| project DeviceName, Timestamp, IPAddresses, TimeDifference=abs(Timestamp-pivotTimeParam);
DeviceInfo
| where Timestamp between ((pivotTimeParam-15m) ..30m)
| project DeviceName, Timestamp, LoggedOnUsers
| join kind=inner (matchingMachines) on DeviceName, Timestamp
| project Timestamp, DeviceName, LoggedOnUsers, TimeDifference, IPAddresses
// In case multiple machines have reported from that IP address arround that time, start with the ones reporting closest to pivotTimeParam
| sort by TimeDifference asc
// Query #3: get machines that have used a given *public* IP address at a given time - as seen in their communications with the WDATP cloud
let pivotTimeParam = datetime(2018-07-15 19:51:00);
let ipAddressParam = "192.168.1.5";
MachineInfo
| where EventTime between ((pivotTimeParam-15m) .. 30m) and PublicIP == ipAddressParam
| project ComputerName, LoggedOnUsers, EventTime, TimeDifference=abs(EventTime-pivotTimeParam)
DeviceInfo
| where Timestamp between ((pivotTimeParam-15m) .. 30m) and PublicIP == ipAddressParam
| project DeviceName, LoggedOnUsers, Timestamp, TimeDifference=abs(Timestamp-pivotTimeParam)
// In case multiple machines have reported from that IP address arround that time, start with the ones reporting closest to pivotTimeParam
| sort by TimeDifference asc
@ -49,9 +49,9 @@ MachineInfo
// This includes IP addresses seen locally in their network adapters configuration or ones used to access the WDATP cloud.
let pivotTimeParam = datetime(2018-07-15 19:51:00);
let ipAddressParam = "192.168.1.5";
MachineNetworkInfo
| where EventTime between ((pivotTimeParam-15m) ..30m) and IPAddresses contains strcat("\"", ipAddressParam, "\"") and NetworkAdapterStatus == "Up"
| project ComputerName, EventTime, Source="NetworkAdapterInfo"
| union (MachineInfo | where EventTime between ((pivotTimeParam-15m) .. 30m) and PublicIP == ipAddressParam | project ComputerName, EventTime, Source="Public IP address")
| extend TimeDifference=abs(EventTime-pivotTimeParam)
DeviceNetworkInfo
| where Timestamp between ((pivotTimeParam-15m) ..30m) and IPAddresses contains strcat("\"", ipAddressParam, "\"") and NetworkAdapterStatus == "Up"
| project DeviceName, Timestamp, Source="NetworkAdapterInfo"
| union (DeviceInfo | where Timestamp between ((pivotTimeParam-15m) .. 30m) and PublicIP == ipAddressParam | project DeviceName, Timestamp, Source="Public IP address")
| extend TimeDifference=abs(Timestamp-pivotTimeParam)
| sort by TimeDifference asc

Просмотреть файл

@ -1,10 +1,10 @@
// Query 1 shows you any network communication happened from endpoints to a specific Remote IP or Remote URL
// Ensure to update RemoteIP and RemoteURL variable.
// For questions @MiladMSFT on Twitter or milad.aslaner@microsoft.com by email
NetworkCommunicationEvents
DeviceNetworkEvents
| where RemoteIP == "IP ADDRESS GOES HERE"
or RemoteUrl endswith "DNS ENTRY GOES HERE"
| project EventTime, ComputerName, ActionType, RemoteIP, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine
| project Timestamp, DeviceName, ActionType, RemoteIP, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine
// Query 2 shows you any network communication that happened from endpoints through a specific file to an Remote IP or Remote URL
//Ensure to update RemoteIP, RemoteURL and InitatingProcessFileName
@ -12,18 +12,18 @@ or RemoteUrl endswith "DNS ENTRY GOES HERE"
let IP = "IP ADDRESS GOES HERE";
let DNS = "DNS ENTRY GOES HERE";
let FILENAME = "FILENAME GOES HERE";
NetworkCommunicationEvents
DeviceNetworkEvents
| where (RemoteIP == IP or RemoteUrl endswith DNS) and InitiatingProcessFileName =~ FILENAME
| project EventTime, ComputerName, ActionType, RemoteIP, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine
| project Timestamp, DeviceName, ActionType, RemoteIP, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine
// Query 3 allows you to find network communication to an IP or URL in the NetworkCommunicationEvents table, as well as in MiscEvents for other events (SmartScreen, launch browser with URL, more)
// Query 3 allows you to find network communication to an IP or URL in the DeviceNetworkEvents table, as well as in DeviceEvents for other events (SmartScreen, launch browser with URL, more)
// Ensure to update RemoteIP and RemoteURL variable.
find in (MiscEvents, NetworkCommunicationEvents)
find in (DeviceEvents, DeviceNetworkEvents)
where RemoteIP == "IP ADDRESS GOES HERE" or RemoteUrl =~ "URL GOES HERE"
project ComputerName, ActionType, FileName, EventTime
project DeviceName, ActionType, FileName, Timestamp
// Query 4 Search for specific network communication of a Remote IP or URL that also discovers related file creation events
// Ensure to update RemoteIP and RemoteURL variable.
FileCreationEvents
DeviceFileEvents
| where FileOriginUrl == "IP ADDRESS GOES HERE" or FileOriginUrl contains "URL GOES HERE" or FileOriginReferrerUrl contains "URL GOES HERE"
| project ComputerName, EventTime, FileName, FileOriginUrl, FileOriginIP, FileOriginReferrerUrl, SHA1
| project DeviceName, Timestamp, FileName, FileOriginUrl, FileOriginIP, FileOriginReferrerUrl, SHA1

Просмотреть файл

@ -1,14 +1,14 @@
// Get information about the netwotk adapters of the given computer in the given time.
// This could include the configured IP addresses, DHCP servers, DNS servers, and more.
let machineIdParam = "c0bfefec0bfefec0bfefec0bfefec0bfefecafe";
let DeviceIdParam = "c0bfefec0bfefec0bfefec0bfefec0bfefecafe";
let pivotTimeParam = datetime(2018-07-15T19:51);
MachineNetworkInfo
DeviceNetworkInfo
// Query for reports sent +-15 minutes around the time we are interested in
| where EventTime between ((pivotTimeParam-15m) .. 30m) and MachineId == machineIdParam and NetworkAdapterStatus == "Up"
| where Timestamp between ((pivotTimeParam-15m) .. 30m) and DeviceId == DeviceIdParam and NetworkAdapterStatus == "Up"
// IPAddresses contains a list of the IP addresses configured on the network adapter, their subnets, and more.
// Here we expand the list so that each value gets a separate row. All the other columns in the row, such as MacAddress, are duplicated.
| mvexpand parse_json(IPAddresses)
| project IPAddress=IPAddresses.IPAddress, AddressType=IPAddresses.AddressType, NetworkAdapterType, TunnelType, MacAddress,
ConnectedNetworks, EventTime, TimeDifference=abs(EventTime-pivotTimeParam)
ConnectedNetworks, Timestamp, TimeDifference=abs(Timestamp-pivotTimeParam)
// In case multiple machines have reported from that IP address arround that time, start with the ones reporting closest to pivotTimeParam
| sort by TimeDifference asc, NetworkAdapterType, MacAddress

Просмотреть файл

@ -5,22 +5,22 @@
// Define the time window
// Please note that results will vary depending on startDate
let startDate = ago(7d);
FileCreationEvents
DeviceFileEvents
| where InitiatingProcessCommandLine has "MpSigStub.exe"
//To exclude Engine Updates and non update events
| where InitiatingProcessParentFileName !~ "AM_Engine.exe" and InitiatingProcessParentFileName !~ "wuauclt.exe"
// Comment the below line if you're looking specifically for a computer
| where EventTime > startDate
| where Timestamp > startDate
// Uncomment the line below when looking for info regarding a specific computer
//| and ComputerName == "COMPUTER"
//| and DeviceName == "COMPUTER"
| extend NewVersion=tostring(split(InitiatingProcessCommandLine, " ")[4])
| summarize arg_max(NewVersion, EventTime) by ComputerName
| project ComputerName , NewVersion
| join (FileCreationEvents
| summarize arg_max(NewVersion, Timestamp) by DeviceName
| project DeviceName , NewVersion
| join (DeviceFileEvents
| where FileName == "MsMpEng.exe"
| where FolderPath has @"C:\ProgramData\Microsoft\Windows Defender\Platform\"
| where EventTime > startDate
| where Timestamp > startDate
| extend PlatformVersion=tostring(split(FolderPath, "\\", 5))
| project ComputerName, PlatformVersion)
on ComputerName
| project ComputerName , NewVersion , PlatformVersion
| project DeviceName, PlatformVersion)
on DeviceName
| project DeviceName , NewVersion , PlatformVersion

Просмотреть файл

@ -1,5 +1,5 @@
// Query #1: Look for public IP addresses that failed to logon to a computer multiple times, using multiple accounts, and eventually succeeded.
LogonEvents
DeviceLogonEvents
| where isnotempty(RemoteIP)
and AccountName !endswith "$"
and RemoteIPType == "Public"
@ -11,22 +11,22 @@ LogonEvents
SuccessfulAccountsCount = dcountif(Account, ActionType == "LogonSuccess"),
FailedAccounts = makeset(iff(ActionType == "LogonFailed", Account, ""), 5),
SuccessfulAccounts = makeset(iff(ActionType == "LogonSuccess", Account, ""), 5)
by ComputerName, RemoteIP, RemoteIPType
by DeviceName, RemoteIP, RemoteIPType
| where Failed > 10 and Successful > 0 and FailedAccountsCount > 2 and SuccessfulAccountsCount == 1
// Query #2: Look for machines failing to log-on to multiple machines or using multiple accounts
// Note - RemoteComputerName is not available in all remote logon attempts
LogonEvents
| where isnotempty(RemoteComputerName)
// Note - RemoteDeviceName is not available in all remote logon attempts
DeviceLogonEvents
| where isnotempty(RemoteDeviceName)
| extend Account=strcat(AccountDomain, "\\", AccountName)
| summarize
Successful=countif(ActionType == "LogonSuccess"),
Failed = countif(ActionType == "LogonFailed"),
FailedAccountsCount = dcountif(Account, ActionType == "LogonFailed"),
SuccessfulAccountsCount = dcountif(Account, ActionType == "LogonSuccess"),
FailedComputerCount = dcountif(ComputerName, ActionType == "LogonFailed"),
SuccessfulComputerCount = dcountif(ComputerName, ActionType == "LogonSuccess")
by RemoteComputerName
FailedComputerCount = dcountif(DeviceName, ActionType == "LogonFailed"),
SuccessfulComputerCount = dcountif(DeviceName, ActionType == "LogonSuccess")
by RemoteDeviceName
| where
Successful > 0 and
((FailedComputerCount > 100 and FailedComputerCount > SuccessfulComputerCount) or

Просмотреть файл

@ -2,11 +2,11 @@
// Author: miflower
// The purpose behind this detection is for finding service accounts that are performing remote powershell sessions
// There are two phases to the detection: Identify service accounts, Find remote PS cmdlets being ran by these accounts
// To accomplish this, we utilize LogonEvents and MiscEvents to find cmdlets ran that meet the criteria
// To accomplish this, we utilize DeviceLogonEvents and DeviceEvents to find cmdlets ran that meet the criteria
// One of the main advantages of this method is that only requires server telemetry, and not the attacking client
// The first phase relies on the LogonEvents to determine whether an account is a service account or not, consider the following accounts with logons:
// random_user has LogonEvents with type 2, 3, 7, 10, 11 & 13
// random_service_account 'should' only have LogonEvents with type 3,4 or 5
// The first phase relies on the DeviceLogonEvents to determine whether an account is a service account or not, consider the following accounts with logons:
// random_user has DeviceLogonEvents with type 2, 3, 7, 10, 11 & 13
// random_service_account 'should' only have DeviceLogonEvents with type 3,4 or 5
//
let InteractiveTypes = pack_array( // Declare Interactive logon type names
'Interactive',
@ -25,7 +25,7 @@ let WhitelistedCmdlets = pack_array( // List of w
'TabExpansion2'
);
let WhitelistedAccounts = pack_array('FakeWhitelistedAccount'); // List of accounts that are known to perform this activity in the environment and can be ignored
LogonEvents // Get all logon events...
DeviceLogonEvents // Get all logon events...
| where AccountName !in~ (WhitelistedAccounts) // ...where it is not a whitelisted account...
| where ActionType == "LogonSuccess" // ...and the logon was successful...
| where AccountName !contains "$" // ...and not a machine logon.
@ -38,7 +38,7 @@ LogonEvents // Get all l
// Now we need to find RemotePS sessions that were spawned by those accounts
// Note that we look at all powershell cmdlets executed to form a 29-day baseline to evaluate the data on today
| join kind=rightsemi ( // Start by dropping the account name and only tracking the...
MiscEvents // ...
DeviceEvents // ...
| where ActionType == 'PowerShellCommand' // ...PowerShell commands seen...
| where InitiatingProcessFileName =~ 'wsmprovhost.exe' // ...whose parent was wsmprovhost.exe (RemotePS Server)...
| extend AccountName = InitiatingProcessAccountName // ...and add an AccountName field so the join is easier
@ -46,9 +46,9 @@ LogonEvents // Get all l
// At this point, we have all of the commands that were ran by service accounts
| extend Command = tostring(extractjson('$.Command', AdditionalFields)) // Extract the actual PowerShell command that was executed
| where Command !in (WhitelistedCmdlets) // Remove any values that match the whitelisted cmdlets
| summarize (EventTime, ReportId)=argmax(EventTime, ReportId), // Then group all of the cmdlets and calculate the min/max times of execution...
makeset(Command), count(), min(EventTime) by // ...as well as creating a list of cmdlets ran and the count..
AccountName, ComputerName, MachineId // ...and have the commonality be the account, computername and machineid
| summarize (Timestamp, ReportId)=argmax(Timestamp, ReportId), // Then group all of the cmdlets and calculate the min/max times of execution...
makeset(Command), count(), min(Timestamp) by // ...as well as creating a list of cmdlets ran and the count..
AccountName, DeviceName, DeviceId // ...and have the commonality be the account, DeviceName and DeviceId
// At this point, we have machine-account pairs along with the list of commands run as well as the first/last time the commands were ran
| order by AccountName asc // Order the final list by AccountName just to make it easier to go through
| where min_EventTime > ago(1d) // Included to restrict the scope for the custom detection page
| where min_Timestamp > ago(1d) // Included to restrict the scope for the custom detection page

Просмотреть файл

@ -9,32 +9,32 @@ let minTime = ago(7d);
let accessibilityProcessNames = dynamic(["utilman.exe","osk.exe","magnify.exe","narrator.exe","displayswitch.exe","atbroker.exe","sethc.exe", "helppane.exe"]);
// Query for debuggers attached using a Registry setting to the accessibility processes
let attachedDebugger =
RegistryEvents
| where EventTime > minTime
DeviceRegistryEvents
| where Timestamp > minTime
and RegistryKey startswith @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\"
and RegistryValueName =~ "debugger"
// Parse the debugged process name from the registry key
| parse RegistryKey with @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\" FileName
| where FileName in~ (accessibilityProcessNames) and isnotempty(RegistryValueData)
| project Technique="AttachedDebugger", FileName, AttachedDebuggerCommandline=RegistryValueData, InitiatingProcessCommandLine, EventTime, ComputerName;
| project Technique="AttachedDebugger", FileName, AttachedDebuggerCommandline=RegistryValueData, InitiatingProcessCommandLine, Timestamp, DeviceName;
// Query for overwrites of the accessibility files
let fileOverwiteOfAccessibilityFiles =
FileCreationEvents
| where EventTime > minTime
DeviceFileEvents
| where Timestamp > minTime
and FileName in~ (accessibilityProcessNames)
and FolderPath contains @"Windows\System32"
| project Technique="OverwriteFile", EventTime, ComputerName, FileName, SHA1, InitiatingProcessCommandLine;
| project Technique="OverwriteFile", Timestamp, DeviceName, FileName, SHA1, InitiatingProcessCommandLine;
// Query for unexpected hashes of processes with names matching the accessibility processes.
// Specifically, query for hashes matching cmd.exe and powershell.exe, as these MS-signed general-purpose consoles are often used with this technique.
let executedProcessIsPowershellOrCmd =
ProcessCreationEvents
| project Technique="PreviousOverwriteFile", EventTime, ComputerName, FileName, SHA1
| where EventTime > minTime
DeviceProcessEvents
| project Technique="PreviousOverwriteFile", Timestamp, DeviceName, FileName, SHA1
| where Timestamp > minTime
| where FileName in~ (accessibilityProcessNames)
| join kind=leftsemi(
ProcessCreationEvents
| where EventTime > ago(14d) and (FileName =~ "cmd.exe" or FileName =~ "powershell.exe")
| summarize MachinesCount = dcount(ComputerName) by SHA1
DeviceProcessEvents
| where Timestamp > ago(14d) and (FileName =~ "cmd.exe" or FileName =~ "powershell.exe")
| summarize MachinesCount = dcount(DeviceName) by SHA1
| where MachinesCount > 5
| project SHA1
) on SHA1;

Просмотреть файл

@ -5,11 +5,11 @@
// Query #1: Query for users being created using "net user" command
// "net user" commands are noisy, so needs to be joined with another signal -
// e.g. in this example we look for use of uncommon & undocumented commandline switches (e.g. /ad instead of /add)
ProcessCreationEvents
DeviceProcessEvents
// Pro-tip:
// There are many different ways to run a process from a file - e.g. by using full path, env. variables, ~1 annotation, more...
// So, to find executions of a known filename, better filter on the filename (and possibly on folder path) than on the commandline.
| where FileName in~ ("net.exe", "net1.exe") and EventTime > ago(3d)
| where FileName in~ ("net.exe", "net1.exe") and Timestamp > ago(3d)
// Parse the user name from the commandline.
// To have case-insensitive parsing use the i flag, to have non-greedy match (e.g. CreatedUser as short as possible), specify U flag:
// "kind=regex flags=i"
@ -23,14 +23,14 @@ ProcessCreationEvents
// Also, any prefix that's longer than 1 char will also do the same, e.g. /do, /dom, /doma, ....
| extend CreatedOnLocalMachine=(ProcessCommandLine !contains "/do")
| where ProcessCommandLine !contains "/add" or (CreatedOnLocalMachine == 0 and ProcessCommandLine !contains "/domain")
| summarize MachineCount=dcount(ComputerName) by CreatedUser, CreatedOnLocalMachine, InitiatingProcessFileName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| summarize MachineCount=dcount(DeviceName) by CreatedUser, CreatedOnLocalMachine, InitiatingProcessFileName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
// Query #2: Query for accounts created on machines onboarded with Sense.
// Create account event is noisy, so we need to join it with some other signal.
// E.g. In this query we look for accounts created which name resembles "administrator".
// Using account names similar to known common account names is a common way to be evade the human analyst eye.
MiscEvents
DeviceEvents
| where ActionType == "UserAccountCreated"
// To look for account names similar to administrator, we'll simply query for the prefix and suffix,
// because these letters matter most to the human perception: https://en.wikipedia.org/wiki/Typoglycemia
@ -38,5 +38,5 @@ MiscEvents
// and looking for prefix and suffix should work in this case pretty well.
| where AccountName startswith "ad" and AccountName endswith "or" and AccountName !~ "administrator"
// Note: For the UserAccountCreated event we do not know the details of the process / account that was used to create this new account.
| project AccountName, AccountDomain, ComputerName, EventTime
| project AccountName, AccountDomain, DeviceName, Timestamp
| limit 100

Просмотреть файл

@ -1,5 +1,5 @@
//Original Sigma Rule: https://github.com/Neo23x0/sigma/blob/master/rules/windows/process_creation/win_susp_schtask_creation.yml
//Questions via Twitter: @janvonkirchheim
ProcessCreationEvents
DeviceProcessEvents
| where FolderPath endswith "\\schtasks.exe" and ProcessCommandLine has " /create " and AccountName != "system"
| where EventTime > ago(7d)
| where Timestamp > ago(7d)

Просмотреть файл

@ -1,14 +1,14 @@
// Query for Windows Defender Antivirus detections.
// Query #1: Query for Antivirus detection events
MiscEvents
DeviceEvents
| where ActionType == "AntivirusDetection"
| extend ParsedFields=parse_json(AdditionalFields)
| project ThreatName=tostring(ParsedFields.ThreatName),
WasRemediated=tobool(ParsedFields.WasRemediated),
WasExecutingWhileDetected=tobool(ParsedFields.WasExecutingWhileDetected),
FileName, SHA1, InitiatingProcessFileName, InitiatingProcessCommandLine,
ComputerName, EventTime
DeviceName, Timestamp
| limit 100
// Query #2:
@ -18,9 +18,9 @@ MiscEvents
// This query looks for alerts on Windows Defender Antivirus detections.
// For most purposes it is probably better to query on the events themselves (see query #1).
// However, this query might still be useful sometimes (e.g. to quickly parse the family name).
AlertEvents
DeviceAlertEvents
| where Title contains "Defender AV detected"
| parse Title with *"'"FamilyName"'"*
| summarize FamilyCount=dcount(FamilyName), Families=makeset(FamilyName), Titles=makeset(Title) by ComputerName, MachineId, bin(EventTime, 1d)
| summarize FamilyCount=dcount(FamilyName), Families=makeset(FamilyName), Titles=makeset(Title) by DeviceName, DeviceId, bin(Timestamp, 1d)
| where FamilyCount > 1
| limit 100

Просмотреть файл

@ -1,10 +1,10 @@
// Expanding on MiscEvents output with Attack Surface Reduction (ASR) rule descriptions
// Expanding on DeviceEvents output with Attack Surface Reduction (ASR) rule descriptions
// The ActionType values of the ASR events already explain what rule was matched and if it was audited or blocked.
// However, it could still be useful to have a more human-friendly description in the results.
// Also, this query is a good example for how you could define your own lookup tables and join with them.
// The events in the MiscEvents table contain a GUID for the various ASR rules rather than a full description of the rule
// The events in the DeviceEvents table contain a GUID for the various ASR rules rather than a full description of the rule
// This query will create a table which has the description for each ASR rule as per https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-exploit-guard/enable-attack-surface-reduction
// This table is then joined to the output of a query against the MiscEvents table and shows a summary count of the events by the newly defined description
// This table is then joined to the output of a query against the DeviceEvents table and shows a summary count of the events by the newly defined description
// This query shows the ability to use joins and custom dimension tables
// See https://docs.loganalytics.io/docs/Language-Reference/Tabular-operators/join-operator for more information on the join syntax
// For more questions on this query, feel free to ping @FlyingBlueMonki on twitter or mattegen@microsoft.com via email
@ -26,8 +26,8 @@ let AsrDescriptionTable = datatable(RuleDescription:string, RuleGuid:string)
"Block untrusted and unsigned processes that run from USB","b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4",
"Block Office communication applications from creating child processes (available for beta testing)","26190899-1602-49e8-8b27-eb1d0a1ce869",
];
// Now we query the MiscEvents table for events where the ActionType field starts with "Asr" - which should cover values such as AsrExecutableEmailContentAudited, AsrExecutableEmailContentBlocked, AsrOfficeChildProcessAudited, ....
MiscEvents
// Now we query the DeviceEvents table for events where the ActionType field starts with "Asr" - which should cover values such as AsrExecutableEmailContentAudited, AsrExecutableEmailContentBlocked, AsrOfficeChildProcessAudited, ....
DeviceEvents
| where ActionType startswith "Asr"
// since the RuleGuid is stored inside the additionlfields column, we need to extract it for the join
// we extend the results to include a new "RuleGuid" column that is populated by the extracted RuleId from the json data in AdditionalFields.
@ -35,11 +35,11 @@ MiscEvents
// and finally we also extend the results with the extracted "IsAudit" column populated from AdditionalFields. This allow us to determine if the event was blocked or merely audited
| extend RuleGuid = tolower(tostring(parsejson(AdditionalFields).RuleId))
| extend IsAudit = parse_json(AdditionalFields).IsAudit
| project ComputerName, RuleGuid, MachineId, IsAudit
| project DeviceName, RuleGuid, DeviceId, IsAudit
// Now we're making our join back to the earlier defined table of rule descriptions and guids (applying that tolower() statement for consistency) and finally outputting our summary counts
// We're projecting both the RuleDescription and the RuleGuid. If there is a new rule that is *NOT* in our table above, we'll get a row with no description, but including the Guid so we can find it and update the table
| join kind = leftouter (AsrDescriptionTable | project RuleGuid = tolower(RuleGuid), RuleDescription) on RuleGuid
| summarize MachinesWithAuditEvents = dcountif(MachineId,IsAudit==1), MachinesWithBlockEvents = dcountif(MachineId, IsAudit==0), AllEvents=count() by RuleDescription, RuleGuid
| summarize MachinesWithAuditEvents = dcountif(DeviceId,IsAudit==1), MachinesWithBlockEvents = dcountif(DeviceId, IsAudit==0), AllEvents=count() by RuleDescription, RuleGuid
// an alternative summary line is commented out below. This would show us a count of each rule on each machine rather than a machine / event count
//| summarize count() by RuleDescription, ComputerName
//| summarize count() by RuleDescription, DeviceName

Просмотреть файл

@ -7,10 +7,10 @@
// Tags: #ASR
//Query #1: block stats
MiscEvents
| where ActionType == "AsrOfficeChildProcessBlocked" and EventTime > ago(7d)
| project BlockedProcess=FileName, ParentProcess=InitiatingProcessFileName, ComputerName
| summarize MachineCount=dcount(ComputerName), RuleHits=count() by BlockedProcess, ParentProcess
DeviceEvents
| where ActionType == "AsrOfficeChildProcessBlocked" and Timestamp > ago(7d)
| project BlockedProcess=FileName, ParentProcess=InitiatingProcessFileName, DeviceName
| summarize MachineCount=dcount(DeviceName), RuleHits=count() by BlockedProcess, ParentProcess
| sort by MachineCount desc
// Query #2: investigate audit events - before turning the rule on in block mode
@ -19,15 +19,15 @@ let minTime = ago(7d);
// If there was an alert, so this is probably malware, and it's good that it will be blocked.
// If there was no alert, so it requires further analysis to determine if this is a clean file or some malware that was missed.
let alerts =
AlertEvents
| where EventTime > minTime
| project ComputerName, DetectedEventTime=EventTime;
MiscEvents
| where ActionType == "AsrOfficeChildProcessAudited" and EventTime > minTime
| project BlockedProcess=FileName, ParentProcess=InitiatingProcessFileName, ComputerName, EventTime
| join kind=leftouter (alerts) on ComputerName
| extend HasNearbyAlert = abs(EventTime - DetectedEventTime) between (0min .. 5min)
| summarize MachineCount=dcount(ComputerName),
DeviceAlertEvents
| where Timestamp > minTime
| project DeviceName, DetectedTimestamp=Timestamp;
DeviceEvents
| where ActionType == "AsrOfficeChildProcessAudited" and Timestamp > minTime
| project BlockedProcess=FileName, ParentProcess=InitiatingProcessFileName, DeviceName, Timestamp
| join kind=leftouter (alerts) on DeviceName
| extend HasNearbyAlert = abs(Timestamp - DetectedTimestamp) between (0min .. 5min)
| summarize MachineCount=dcount(DeviceName),
RuleHits=count(),
NearbyAlertPercent=countif(HasNearbyAlert)*100.0 / count()
by BlockedProcess, ParentProcess

Просмотреть файл

@ -1,7 +1,7 @@
//Simple query to show the unique network connections that were audited or blocked by ExploitGuard
// For more questions on this query, feel free to ping @FlyingBlueMonki on twitter or mattegen@microsoft.com via email
MiscEvents
| where EventTime > ago(7d)
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType =~ "ExploitGuardNetworkProtectionBlocked"
| summarize count(RemoteUrl) by InitiatingProcessFileName, RemoteUrl, Audit_Only=tostring(parse_json(AdditionalFields).IsAudit)
| sort by count_RemoteUrl desc

Просмотреть файл

@ -1,11 +1,11 @@
// Get stats on ExploitGuard blocks - count events and machines per rule
MiscEvents
DeviceEvents
| where ActionType startswith "ExploitGuard" and ActionType endswith "Blocked"
// Count total stats - count events and machines per rule
| summarize EventCount=count(), MachinesCount=dcount(ComputerName) by ActionType
| summarize EventCount=count(), MachinesCount=dcount(DeviceName) by ActionType
// View ExploitGuard audit events - but remove repeating events (e.g. multiple events with same machine, rule, file and process)
MiscEvents
DeviceEvents
| where ActionType startswith "ExploitGuard" and ActionType endswith "Audited"
| summarize EventTime =max(EventTime) by ComputerName, ActionType,FileName, FolderPath, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessId, SHA1
| summarize Timestamp =max(Timestamp) by DeviceName, ActionType,FileName, FolderPath, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessId, SHA1

Просмотреть файл

@ -5,25 +5,25 @@
// Tags: #SmartScreen
let minTimeRange = ago(7d);
let smartscreenUrlBlocks =
MiscEvents
| where ActionType == "SmartScreenUrlWarning" and EventTime > minTimeRange
DeviceEvents
| where ActionType == "SmartScreenUrlWarning" and Timestamp > minTimeRange
// Filter out SmartScreen test URLs under https://demo.smartscreen.msft.net/
and RemoteUrl !startswith "https://demo.smartscreen.msft.net/"
| extend ParsedFields=parse_json(AdditionalFields)
| project EventTime, ComputerName, BlockedUrl=RemoteUrl, Recommendation=tostring(ParsedFields.Recommendation), Experience=tostring(ParsedFields.Experience), ActivityId=tostring(ParsedFields.ActivityId);
| project Timestamp, DeviceName, BlockedUrl=RemoteUrl, Recommendation=tostring(ParsedFields.Recommendation), Experience=tostring(ParsedFields.Experience), ActivityId=tostring(ParsedFields.ActivityId);
// Query for UserDecision events - each one means the user has decided to ignore the warning and run the app.
let userIgnoredWarning=
MiscEvents
| where ActionType == "SmartScreenUserOverride" and EventTime > minTimeRange
| project ComputerName, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string));
DeviceEvents
| where ActionType == "SmartScreenUserOverride" and Timestamp > minTimeRange
| project DeviceName, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string));
// Join the block and user decision event using an ActivityId
let ignoredBlocks = smartscreenUrlBlocks | join kind=leftsemi (userIgnoredWarning) on ComputerName, ActivityId | project-away ActivityId;
let ignoredBlocks = smartscreenUrlBlocks | join kind=leftsemi (userIgnoredWarning) on DeviceName, ActivityId | project-away ActivityId;
// Optional additional filter - look only for cases where a file was downloaded from Microsoft Edge following the URL block being ignored
let edgeDownloads =
FileCreationEvents
| where EventTime > minTimeRange and InitiatingProcessFileName =~ "browser_broker.exe"
| summarize (DownloadTime, SHA1) = argmax(EventTime, SHA1) by FileName, ComputerName, FileOriginUrl, FileOriginReferrerUrl;
DeviceFileEvents
| where Timestamp > minTimeRange and InitiatingProcessFileName =~ "browser_broker.exe"
| summarize (DownloadTime, SHA1) = argmax(Timestamp, SHA1) by FileName, DeviceName, FileOriginUrl, FileOriginReferrerUrl;
ignoredBlocks
| join kind=inner (edgeDownloads) on ComputerName
| where DownloadTime - EventTime between (0min .. 2min)
| project-away ComputerName1
| join kind=inner (edgeDownloads) on DeviceName
| where DownloadTime - Timestamp between (0min .. 2min)
| project-away DeviceName1

Просмотреть файл

@ -4,21 +4,21 @@
// Tags: #SmartScreen
let minTimeRange = ago(7d);
let smartscreenAppBlocks =
MiscEvents
| where ActionType == "SmartScreenAppWarning" and EventTime > minTimeRange
DeviceEvents
| where ActionType == "SmartScreenAppWarning" and Timestamp > minTimeRange
// Filter out SmartScreen test files downloaded from https://demo.smartscreen.msft.net/
and not (FileName startswith "knownmalicious" and FileName endswith ".exe")
| extend ParsedFields=parse_json(AdditionalFields)
| project EventTime, ComputerName, BlockedFileName=FileName, SHA1, Experience=tostring(ParsedFields.Experience), ActivityId=tostring(ParsedFields.ActivityId), InitiatingProcessFileName;
| project Timestamp, DeviceName, BlockedFileName=FileName, SHA1, Experience=tostring(ParsedFields.Experience), ActivityId=tostring(ParsedFields.ActivityId), InitiatingProcessFileName;
// Query for UserDecision events - each one means the user has decided to ignore the warning and run the app.
let userIgnoredWarning=
MiscEvents
| where ActionType == "SmartScreenUserOverride" and EventTime > minTimeRange
| project ComputerName, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string));
DeviceEvents
| where ActionType == "SmartScreenUserOverride" and Timestamp > minTimeRange
| project DeviceName, ActivityId=extractjson("$.ActivityId", AdditionalFields, typeof(string));
// Join the block and user decision event using an ActivityId
let ignoredBlocks =
smartscreenAppBlocks
| join kind=leftsemi (userIgnoredWarning) on ComputerName, ActivityId
| join kind=leftsemi (userIgnoredWarning) on DeviceName, ActivityId
| project-away ActivityId;
ignoredBlocks
// Select only blocks on "Malicious" files.

Просмотреть файл

@ -1,8 +1,8 @@
// Get all filtering events done by the Windows filtering platform.
// This includes any blocks done by Windows Firewall rules, but also blocks triggered by some 3rd party firewalls.
// When no Firewall rules are configured, the default behavior is to block all incoming network connections.
MiscEvents
DeviceEvents
| where ActionType in ("FirewallOutboundConnectionBlocked", "FirewallInboundConnectionBlocked", "FirewallInboundConnectionToAppBlocked")
| project MachineId , EventTime , InitiatingProcessFileName , InitiatingProcessParentFileName, RemoteIP, RemotePort, LocalIP, LocalPort
| summarize MachineCount=dcount(MachineId) by RemoteIP
| project DeviceId , Timestamp , InitiatingProcessFileName , InitiatingProcessParentFileName, RemoteIP, RemotePort, LocalIP, LocalPort
| summarize MachineCount=dcount(DeviceId) by RemoteIP
| top 100 by MachineCount desc