// Hosts in your estate that look like they run ARTEX (The Tokyo Nomad, 4 Nov 2026). Read-only; last 24 hours. // Not executed in a live tenant; checked with Microsoft's Kusto parser against Microsoft Learn schemas. // Defaults from the public artex-global source at v0.3.15-global.1: binary "artex", UI on 8787, recording proxy on 8788, // tools started with "bash -c" (a simple command replaces bash, so its parent is artex), updater calls api.github.com. // Leads, not verdicts: a renamed binary or new -addr/-proxy flags defeats these checks, and an approved // red-team host will match. Confirm the owner and authorisation before containment. let Window = 24h; let ArtexNames = dynamic(["artex", "artex.exe"]); let Listeners = DeviceNetworkEvents | where TimeGenerated > ago(Window) | where ActionType == "ListeningConnectionCreated" and LocalPort in (8787, 8788) | project TimeGenerated, DeviceId, DeviceName, Evidence=strcat("listening on ", LocalIP, ":", tostring(LocalPort), " by ", InitiatingProcessFileName), Detail=InitiatingProcessCommandLine; let Children = DeviceProcessEvents | where TimeGenerated > ago(Window) | where InitiatingProcessFileName in~ (ArtexNames) | project TimeGenerated, DeviceId, DeviceName, Evidence=strcat("artex started ", FileName), Detail=ProcessCommandLine; let Outbound = DeviceNetworkEvents | where TimeGenerated > ago(Window) | where InitiatingProcessFileName in~ (ArtexNames) or InitiatingProcessParentFileName in~ (ArtexNames) | where RemoteIP == "169.254.169.254" or RemoteUrl has "api.github.com" | project TimeGenerated, DeviceId, DeviceName, Evidence=strcat(InitiatingProcessFileName, " connected to ", iff(isempty(RemoteUrl), RemoteIP, RemoteUrl)), Detail=InitiatingProcessCommandLine; union Listeners, Children, Outbound | summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Events=count(), Evidence=make_set(Evidence, 20), Details=make_set(Detail, 5) by DeviceId, DeviceName | order by LastSeen desc | take 100