| Process/Start* | Process/End* | Process/ParentIdSpoofing* | Thread/RemoteThread* | Image/Load* |
|---|---|---|---|---|
| ProcessID | ProcessID | ProcessID | ProcessID | ProcessID |
| ParentProcessID | ParentProcessID | ParentProcessID | InjectorProcessID | ImageChecksum |
| ImageName | UserName | ImageName | ThreadID | TimeDateStamp* |
| CommandLine | CommandLine | Spoofed_ParentProcessID | ImageMD5 | |
| UserName | UserName | ImageSHA1 | ||
| ProcessGUID | PackageFullName | ImageSHA256 | ||
| ParentProcessGUID | CreationFlags | FileName | ||
| PackageFullName | ||||
| CreationFlags | ||||
| Creation_timestamp |
* donates a nested query
| FileIo/Write* | FileIo/FileRename* | FileIo/FileDelete* | Registry/Create* | Registry/SetValue* |
|---|---|---|---|---|
| ProcessID | ProcessID | ProcessID | ProcessID | ProcessID |
| FilePath | FileObject | FilePath | KeyName | KeyName |
| IOSize | FileOldPath* | FileKey | Registry/Create* | KeyHandle |
| IOFlags | FileNewPath* | ValueName | ||
| FileObject | Data |
| TcpIp/ConnectIPV4 | TcpIp/ConnectIPV6 | WinHTTP/Log | WinInet/Log |
|---|---|---|---|
| ProcessID | ProcessID | ProcessID | ProcessID |
| SourceAddress | DestAddress | URI | URL |
| SourcePort | DestPort | Verb | Verb |
| DestAddress | RequestHeaders | ||
| DestPort | ResponseHeaders |
| PowerShell/Command Execution | |||
|---|---|---|---|
| ProcessID | HostApplication | ScriptName | ShellID |
| ContextInfo | EngineVersion | CommandPath | UserData |
| HostName | RunspaceID | SequenceNumber | Payload |
| HostVersion | CommandName | User | |
| HostID | CommandType | ConnectedUser | |
| Direct Items | |||
|---|---|---|---|
| EndpointID | EventTime | ProcessID | ProcessUserName |
| OS | AccountName | ParentProcessID | CommandLine |
| Hostname | TenantName | ImageName | ProcessGUID |
| AgentVersion | GroupName | ImagePath | ParentProcessGUID |
| TrackID | Events | ImageSHA256 | |
| SecurityAudit/CredentialValidation | DOTNET/ModuleLoad | |
|---|---|---|
| ProcessID | CommandLine | ProcessID |
| ParentProcessID | PackageName | ModuleILPath |
| UserName | TargetUserName | ModuleNativePath |
| ProcessGUID | Workstation | |
| ParentProcessGUID | Status | |
| Creation_timestamp | ||
There are two kinds of queries:
| Direct Query Example | Nested Query Example |
|---|---|
| Prodv1 | ProdV1 |
| |where EndpointID == “e115190832445” | |mv-expand Action = parse_json(Events) |
| |where ImageName has “powershell.exe” |
|extend EventName = tostring(Action.EventName) |
| , Key = tostring(Action.KeyName) | |
| , Value = tostring(Action.ValueName) | |
|
The command ‘mv-expand’ allows the nested items in ‘Events’ to be read by ‘parse_json’ and assigned via ‘Action’. Then, columns are added via ‘extend’ to break the results up respectively. |
|
| |where EventName startswith “Registry” | |
| |project ProcessID, ImageName, EventName, EventTime, Key, Value |
When using “project”, those will be the only columns shown in the results. Ensure you have all necessary and/or relevant fields listed.
When running queries, multiple statements can be run and joined into a single table result. Here is an example of a query that shows parent processes which make network connections, along with the geolocation data for each IP address.
| let trackInfo = |
|---|
|
The first query statement ‘trackInfo’ pulls the events where processes start. |
| ProdV1 |
| | mv-expand Action = parse_json(Events) |
| | extend EventName = tostring(Action.EventName) , ProcessID = tostring(Action.ProcessID) , Timestamp = tostring(Action.Creation_timestamp) |
| | where EventName has "Process/Start" |
| | project EndpointID, TrackID, ProcessID, EventName, Timestamp; |
| let geoInfo = |
|---|
|
The ‘geoInfo’ statement pulls addresses and sources geolocation data using the KQL ‘geo_info_from_ip_address’ function. |
| ProdV1 |
| | mv-expand Action = parse_json(Events) |
| | extend EventName = tostring(Action.EventName) |
| , ProcessID = tostring(Action.ProcessID) |
| , Timestamp = tostring(Action.Creation_timestamp) |
| , DestAddress = tostring(Action.DestAddress) |
| , DestPort = tostring(Action.DestPort) |
| , SourceAddress = tostring(Action.SourceAddress) |
| , SourcePort = tostring(Action.SourcePort) |
| , DestCountry = geo_info_from_ip_address(tostring(Action.DestAddress)).country |
| , DestState = geo_info_from_ip_address(tostring(Action.DestAddress)).state |
| , Latitude = geo_info_from_ip_address(tostring(Action.DestAddress)).latitude |
| , Longitude = geo_info_from_ip_address(tostring(Action.DestAddress)).longitude |
|
| where EventName has "TcpIp" | join kind=inner (trackInfo) on ProcessID; These two statements will be tied together based on the ProcessID and put into a new ‘geoInfo’ table. |
| geoInfo |
|---|
| | summarize arg_max(EventTime, *) by EventTime |
| | project EndpointID, ProcessID, ImageName, EventName, DestAddress, DestPort, SourceAddress, SourcePort, DestCountry |
| Name | Query |
|---|---|
| Recent Events (No Filters) | | limit 10/50/100 |
| Application Events | | where ImageName == “<application.exe>” |
| Endpoint Events | | where EndpointID == “<endpoint id>” |
| Command or String | | where CommandLine has “<string>” |
| Process File Path | | where ImagePath == “<path>” OR| where ImagePath has_any (“AppData”, “Temp”) |
| Events w/ Specific Agent Version(s) | | where AgentVersion == “1.0.4” OR| where AgentVersion has_any (“1.0.4”, “1.0.15”) |
| List Distinct Items (Endpoints, Events, etc) | | distinct <keyword> |
| Find Specific Process | | where ProcessID == “5492” |
| Find All Events In a Process | | where TrackID == “<TrackID>” |
| Operating Systems | | distinct OS OR| where OS startswith “Windows” |
| Processes Run By Specific User | | where ProcessUserName == “<username>” |
| Lookup Tenant | | where TenantName == “<tenant>” |
| Lookup Group | | where GroupName == “<group>” |
| Lookup Account | | where AccountName == “<account>” |
| Track Events By ProcessGUID | | where ProcessGUID == “<guid>” |
| Track Events By ParentProcessGUID | | where ParentProcessGUID == “<parentGUID>” |
| SHA256 (Main Process Executable) | | where ImageSHA256 == “<SHA256>” |
| Backup - Network Share(s) Disabled | | where ImageName in (“cmd.exe”, “net.exe”, “net1.exe”) and CommandLine has_any (“memserv”, “mssql”, “pg-ctl”, “sql”) |
| Backup - Shadow Volume Delete | | where ImageName == "vssadmin.exe" and CommandLine has tolower("Delete", "Shadows", "//All//Quiet") |
| Find Registry Processes on an Endpoint | | where EndpointID == “d19ijuvdffd3f2” and ImageName has_any (“reg.exe”, “regsvr32.exe”) |
| Persistence (Basic) | | where CommandLine has_any (“\CurrentVersion\Run”, “\CurrentVersion\Group Policy\Scripts”) |
| PowerShell Events (Basic) | | where ImageName has_any (“powershell”, “psexec”) |
| PowerShell Hidden/Encoded Events | | where ImageName has “powershell” and CommandLine has_any (“-e”, “-ec”, “-en”, -”enc”, “-enco”“hidden”, “http”, “iex”, “SilentlyContinue”, “Start-Process -WindowStyle Hidden”) |
| System Reconnaissance (Basic) | | where ImageName == “cmd.exe” and CommandLine has_any (“systeminfo”, “ipconfig”, “netstat”, “arp -a”) |
| System Reconnaissance (net) | | where CommandLine has “net” and CommandLine has_any (“computer”, “config”, “group”, “localgroup”, “user”) |
| System Reconnaissance (netsh) | | where CommandLine has “netsh” and CommandLine has_any (“add”, “advfirewall”, “alias”, “delete”, “exec”, “firewall”) |
| WevUtil Extraction | | where ImageName == "wevtutil.exe" and CommandLine has_any("TokenElevationTypeFull", "cl", "qe") |
When accessing nested items, you are parsing through the raw JSON data that is not immediately filtered through to the basic queries. This includes things like process start/end, libraries loaded, threads started, network traffic details, etc.
Here is an example query to show TCP/IP traffic:
| let TCP/IP traffic = |
|---|
| ProdV1 |
| | mv-expand Action = parse_json(Events) |
| | extend EventName = tostring(Action.EventName) |
| , ProcessID = tostring(Action.ProcessID) |
| , SourceAddress = tostring(Action.SourceAddress) |
| , SourcePort = tostring(Action.SourcePort) |
| , DestAddress = tostring(Action.DestAddress |
| , DestPort = tostring(Action.DestPort) |
| | where EventName startswith "TcpIp/Connect" |
| | project ProcessID, ImageName, EventName, SourceAddress, SourcePort, DestAddress, DestPort |
This will give the Process ID, Process Name, and addresses related.
This is not a packet capture.
Reformat has no queries to the right.
When creating queries with these fields, keep in mind that some records will be blank or missing, depending on the context of the JSON log and what was received. In the above example, one could add fields like ‘URI’, ‘URL’, ‘Verb’, ‘RequestHeaders’, and ‘ResponseHeaders’. However, these fields return empty entries unless looking at WinHTTP/Log and WinInet/Log events (adding ‘and where EventName endswith “Log” after “TcpIp/Connect”’).
| Name | Query |
|---|---|
| List Nested Event Types | | distinct EventNames |
| Registry Key Creation |
| where EventName == “Registry/Create” | project EventName, KeyValue |
| Registry Value Write |
| where EventName == “Registry/SetValue” | project EventName, KeyName, ValueName, Data |
| File Write |
| where EventName == “FileIo/Write” | project EventName, FilePath, FileObject |
| File Delete |
| where EventName == “FileIo/FileDelete” | project EventName, FilePath |
| File Rename |
| where EventName == “FileIo/FileRename” | project EventName, OldFilePath, NewFilePath |
| Remote Thread Creation |
| where EventName == “Thread/RemoteThread” | project EventName, InjectorProcessID, ThreadID |
| API Library Load | | where EventName == “Image/Load” and FileName == “ntdll.dll” |
| DotNet Use | | where EventName == “DOTNET/ModuleLoad” |
| PowerShell Script | | where EventName == “Powershell/Script Execution” |
| PowerShell Commands | | where EventName == “Powershell/Command Execution” |
| HTTP Log |
| where EventName == “WinHTTP/Log” | project EventName, URI, Verb |
| INET Log |
| where EventName == “WinInet/Log” | project EventName, URL, Verb |