Teams → RMM correlation
let _timeFrame = 30m;
// Teams message signal
let _teams =
MessageEvents
| where Timestamp > ago(30d)
| extend Recipient = parse_json(RecipientDetails)
| mv-expand Recipient
| extend
VictimAccountObjectId = tostring(Recipient.RecipientObjectId),
VictimRecipientDisplayName = tostring(Recipient.RecipientUserDisplayName)
| project
TTime = Timestamp,
SenderEmailAddress,
SenderDisplayName,
VictimRecipientDisplayName,
VictimAccountObjectId;
// RMM launches on endpoint side
let _rmm =
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("QuickAssist.exe", "AnyDesk.exe", "TeamViewer.exe")
| extend VictimAccountObjectId = tostring(InitiatingProcessAccountObjectId)
| project
DeviceName,
QTime = Timestamp,
RmmTool = FileName,
VictimAccountObjectId;
_teams
| where isnotempty(VictimAccountObjectId)
| join kind=inner _rmm on VictimAccountObjectId
| where isnotempty(DeviceName)
| where QTime between ((TTime) .. (TTime + (_timeFrame)))
| project
DeviceName,
SenderEmailAddress,
SenderDisplayName,
VictimRecipientDisplayName,
VictimAccountObjectId,
TTime,
QTime,
RmmTool
| order by QTime desc
Execution
DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName =~ "cmd.exe"
| where FileName =~ "cmd.exe"
| where ProcessCommandLine has_all (
"/S /D /c",
"\" set /p=\"PK\"",
"1>"
)
ZIP → ProgramData service path → signed host sideload
let _timeFrame = 10m;
let _armOrDevice =
DeviceFileEvents
| where Timestamp > ago(30d)
| where FolderPath startswith "C:\\ProgramData\\"
or FolderPath startswith "D:\\ProgramData\\"
| where ActionType in ("FileCreated", "FileRenamed")
| where FileName endswith ".dll"
or FileName endswith ".exe"
or FileName endswith ".zip"
| where FolderPath !has "\\Microsoft\\Windows Defender\\"
and FolderPath !has "\\Microsoft\\Windows\\WER\\"
and FolderPath !has "\\Package Cache\\"
and FolderPath !has "\\Microsoft\\ClickToRun\\"
and FolderPath !has "\\Dell\\"
and FolderPath !has "\\Lenovo\\"
and FolderPath !has "\\ASUS\\"
and FolderPath !has "\\Adobe\\ARM\\"
| project DeviceName, First=Timestamp, FileName, FolderPath;
let _hostRun =
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ (
"AcroServicesUpdater2_x64.exe",
"DlpUserAgent.exe",
"ADNotificationManager.exe",
"werfault.exe"
)
| where FolderPath startswith "C:\\ProgramData\\"
or FolderPath startswith "D:\\ProgramData\\"
| where FolderPath !has "\\Microsoft\\Windows Defender\\"
and FolderPath !has "\\Adobe\\ARM\\"
| project
DeviceName,
Run=Timestamp,
Host=FileName,
ProcessCommandLine,
ProcessFolderPath=FolderPath;
_armOrDevice
| join kind=inner _hostRun on DeviceName
| where Run between (First .. (First + (_timeFrame)))
| summarize
First=min(First),
Run=min(Run),
Files=make_set(FileName, 10),
Paths=make_set(FolderPath, 10)
by DeviceName, Host, ProcessCommandLine, ProcessFolderPath
| order by Run desc
PowerShell → high‑risk TLD → writes %AppData%/Roaming EXE
let _timeFrame = 5m;
let _psNet = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("powershell.exe", "pwsh.exe")
| where RemoteUrl matches regex @"(?i)\.(top|xyz|zip|click)$"
| project DeviceName, NetTime=Timestamp, RemoteUrl, RemoteIP;
let _exeWrite = DeviceFileEvents
| where Timestamp > ago(30d)
| where FolderPath has @"\AppData\Roaming\" and FileName endswith ".exe"
| project DeviceName, WTime=Timestamp, FileName, FolderPath, SHA256;
_psNet
| join kind=inner _exeWrite on DeviceName
| where WTime between (NetTime .. (NetTime + (_timeFrame)))
| project
DeviceName,
NetTime,
RemoteUrl,
RemoteIP,
WTime,
FileName,
FolderPath,
SHA256
| order by WTime desc
Registry breadcrumbs / ASEP anomalies
DeviceRegistryEvents
| where Timestamp > ago(30d)
| where RegistryKey has @"\SOFTWARE\Classes\Local Settings\Software\Microsoft"
| where RegistryValueName in~ (
"UCID",
"UFID",
"XJ01",
"XJ02",
"UXMP"
)
| where ActionType in (
"RegistryValueSet",
"RegistryValueDeleted"
)
| where InitiatingProcessFileName in~ (
"AcroServicesUpdater2_x64.exe",
"DlpUserAgent.exe",
"ADNotificationManager.exe",
"werfault.exe"
)
| project
Timestamp,
DeviceName,
ActionType,
RegistryKey,
RegistryValueName,
RegistryValueData,
PreviousRegistryValueData,
InitiatingProcessFileName
| order by Timestamp desc
Non‑browser process → API‑Gateway → internal AD and WinRm protocols
let _timeFrame = 10m;
let _net1 =
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has ".execute-api."
| where isnotempty(InitiatingProcessFileName)
| where InitiatingProcessFileName !in~ (
"chrome.exe",
"msedge.exe",
"firefox.exe",
"opera.exe",
"brave.exe",
"iexplore.exe"
)
| where InitiatingProcessFolderPath !startswith "C:\\Program Files"
and InitiatingProcessFolderPath !startswith "C:\\Program Files (x86)"
| project
DeviceName,
Proc=InitiatingProcessFileName,
ProcPath=InitiatingProcessFolderPath,
AccountName=InitiatingProcessAccountName,
OutTime=Timestamp,
RemoteUrl,
C2IP=RemoteIP;
let _net2 =
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemotePort in (135, 389, 445, 636, 5985, 5986)
| where isnotempty(InitiatingProcessFileName)
| where InitiatingProcessFileName !in~ (
"chrome.exe",
"msedge.exe",
"firefox.exe",
"svchost.exe",
"lsass.exe",
"System"
)
| project
DeviceName,
Proc=InitiatingProcessFileName,
InTime=Timestamp,
InternalIP=RemoteIP,
RemotePort;
_net1
| join kind=inner _net2 on DeviceName, Proc
| where InTime between (OutTime .. (OutTime + (_timeFrame)))
| project
DeviceName,
Proc,
ProcPath,
AccountName,
OutTime,
RemoteUrl,
C2IP,
InTime,
InternalIP,
RemotePort
| order by InTime desc
DLL side‑loading through trusted signed applications → HTTPs and WinRm outbound connections
let _timeFrame = 10m;
let _c2 =
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ (
"AcroServicesUpdater2_x64.exe",
"DlpUserAgent.exe",
"ADNotificationManager.exe",
"werfault.exe"
)
| where RemotePort == 443
| project
DeviceName,
C2Time=Timestamp,
InitiatingProcessFileName,
C2IP=RemoteIP,
C2Url=RemoteUrl;
let _winrm =
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ (
"AcroServicesUpdater2_x64.exe",
"DlpUserAgent.exe",
"ADNotificationManager.exe",
"werfault.exe"
)
| where RemotePort in (5985, 5986)
| project
DeviceName,
WinRMTime=Timestamp,
InitiatingProcessFileName,
WinRMTarget=RemoteIP,
WinRMPort=RemotePort;
_c2
| join kind=inner _winrm on DeviceName, InitiatingProcessFileName
| where WinRMTime between (C2Time .. (C2Time + (_timeFrame)))
| project
DeviceName,
InitiatingProcessFileName,
C2Time,
C2IP,
C2Url,
WinRMTime,
WinRMTarget,
WinRMPort
| order by C2Time desc
PowerShell history deletion
DeviceFileEvents
| where Timestamp > ago(30d)
| where FileName =~ "ConsoleHost_history.txt"
| where ActionType == "FileDeleted"
| where InitiatingProcessFileName in~ (
"AcroServicesUpdater2_x64.exe",
"DlpUserAgent.exe",
"ADNotificationManager.exe",
"werfault.exe",
"cmd.exe",
"powershell.exe",
"pwsh.exe"
)
| project
Timestamp,
DeviceName,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
FolderPath
| order by Timestamp desc
Reconnaissance burst (cmd / PowerShell)
let _reconWindow = 10m;
let _rmm =
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("QuickAssist.exe", "AnyDesk.exe", "TeamViewer.exe")
| project DeviceName, RMMTime=Timestamp;
let _recon =
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("cmd.exe", "powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (
"whoami",
"nltest",
"net user",
"ipconfig /all",
"arp -a",
"route print",
"query user",
"whoami /all",
"whoami /groups",
"whoami /priv"
)
| where InitiatingProcessFileName !in~ (
"m_agent_service.exe",
"updater.exe",
"msiexec.exe",
"asussplendid.exe",
"icloudhome.exe",
"dropbox.exe",
"code.exe",
"TiWorker.exe",
"MsMpEng.exe",
"ccmexec.exe",
"cscript.exe",
"wscript.exe"
)
| where InitiatingProcessFileName !has "agent"
and InitiatingProcessFileName !has "update"
and InitiatingProcessFileName !has "install"
| project
Timestamp,
DeviceName,
FileName,
InitiatingProcessFileName,
ProcessCommandLine;
_rmm
| join kind=inner _recon on DeviceName
| where Timestamp between (RMMTime .. (RMMTime + (_reconWindow)))
| summarize
RMMTime=min(RMMTime),
eventCount=count(),
FileNames=make_set(FileName),
InitiatingProcessFileNames=make_set(InitiatingProcessFileName),
ProcessCommandLines=make_set(ProcessCommandLine, 5)
by DeviceName
| where eventCount > 2
| project
DeviceName,
RMMTime,
eventCount,
FileNames,
InitiatingProcessFileNames,
ProcessCommandLines
| order by eventCount desc
Data exfiltration
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName =~ "rclone.exe" or ProcessVersionInfoOriginalFileName =~ "rclone.exe"
| where ProcessCommandLine has_all (
"copy ",
"--config rclone_uploader.conf",
"--transfers 16",
"--checkers 16",
"--buffer-size 64M",
"--max-age=3y",
"--exclude *.mdf"
)
Quick Assist–anchored recon (no staging writes within 10 minutes)
let _reconWindow = 10m;
let _stageWindow = 15m;
let _rmm =
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("QuickAssist.exe", "AnyDesk.exe", "TeamViewer.exe")
| project DeviceName, RMMTime=Timestamp;
let _recon =
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("cmd.exe", "powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (
"whoami",
"whoami /all",
"whoami /groups",
"whoami /priv",
"nltest",
"net user",
"net localgroup",
"query user",
"ipconfig /all",
"arp -a",
"route print"
)
| where InitiatingProcessFileName !in~ (
"m_agent_service.exe",
"updater.exe",
"msiexec.exe",
"asussplendid.exe",
"icloudhome.exe",
"dropbox.exe",
"code.exe",
"explorer.exe",
"TiWorker.exe",
"MsMpEng.exe",
"ccmexec.exe",
"GoogleUpdate.exe",
"setup.exe"
)
| where InitiatingProcessFileName !has "agent"
and InitiatingProcessFileName !has "update"
and InitiatingProcessFileName !has "install"
and InitiatingProcessFileName !has "deploy"
| where ProcessCommandLine !has "Program Files"
and ProcessCommandLine !has "uninstall"
and ProcessCommandLine !has "InventoryInstall"
and ProcessCommandLine !has "spool\\drivers"
and ProcessCommandLine !has "AppData\\Roaming\\Apple"
and ProcessCommandLine !has "AppData\\Local\\Apple"
| project
DeviceName,
ReconTime=Timestamp,
ReconCmd=ProcessCommandLine,
ReconProc=FileName,
InitiatingProc=InitiatingProcessFileName;
let _staging =
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated", "FileRenamed")
| where FileName endswith ".zip"
or FileName endswith ".exe"
or FileName endswith ".dll"
| where FolderPath !has "\\Program Files"
and FolderPath !has "\\Windows\\Temp"
and FolderPath !has "\\Google\\"
and FolderPath !has "\\Apple\\"
| project
DeviceName,
STime=Timestamp,
StageFile=FileName,
StagePath=FolderPath;
let _rmmRecon =
_rmm
| join kind=inner _recon on DeviceName
| where ReconTime between (RMMTime .. (RMMTime + (_reconWindow)))
| project DeviceName, RMMTime, ReconTime, ReconProc, ReconCmd, InitiatingProc;
_rmmRecon
| join kind=leftouter _staging on DeviceName
| extend HasStagingInWindow = iff(
STime between (RMMTime .. (RMMTime + (_stageWindow))), 1, 0)
| summarize HasStagingInWindow=max(HasStagingInWindow)
by DeviceName, RMMTime, ReconTime, ReconProc, ReconCmd, InitiatingProc
| where HasStagingInWindow == 0
| project
DeviceName,
RMMTime,
ReconTime,
ReconProc,
ReconCmd,
InitiatingProc
| order by ReconTime desc