Update github queries to use the new advanced hunting device schema
This commit is contained in:
Родитель
00dabae92e
Коммит
d3d93746b3
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче