CaptiveCrunch — Storm-2945 / Midnight Blizzard captive-portal AiTM

Hospitality-network captive portals manipulated to deliver the CornFlake Go RAT and the ChocoShell PowerShell infostealer, then pivot to Microsoft 365 via Entra device code phishing.
Threat
CaptiveCrunch (Storm-2945)
Severity
ACTIVE NATION-STATE
Type
AiTM · RAT · Infostealer · OAuth abuse
Access
Captive-portal DNS/HTTP manipulation
Version
v0.1 · 2026-08-04
Author
HuntPack
Confidence
High (single Tier-1 vendor, 4 corroborating)
01

Executive Summary

Storm-2945, assessed by Microsoft as a sub-cluster of Midnight Blizzard (APT29 / Cozy Bear, attributed to Russia's SVR), has since early May 2026 been manipulating DNS and HTTP traffic on hospitality-sector captive-portal networks worldwide. The attacker objective is not the hotel: it is the corporate traveller's laptop and, through it, the traveller's Microsoft 365 tenant. Venue Wi-Fi is simply the position from which an adversary-in-the-middle can rewrite what a managed endpoint sees the moment it joins a network.

The delivery trick is what makes this campaign efficient. Rather than waiting for a user to browse somewhere, the actor intercepts the automated connectivity check that Windows and browsers issue on joining a network (NCSI), and answers it with a ClickFix-style page impersonating Windows Update, a browser update, DirectX or a human-verification check. The user is instructed to copy a command and run it themselves, which sidesteps both the download-mark-of-the-web path and any "don't click attachments" training. Two payloads follow: CornFlake, a Go Windows RAT that installs as a service named svchost32 with the display name "Cloud Sync Service" and re-installs its own persistence via a watchdog, and ChocoShell, an in-memory PowerShell infostealer that bypasses AMSI and UAC and harvests browser cookies, saved passwords, Wi-Fi keys and Microsoft 365 / Entra tokens. FruitStone is the operator-side web C2 panel — it never touches a victim endpoint, so no endpoint detection is written for it here.

Since 2026-07-16 a portion of the landing pages skip malware entirely and run Entra device code phishing: the user is sent to the genuine Microsoft sign-in page and told to type an actor-supplied code, authenticating the attacker's session and enabling attacker device registration plus Microsoft Graph collection. That branch leaves no endpoint artefact at all.

Highest-value defensive angle: the endpoint half of this campaign is loud and cheap to catch — a non-Microsoft binary called svchost32.exe running out of %APPDATA% and registering a service is close to a zero-false-positive signal (Q1–Q3). The identity half is the one that will actually hurt you, because it survives reimaging the laptop. Block or scope the device code flow, and treat any device-code sign-in from a traveller as an incident until proven otherwise.

Defender priority: Deploy Q1, Q2, Q3 and Q5 today — they are high-confidence and low-noise. Then, before the next travel week, block the Entra device code flow via Conditional Access (or scope it to a named app allowlist) and hunt Q13 across the last 90 days. An endpoint you reimage is recoverable; a refresh token and an attacker-registered device are not, until you revoke them.

02

Source Review & Web Hunter Notes

TierSourceKey findingAtomic IOCsCarry forward
1Microsoft Threat Intelligence — CaptiveCrunch advisory (2026-07-31)Full campaign write-up: attribution, chain, CornFlake / ChocoShell / FruitStone internals, mitigations, hunting guidance, IOC tableYES — all 12Yes
2Infosecurity MagazineCorroborates campaign name, actor, timeline, NCSI-interception delivery, Android APK variant, device-code phase from 2026-07-16None printedNarrative only
2Security AffairsCorroborates service name svchost32 / "Cloud Sync Service", ECDH P-256 C2, netsh wlan show profile, _.tbres token cache, cookies.sqliteArtefact names onlyBehavioural detail
2Wiz Threat Intelligence incident trackerIndependent tracking entry; confirms Entra ID as the primary targeted technology and the hospitality captive-portal vectorNone printedScoping only
2Check Point Research weekly report (2026-08-03)Independent confirmation of actor, both malware names, and the M365 / Entra token-theft objectiveNone printedCorroboration only

Decisions taken during research

  • Every atomic indicator in this pack comes from one source. Microsoft is the only publisher of the hashes, domains and IPs; the four corroborating sources carry the narrative but print no indicators. The two-independent-source bar for a blocking action is therefore not met, so nothing ships at action=prevent / severity=critical. All atomic rows are detect / high. Promote to prevent only after a second source or an internal confirmed hit.
  • FruitStone gets no endpoint detection. It is the operator's browser-based C2 console. Writing endpoint logic for it would generate a coverage claim the telemetry cannot support.
  • Name collision, deliberately noted. This campaign's CornFlake is a Golang Windows RAT. It is not the Interlock-associated CORNFLAKE loader covered elsewhere in the library — different family, different infrastructure, no shared indicators. Do not merge the two IOC sets or reuse the other pack's detections here.
  • Prompt-injection screen: clean. All five fetched pages were checked for text attempting to direct the research process (instructions to run commands, to fetch a further URL for "the real IOCs", or claims of prior authorisation). None was present in any source. No source was disqualified.
  • Perishability. Captive-portal infrastructure and C2 IPs rotate fast; Microsoft describes proxy relays with health checks and rotation built into the operator panel. Treat the six IPs as 90-day indicators and the behavioural queries as the durable coverage.
03

Hunt Brief & Attack Chain

Attack chain

#StepTelemetryHunt angle
1Captive-portal network manipulates DNS / HTTP for connected guestsOff-endpoint (venue infrastructure)No endpoint telemetry — travel-correlated logon review only
2OS/browser connectivity check (NCSI) intercepted; ClickFix page servedDnsRequest, browser process activityExecutable written or shell spawned shortly after a network-join event
3User copies and runs the supplied command (paste-and-run)ProcessRollup2Q8 — browser-parented shell with paste-and-run hallmarks
4CornFlake dropper shows a fake progress window, copies to %APPDATA%\svchost32\svchost32.exeProcessRollup2, PeFileWrittenQ1, Q5
5Service svchost32 registered as "Cloud Sync Service"CreateServiceQ2
6Registry Run key + scheduled task persistence, watchdog restores removalsAsepValueUpdate, ScheduledTaskRegisteredQ3, Q4
7ChocoShell executes in memory; AMSI bypass; three-tier UAC bypassProcessRollup2, RegGenericValueUpdateQ11
8Browser cookie / password theft (CDP debug port, ChromeKatz, ABE bypass)ProcessRollup2Q10, Q12
9Wi-Fi credential harvest via netsh wlan show profile key=clearProcessRollup2Q9
10M365 / Entra token theft from Token Broker cache (_.tbres, WAM)ProcessRollup2Q12
11Encrypted ECDH P-256 C2 to actor infrastructureDnsRequest, NetworkConnectIP4Q6, Q7
12Entra device code phishing; attacker device registration; Graph collectionEntra sign-in / audit logsQ13 + native hunts

Hunt hypotheses (ordered by fidelity)

#HypothesisMITREFalcon eventsExpected FPConf
H1A process named svchost32 is executing from a user-writable path — no legitimate Microsoft binary uses that nameT1036.005ProcessRollup2None expectedHIGH
H2A service is registered with the display name "Cloud Sync Service" or an image path containing svchost32T1543.003CreateServiceNone expectedHIGH
H3ASEP / Run-key or service-key registry writes reference svchost32T1547.001AsepValueUpdate, RegGenericValueUpdateNone expectedHIGH
H4Either published SHA256 executes or is written to diskT1204ProcessRollup2, PeFileWrittenNoneHIGH
H5An endpoint resolves or connects to published Storm-2945 C2 infrastructureT1071.001DnsRequest, NetworkConnectIP4Sinkhole / TI-proxy rescansHIGH
H6A browser process directly parents an interpreter carrying paste-and-run hallmarks (ClickFix)T1204.004, T1059.001ProcessRollup2Developer tooling, IT self-service portalsMEDIUM
H7A scheduled task is registered whose executable lives under %APPDATA%T1053.005ScheduledTaskRegisteredUpdaters (Chrome, Teams, Zoom, Slack)MEDIUM
H8Wi-Fi profile keys are dumped in cleartextT1555ProcessRollup2Helpdesk diagnostics, MDM scriptsMEDIUM
H9A browser is relaunched with a remote debugging port (CDP cookie theft)T1539ProcessRollup2Developers, automated UI testingMEDIUM
H10UAC-bypass registry hijacks are written (windir env var, COM DelegateExecute, Folder shell open command)T1548.002RegGenericValueUpdateRare; some installers touch ClassesMEDIUM
H11A process references browser credential stores or the Entra Token Broker cache by pathT1555.003, T1528ProcessRollup2Backup agents, browser migration toolsMEDIUM
H12Device code sign-ins appear for users who do not normally use that flow, or from travel geographiesT1528, T1098.005Entra sign-in logsAzure CLI, kiosk/IoT enrolmentMEDIUM

Attack-chain steps with no mappable Falcon telemetry

  • Step 1 — venue-side DNS/HTTP manipulation. Happens on infrastructure you do not own or instrument. There is no sensor event for "the captive portal lied to me". Compensate with the travel-correlation native hunt in §7 and with hardening (§12) rather than pretending detection coverage exists.
  • Step 2 — NCSI interception. The Falcon sensor does not surface the connectivity-check request as a distinct event, so the "file created within 2 minutes of an NCSI probe" query Microsoft published for Defender has no direct CQL equivalent. Q8 approximates the same moment from the process side.
  • Android APK variant. Out of scope for Windows sensor telemetry; route to your MTD/MDM stack.
04

Consolidated IOC Table

Single-source caveat. Every atomic indicator below is published by Microsoft Threat Intelligence only. No second independent source reprints them. They ship as detect, never prevent, and the behavioural signals in §10 are the durable half of this pack.

TypeValueConfActionContextExpiry
sha256918fa52ae45ed60ba7cc8bdc99c3cbe9ab92e0375ec31fc05d0d4513be11c593HIGHdetectCornFlake Go RAT sample2027-02-04
sha256be99857449d2856dd5a84e21c8a3d5e0e01456adb44062ddec5a6b4970d8d42cHIGHdetectChocoShell PowerShell infostealer2027-02-04
domainms365-device[.]comHIGHdetectDoppelganger / device-code phishing2027-02-04
domainms365-live[.]comHIGHdetectDoppelganger / device-code phishing2027-02-04
domainm365-owa[.]comHIGHdetectDoppelganger / credential capture2027-02-04
domainowa-ms365[.]comHIGHdetectDoppelganger / credential capture2027-02-04
ipv431.57.243[.]154HIGHdetectStorm-2945 C22026-11-04
ipv438.146.28[.]75HIGHdetectStorm-2945 C22026-11-04
ipv438.146.28[.]132HIGHdetectStorm-2945 C22026-11-04
ipv4104.194.159[.]150HIGHdetectStorm-2945 C22026-11-04
ipv4107.189.26[.]194HIGHdetectStorm-2945 C22026-11-04
ipv4213.145.86[.]112HIGHdetectHardcoded ChocoShell C22026-11-04
filenamesvchost32.exeHIGHhuntCornFlake payload — not a Microsoft binary name2026-11-04
filenamesync.datMEDIUMhuntCornFlake runtime config (hot-reloadable)2026-11-04
path%APPDATA%\svchost32\svchost32.exeHIGHhuntCornFlake install location2026-11-04
servicesvchost32 — "Cloud Sync Service"HIGHdetectDescription: "Synchronizes files with the cloud storage provider"2027-02-04
url-path/t/pixel.gif?m=MEDIUMhuntChocoShell beacon (tracking-pixel mimicry)2026-11-04
url-path/cdn/chunks/polyfill-7e2b.min.jsMEDIUMhuntChocoShell payload fetch2026-11-04
url-path/t/eventLOWenrichExfil POST endpoint — generic, use with a domain2026-09-04
05

Affected Surface & Telemetry Matrix

SurfaceRequired telemetryPriorityGap risk
Windows endpoints used off-network (laptops, travel fleet)Falcon sensor — ProcessRollup2, PeFileWritten, CreateService, AsepValueUpdate, ScheduledTaskRegisteredCRITICALLow — standard sensor coverage
Endpoint DNS + egressDnsRequest, NetworkConnectIP4HIGHMedium — a roaming laptop on hotel Wi-Fi produces no corporate proxy/firewall log at all; the sensor is the only witness
Microsoft Entra ID sign-in and audit logsThird-party ingest into NG-SIEM (azure_signinlogs, azure_auditlogs) or native Entra portalCRITICALHigh — this is the surface that survives endpoint reimaging, and it is the one most often not ingested
Microsoft 365 / Exchange Online auditUnified Audit Log — MailItemsAccessed, Graph activityHIGHMedium — needed to scope post-token-theft collection
Browsers (Chromium and Firefox families)Process telemetry only; cookie theft is in-memoryHIGHHigh — App-Bound Encryption bypass leaves little file-level trace
Mobile (Android) fleetMTD / MDM app-install telemetryMEDIUMHigh — outside Falcon Windows telemetry entirely
Venue / captive-portal networkNone — third-party infrastructureMEDIUMTotal — no telemetry is available; mitigate by policy, not detection
06

ATT&CK Mapping

TacticTechniqueObserved behaviourQuery / control
Initial AccessT1557 — Adversary-in-the-MiddleDNS/HTTP manipulation on captive-portal networksPolicy (§12) — no telemetry
Initial AccessT1566.002 — Spearphishing LinkDoppelganger domains served from the portalQ6
ExecutionT1204.004 — Malicious Copy-and-PasteClickFix fake-update / verification promptsQ8
ExecutionT1059.001 / T1059.003 — PowerShell / cmdChocoShell in-memory execution; CornFlake remote shellQ8, Q9, Q12
PersistenceT1543.003 — Windows ServiceService svchost32, display name "Cloud Sync Service"Q2
PersistenceT1547.001 — Registry Run KeysRun-key entries pointing at the CornFlake payloadQ3
PersistenceT1053.005 — Scheduled TaskTasks registered with TASK_LOGON_INTERACTIVE_TOKENQ4
PersistenceT1098.005 — Device RegistrationAttacker device registered in Entra after device-code phishQ13, §7
Priv. EscalationT1548.002 — Bypass UACSilentCleanup windir hijack, wsreset COM hijack, sdclt folder hijackQ11
Defense EvasionT1562.001 — Impair Defenses (AMSI)AMSI bypass via .NET reflectionQ8, §9 IOA-4
Defense EvasionT1036.005 — Match Legitimate Namesvchost32.exe imitating svchost.exe; "Cloud Sync Service"Q1, Q2
Credential AccessT1555.003 — Credentials from Web BrowsersChromeKatz, App-Bound-Encryption bypass, cookies.sqlite copyQ10, Q12
Credential AccessT1539 — Steal Web Session CookieChrome DevTools Protocol Network.getAllCookiesQ10
Credential AccessT1555 — Credentials from Password Storesnetsh wlan show profile key=clearQ9
Credential AccessT1528 — Steal Application Access TokenWAM / Token Broker _.tbres cache theft; device code flow abuseQ12, Q13
CollectionT1056.001 / T1115 / T1113 / T1123 / T1125Keylogging, clipboard, screenshots, audio, webcamQ1 (parent process), §9 IOA-1
CollectionT1025 — Data from Removable MediaUSB monitoring moduleCoverage gap — see §15
Command & ControlT1071.001 / T1573.002ECDH P-256 encrypted JSON over HTTPS; SNI spoofingQ6, Q7
ExfiltrationT1041 — Exfiltration Over C2GZip + Base64 JSON POST to /t/eventQ7
ImpactT1490 — Inhibit System RecoveryVSS shadow-copy deletion during ChocoShell cleanup§9 IOA-5
Lateral / CloudT1550.001 — Application Access TokenStolen refresh tokens replayed against Microsoft Graph§7 native hunts
07

Native Audit-Log Hunts (non-CQL)

The identity half of CaptiveCrunch is invisible to the endpoint sensor. These run in the Entra / M365 admin surfaces and are the checks that matter most after a travel period.

N1 — Device code sign-ins (Entra sign-in logs)

Filter Sign-in logs → Authentication requirements / Protocol for the device code flow over the last 90 days. Any result for a user who has never used a CLI or kiosk enrolment is suspicious. Cross-reference the source IP against the six published C2 addresses and against the traveller's expected geography. Microsoft's own alert names to watch for: "Anomalous OAuth device code authentication activity" and "Suspicious Azure authentication through possible device code phishing".

N2 — New Entra device registrations

Entra ID → Devices → All devices, sorted by registration date. Successful device-code phishing is followed by the actor registering their own device to make the session durable. Correlate every device registered during or shortly after a travel window with the owning user's known hardware. Also review Audit logs for Add registered device and Add device events.

N3 — MFA / passkey registration from unexpected locations

Audit logs → Activity: "User registered security info". An actor with a live session will try to add their own authentication method. Filter to registrations occurring outside your trusted named locations.

N4 — Post-token-theft mailbox and Graph collection

Unified Audit Log: MailItemsAccessed with a client app of Graph or an unfamiliar app ID, plus Add delegated permission grant and Consent to application in the Entra audit log. Midnight Blizzard's established pattern is Graph-based mail exfiltration once a session is held.

N5 — Travel correlation (the pivot that ties the two halves together)

Build a list of users who travelled in the affected window (early May 2026 onward). For each, check: any device-code sign-in, any new device registration, any interactive sign-in from a hospitality ASN, and any Falcon detection from §8 on their assigned endpoint. A hit on either side for a traveller warrants a full session revocation, not a triage note.

N6 — Conditional Access posture review

Confirm whether a policy blocking or scoping the device code flow exists at all. If it does not, this hunt has no compensating control behind it and §12 Immediate item 2 becomes the priority action.

08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.

No query below carries an in-query time filter. Set the window with the console time picker; each query records its intended lookback as a // LOOKBACK: comment. An @timestamp comparison against now() is rejected by the CQL parser on line 1, which silently kills the whole query.

Q1 · CornFlake execution — svchost32 masquerade
CONF HIGHFP LOWCOST LOW

Looks for: any process named svchost32. There is no legitimate Microsoft binary by that name — it exists only to sit next to svchost.exe in a process list and be overlooked. Highest-fidelity query in the pack; deploy first.

// HUNT: CornFlake RAT execution (svchost32 masquerade)
// MITRE: T1036.005, T1543.003 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: none expected -- svchost32 is not a Microsoft binary name
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^svchost32(\.exe)?$/i
| table([@timestamp, aid, ComputerName, UserName, FileName, ImageFileName, CommandLine, ParentBaseFileName, SHA256HashData])
| sort(@timestamp, order=desc)
Q2 · CornFlake service persistence — "Cloud Sync Service"
CONF HIGHFP LOWCOST LOW

Looks for: service creation with CornFlake's cover identity — display name "Cloud Sync Service", or any service image path containing svchost32. The description string Microsoft published is "Synchronizes files with the cloud storage provider".

// HUNT: CornFlake service registration ("Cloud Sync Service")
// MITRE: T1543.003 | CONF: high  FP: low  COST: low
// REQUIRES: CreateService
// NOTE: ServiceDisplayName / ServiceImagePath population varies by sensor version
//       -- (!) validate in tenant against a known-good service install first
// FALSE POSITIVES: none expected for this exact display-name string
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^CreateService$/
| ServiceDisplayName=/^Cloud\s+Sync\s+Service$/i or ServiceImagePath=/svchost32/i
| table([@timestamp, aid, ComputerName, UserName, ServiceName, ServiceDisplayName, ServiceImagePath])
| sort(@timestamp, order=desc)
Q3 · CornFlake registry persistence (Run key + service key)
CONF HIGHFP LOWCOST LOW

Looks for: ASEP or generic registry writes that name the CornFlake payload. Because the malware runs a watchdog that restores persistence a defender deletes, repeat hits on the same host after remediation are expected and are themselves confirmation the RAT is still resident.

// HUNT: CornFlake Run-key / service-key persistence
// MITRE: T1547.001, T1543.003 | CONF: high  FP: low  COST: low
// REQUIRES: AsepValueUpdate, RegGenericValueUpdate
// FALSE POSITIVES: none expected -- the string svchost32 is campaign-specific
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(AsepValueUpdate|RegGenericValueUpdate)$/
| RegStringValue=/svchost32/i or RegObjectName=/\\Services\\svchost32/i
| table([@timestamp, aid, ComputerName, UserName, RegObjectName, RegValueName, RegStringValue])
| sort(@timestamp, order=desc)
Q4 · Scheduled task executing from %APPDATA%
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: the third leg of CornFlake's persistence tripod — a scheduled task whose executable lives in a user-writable roaming/local profile path. FP: consumer-grade updaters legitimately do this (Chrome, Edge, Teams, Zoom, Slack, Dropbox, OneDrive), so this is a stack-rank hunt, not an alert. Sort by rarity of the task executable across your fleet.

// HUNT: Scheduled task registered against a user-writable AppData executable
// MITRE: T1053.005 | CONF: medium  FP: medium  COST: low
// REQUIRES: ScheduledTaskRegistered
// FALSE POSITIVES: Chrome/Edge/Teams/Zoom/Slack/Dropbox per-user updaters
// TUNING: exclude your approved per-user updaters by task executable basename,
//         e.g. TaskExecutable!=/(GoogleUpdate|MicrosoftEdgeUpdate|Update\.exe|Teams\.exe|Zoom)/i
//         then stack the survivors by count -- CornFlake will be a rare singleton
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^ScheduledTaskRegistered$/
| TaskExecutable=/\\AppData\\(Roaming|Local)\\/i
| TaskExecutable!=/(GoogleUpdate|MicrosoftEdgeUpdate|OneDrive|Teams|Zoom|Slack|Dropbox)/i
| groupBy([TaskExecutable, TaskName], function=[count(as=Hosts), collect([ComputerName, UserName, TaskAuthor])], limit=500)
| sort(Hosts, order=asc, limit=200)
Q5 · Known CaptiveCrunch payload hashes
CONF HIGHFP LOWCOST LOW

Looks for: execution or on-disk write of either published sample. Exact-hash coverage is brittle by nature — the operator panel offers per-build symbol randomisation and XOR encoding, so assume rebuilt variants exist and rely on Q1–Q3 for durable coverage.

// HUNT: CornFlake / ChocoShell known SHA256 execution or write
// MITRE: T1204 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2, PeFileWritten, NewExecutableWritten
// FALSE POSITIVES: none -- exact hash match
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|PeFileWritten|NewExecutableWritten)$/
| SHA256HashData=/^(918fa52ae45ed60ba7cc8bdc99c3cbe9ab92e0375ec31fc05d0d4513be11c593|be99857449d2856dd5a84e21c8a3d5e0e01456adb44062ddec5a6b4970d8d42c)$/i
| table([@timestamp, aid, ComputerName, UserName, FileName, ImageFileName, TargetFileName, CommandLine, SHA256HashData])
| sort(@timestamp, order=desc)
Q6 · Storm-2945 doppelganger domain resolution
CONF HIGHFP LOWCOST LOW

Looks for: DNS resolution of the four Microsoft-lookalike domains used for the phishing landing pages and device-code prompts. A roaming laptop on hotel Wi-Fi produces no corporate DNS log, so the sensor is your only witness for this step.

// HUNT: CaptiveCrunch doppelganger domain resolution
// MITRE: T1566.002, T1071.001 | CONF: high  FP: low  COST: low
// REQUIRES: DnsRequest / SuspiciousDnsRequest
// FALSE POSITIVES: TI-scanner or sandbox hosts deliberately re-resolving IOCs
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(DnsRequest|SuspiciousDnsRequest)$/
| DomainName=/^(.*\.)?(ms365-device|ms365-live|m365-owa|owa-ms365)\.com$/i
| table([@timestamp, aid, ComputerName, DomainName, ContextProcessId_decimal])
| sort(@timestamp, order=desc)
Q7 · Storm-2945 C2 egress (published IPs)
CONF HIGHFP LOWCOST LOW

Looks for: outbound connections to the six published C2 addresses. 213.145.86[.]112 is the hardcoded ChocoShell C2 and is the highest-value of the six. The initiating process name in ContextBaseFileName is the pivot — expect svchost32.exe or powershell.exe.

// HUNT: CaptiveCrunch C2 IP egress
// MITRE: T1071.001, T1041 | CONF: high  FP: low  COST: low
// REQUIRES: NetworkConnectIP4
// FALSE POSITIVES: TI-proxy or scanner infrastructure re-checking indicators
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^NetworkConnectIP4$/
| in(RemoteAddressIP4, values=["31.57.243.154", "38.146.28.75", "38.146.28.132", "104.194.159.150", "107.189.26.194", "213.145.86.112"])
| table([@timestamp, aid, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort, Protocol_decimal])
| sort(@timestamp, order=desc)
Q8 · ClickFix — browser-parented interpreter (paste-and-run)
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: the moment the ClickFix lure succeeds — an interpreter spawned directly by a browser (or by explorer.exe, the Run-dialog path) carrying the hallmarks of a pasted one-liner. This is the closest CQL gets to Microsoft's "file created within 2 minutes of an NCSI probe" query. FP: developer tooling and IT self-service portals legitimately launch scripts from a browser.

// HUNT: ClickFix paste-and-run -- browser/Run-dialog parented interpreter
// MITRE: T1204.004, T1059.001, T1059.003 | CONF: medium  FP: medium  COST: medium
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: developer workstations, IT self-service / RMM web consoles,
//                  browser-launched installer helpers
// TUNING: exclude your engineering OU or known dev hosts by ComputerName pattern,
//         and exclude your RMM/self-service agent by ParentBaseFileName before
//         promoting this to an alert. Triage newest-first; a real ClickFix hit is
//         followed within seconds by a write under %APPDATA% (pivot to Q1).
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(msedge|chrome|firefox|brave|opera|iexplore|explorer)\.exe$/i
| FileName=/^(powershell|pwsh|cmd|mshta|wscript|cscript|curl|certutil|conhost)\.exe$/i
| CommandLine=/(-nop\b|-noprofile\b|-w\s+hidden|-windowstyle\s+hidden|-enc\b|-encodedcommand\b|iex\b|invoke-expression|invoke-webrequest|downloadstring|frombase64string|scriptblock)/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)
Q9 · ChocoShell — Wi-Fi credential harvesting
CONF HIGHFP MEDIUMCOST LOW

Looks for: cleartext dumping of stored Wi-Fi profile keys — a distinctive ChocoShell collection step and, for a travel-focused actor, an obvious one to want. FP: helpdesk diagnostic scripts and MDM/RMM Wi-Fi inventory jobs use the identical command.

// HUNT: Wi-Fi profile key dump (netsh wlan show profile key=clear)
// MITRE: T1555 | CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: helpdesk diagnostics, MDM/RMM wireless inventory scripts
// TUNING: exclude your RMM agent and support tooling by ParentBaseFileName
//         (e.g. ParentBaseFileName!=/(ninjarmm|screenconnect|intune|ccmexec)/i)
//         -- a genuine ChocoShell hit is parented by powershell.exe with no
//         management agent anywhere in the tree
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^netsh\.exe$/i
| CommandLine=/wlan\s+show\s+profile/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, GrandparentBaseFileName, CommandLine])
| sort(@timestamp, order=desc)
Q10 · Browser relaunched with a remote debugging port (CDP cookie theft)
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: the App-Bound-Encryption workaround — rather than decrypting the cookie store, ChocoShell restarts the browser with a debugging port and asks it politely for the cookies via Network.getAllCookies, then restores the session with --restore-last-session so the user notices nothing. FP: developers and UI test automation do exactly this.

// HUNT: Chromium relaunch with remote debugging port (CDP cookie extraction)
// MITRE: T1539, T1555.003 | CONF: medium  FP: medium  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: web developers, Selenium/Playwright/Puppeteer test automation
// TUNING: exclude engineering hosts and CI runners by ComputerName; a malicious
//         relaunch is parented by powershell.exe or svchost32.exe rather than by
//         a shell, an IDE, or a node.exe test harness -- filter on
//         ParentBaseFileName before alerting
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^(chrome|msedge|brave|opera|vivaldi)\.exe$/i
| CommandLine=/--remote-debugging-(port|pipe)/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)
Q11 · ChocoShell UAC-bypass registry hijacks
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: ChocoShell's three-tier UAC bypass — the windir environment variable hijack that poisons the SilentCleanup task, the wsreset.exe COM handler hijack under HKCU\Software\Classes, and the sdclt.exe Folder shell-open-command hijack. FP: a small number of installers legitimately write under HKCU\Software\Classes; the windir and DelegateExecute values are the discriminating ones.

// HUNT: UAC bypass via HKCU windir / COM DelegateExecute / Folder shell hijack
// MITRE: T1548.002, T1112 | CONF: medium  FP: medium  COST: low
// REQUIRES: AsepValueUpdate, RegGenericValueUpdate, RegSystemConfigValueUpdate
// FALSE POSITIVES: installers registering file associations under HKCU Classes
// TUNING: exclude msiexec.exe and your software-deployment agent as the writing
//         process; a HKCU\Environment "windir" write has essentially no benign
//         use -- treat that value alone as high severity and the Classes writes
//         as the lower-confidence tier
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(AsepValueUpdate|RegGenericValueUpdate|RegSystemConfigValueUpdate)$/
| RegValueName=/^(windir|DelegateExecute)$/i or RegObjectName=/\\Classes\\(Folder|ms-settings|mscfile|Applications\\wsreset)\\/i
| table([@timestamp, aid, ComputerName, UserName, RegObjectName, RegValueName, RegStringValue])
| sort(@timestamp, order=desc)
Q12 · Credential-store and Entra token-cache targeting
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: command lines naming browser credential stores or the Microsoft Token Broker cache. The _.tbres files are where WAM caches Microsoft 365 access and refresh tokens — the artefact that converts an endpoint compromise into a cloud compromise. FP: profile-migration utilities and endpoint backup agents enumerate the same paths.

// HUNT: browser credential store / Entra Token Broker cache targeting
// MITRE: T1555.003, T1528, T1539 | CONF: medium  FP: medium  COST: medium
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// FALSE POSITIVES: profile migration tools, endpoint backup agents, browser
//                  import wizards run by the user
// TUNING: exclude your backup/migration agent by FileName and ParentBaseFileName;
//         the malicious pattern is powershell.exe touching several of these paths
//         in one command line -- a single path reference alone is weak evidence
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/(tbres|TokenBroker|cookies\.sqlite|logins\.json|key4\.db|Login\s?Data|Local\s+State|Web\s+Data)/i
| FileName!=/^(msedge|chrome|firefox|brave|opera|vivaldi)\.exe$/i
| table([@timestamp, aid, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine])
| sort(@timestamp, order=desc)
Q13 · Entra device code sign-in anomaly
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: device code authentication events in ingested Entra sign-in logs, stacked by user, source IP and app. This is the only query covering the branch of the campaign that leaves no endpoint artefact. FP: Azure CLI, device enrolment and kiosk/IoT provisioning use the same flow legitimately.

SaaS / IdP field names are parser-prefixed per tenant. Run this against a single sample row and adjust the field names before deploying it broadly — (!) validate in tenant.

// HUNT: Entra device code authentication anomaly (CaptiveCrunch device-code phish)
// MITRE: T1528, T1550.001, T1098.005 | CONF: medium  FP: medium  COST: medium
// REQUIRES: Entra sign-in logs ingested to NG-SIEM (azure_signinlogs)
// NOTE: (!) validate in tenant -- IdP field names are parser-prefixed and vary
// FALSE POSITIVES: Azure CLI / azd, Intune or Autopilot enrolment, kiosk and IoT
//                  provisioning, developer sign-ins on headless hosts
// TUNING: exclude your sanctioned device-code client app IDs and automation
//         service accounts, then review what remains user-by-user. Any hit for a
//         user who travelled since 2026-05-01 is an incident until disproven.
// LOOKBACK: 90d (set in the console time picker)
#repo=azure_signinlogs
| authenticationProtocol=/deviceCode/i
| groupBy([userPrincipalName, ipAddress, appDisplayName, conditionalAccessStatus], function=[count(as=SignIns), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=1000)
| sort(SignIns, order=desc, limit=200)
09

CrowdStrike Custom IOA Recommendations

Promote only the low-FP queries to blocking IOAs. Q4, Q8, Q9, Q10, Q11, Q12 and Q13 stay hunt-only until baselined in your environment.

IOATypePatternActionExclusions
IOA-1 · CornFlake payload executionProcess CreationImage filename matches svchost32\.exe and grandparent is not a Microsoft-signed installerBlock & TerminateNone required
IOA-2 · Cloud Sync Service registrationProcess Creationsc.exe / powershell.exe command line containing Cloud Sync Service together with a binPath under AppDataDetect — CriticalSoftware deployment agent as parent
IOA-3 · Wi-Fi key dumpProcess Creationnetsh.exe with wlan show profile and key=clearDetect — MediumRMM / helpdesk agent in the process tree
IOA-4 · AMSI tampering by reflectionProcess CreationPowerShell command line referencing amsiInitFailed, AmsiUtils or System.Management.Automation.AmsiUtilsDetect — HighSecurity-testing OU only
IOA-5 · Shadow-copy deletion via WMIProcess Creationwmic or PowerShell CIM call deleting Win32_ShadowCopy instancesBlock & TerminateBackup vendor's own maintenance binary
IOA-6 · Browser debug-port relaunch by a non-shell parentProcess CreationChromium binary with --remote-debugging-port parented by powershell.exeDetect — MediumEngineering hosts, CI runners

Deployment path: Falcon console → Endpoint security → Custom IOA rule groups. Stage every rule in Monitor for one full business cycle (including a travel week) before switching to Block, then confirm the rule group is assigned to the prevention policy that actually covers your travel fleet — laptops are frequently in a looser policy than desktops, which is precisely the population this campaign targets.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
# CaptiveCrunch / Storm-2945 -- Falcon IOC Management import
# Source: Microsoft Threat Intelligence advisory 2026-07-31 (snapshot 01)
# SINGLE-SOURCE: Microsoft is the only publisher of these indicators. The
# two-independent-source bar for a blocking action is NOT met, so nothing here
# ships at action=prevent / severity=critical. Promote only after a second
# source or an internally confirmed hit.
sha256,918fa52ae45ed60ba7cc8bdc99c3cbe9ab92e0375ec31fc05d0d4513be11c593,detect,high,2027-02-04,CornFlake Go RAT payload,campaign:CaptiveCrunch actor:Storm-2945
sha256,be99857449d2856dd5a84e21c8a3d5e0e01456adb44062ddec5a6b4970d8d42c,detect,high,2027-02-04,ChocoShell PowerShell infostealer,campaign:CaptiveCrunch actor:Storm-2945
domain,ms365-device.com,detect,high,2027-02-04,Device-code phishing doppelganger,campaign:CaptiveCrunch actor:Storm-2945
domain,ms365-live.com,detect,high,2027-02-04,Device-code phishing doppelganger,campaign:CaptiveCrunch actor:Storm-2945
domain,m365-owa.com,detect,high,2027-02-04,Credential-capture doppelganger,campaign:CaptiveCrunch actor:Storm-2945
domain,owa-ms365.com,detect,high,2027-02-04,Credential-capture doppelganger,campaign:CaptiveCrunch actor:Storm-2945
ipv4,31.57.243.154,detect,high,2026-11-04,Storm-2945 C2,campaign:CaptiveCrunch actor:Storm-2945
ipv4,38.146.28.75,detect,high,2026-11-04,Storm-2945 C2,campaign:CaptiveCrunch actor:Storm-2945
ipv4,38.146.28.132,detect,high,2026-11-04,Storm-2945 C2,campaign:CaptiveCrunch actor:Storm-2945
ipv4,104.194.159.150,detect,high,2026-11-04,Storm-2945 C2,campaign:CaptiveCrunch actor:Storm-2945
ipv4,107.189.26.194,detect,high,2026-11-04,Storm-2945 C2,campaign:CaptiveCrunch actor:Storm-2945
ipv4,213.145.86.112,detect,high,2026-11-04,ChocoShell hardcoded C2,campaign:CaptiveCrunch actor:Storm-2945
Behavioural signaturesdurable coverage
CornFlake -- persistence tripod (all three, plus a watchdog that restores them)
  1. Windows service  : svchost32  /  display "Cloud Sync Service"
     description      : "Synchronizes files with the cloud storage provider"
  2. Registry Run key : value data referencing svchost32
  3. Scheduled task   : executable under the user profile, interactive-token logon
  install path        : %APPDATA%\svchost32\svchost32.exe
  runtime config      : sync.dat (hot-reloadable, sits beside the payload)
  local control API   : loopback HTTP endpoints /upload, /reload, /status
  C2 crypto           : ECDH P-256 + SHA-256 session key, custom JSON over TLS

ChocoShell -- in-memory PowerShell, no file on disk
  AMSI bypass         : .NET reflection against AmsiUtils / amsiInitFailed
  VM check            : timing-based sandbox detection before staging
  UAC bypass chain    : HKCU\Environment "windir" (SilentCleanup task hijack)
                        -> wsreset.exe COM handler under HKCU\Software\Classes
                        -> sdclt.exe HKCU\Software\Classes\Folder\shell\open\command
                        -> visible UAC prompt as last resort
  Chromium creds      : App-Bound Encryption bypass via SYSTEM token impersonation
                        stolen from winlogon.exe / wininit.exe / services.exe
  Cookie theft        : browser relaunch with --remote-debugging-port, then
                        Chrome DevTools Protocol Network.getAllCookies, then
                        --restore-last-session to hide the interruption
  Firefox creds       : direct copy of cookies.sqlite / logins.json / key4.db
  M365 tokens         : _.tbres files from the WAM Token Broker cache
  Wi-Fi creds         : netsh wlan show profile key=clear
  Anti-forensics      : VSS shadow-copy deletion via WMI, UAC-bypass key removal

Delivery -- ClickFix from an intercepted connectivity check
  Lure themes         : winupdate, defender, directx, vcredist, sysopt, netfix,
                        browser, pdfview  (fake progress windows)
  Trigger             : OS/browser NCSI connectivity probe answered by the portal
  User action         : copy-and-paste a command, run it manually
  Android variant     : APK served to mobile clients (out of Windows telemetry)
Named tooling & host artefactstriage checklist
Malware names
  CornFlake   Golang Windows RAT  -- endpoint
  ChocoShell  PowerShell stealer  -- endpoint, in-memory
  FruitStone  web C2 panel        -- OPERATOR SIDE ONLY, no endpoint detection

  NAME COLLISION: this CornFlake is NOT the Interlock-associated CORNFLAKE
  loader. Different family, different infrastructure, no shared indicators.
  Do not merge the two IOC sets.

Filesystem artefacts to check on a suspect host
  %APPDATA%\svchost32\                      directory
  %APPDATA%\svchost32\svchost32.exe         payload
  %APPDATA%\svchost32\sync.dat              runtime config

Registry artefacts to check
  SYSTEM\CurrentControlSet\Services\svchost32
  HKCU\Environment  ->  windir
  HKCU\Software\Classes\Folder\shell\open\command
  HKCU\Software\Classes  (wsreset COM handler)

C2 URL paths (pair with a domain or IP before alerting -- generic on their own)
  /t/pixel.gif?m=                      ChocoShell beacon
  /cdn/chunks/polyfill-7e2b.min.js     payload fetch
  /t/event                             exfil POST endpoint

Operator infrastructure tradecraft (informs hunting, not blocking)
  Proxy relays with health checks and rotation
  Beacon profiles with SNI spoofing toward common SaaS hostnames
  DNS fallback channel
  Per-build symbol randomisation and XOR encoding -- assume hash churn
Entra / Conditional Access auditidentity half
Conditional Access audit -- run before the next travel week
  [ ] A policy exists that BLOCKS the device code flow for all users
  [ ] If the flow is needed, it is scoped to a named app allowlist + trusted
      network, not left open tenant-wide
  [ ] Phishing-resistant MFA is required for every privileged role
  [ ] MFA and passkey registration is restricted to trusted locations/devices
  [ ] Sign-in risk policy is enabled and set to block or force reauth
  [ ] Device registration requires MFA and is restricted to compliant devices
  [ ] Legacy authentication is blocked

Entra hunts to run over the last 90 days
  [ ] Sign-in logs: authentication protocol = device code, any user
  [ ] Sign-in logs: successful sign-in from any of the 6 published C2 IPs
  [ ] Audit logs: "Add registered device" / "Add device"
  [ ] Audit logs: "User registered security info" outside trusted locations
  [ ] Audit logs: "Consent to application" / "Add delegated permission grant"
  [ ] Unified Audit Log: MailItemsAccessed via Graph by an unfamiliar app ID

Response if any of the above hits for a traveller
  [ ] Revoke all refresh tokens for the user (revokeSignInSessions)
  [ ] Remove attacker-registered devices
  [ ] Reset credentials and re-enrol MFA in person
  [ ] Review mailbox rules, forwarding, and delegate access
  [ ] Isolate and reimage the endpoint -- CornFlake restores its own persistence
11

Detection Validation Gates

GateCheckPass criterion
G1 · Telemetry readyRun each query's bare event filter alone (e.g. #event_simpleName=/^CreateService$/) over 7 daysNon-zero results on your travel fleet. A zero return means the event is not flowing and the query is decorative.
G2 · Entra ingestRun #repo=azure_signinlogs aloneRows returned, and field names match those used in Q13. If not ingested, Q13 and the whole identity half of this pack are non-functional — fix ingest before anything else.
G3 · Benign baselineRun Q4, Q8, Q9, Q10, Q11, Q12 for 14 days with no exclusionsRecord the FP population per query and write the exclusions into the // TUNING: line before promoting anything.
G4 · Positive test — serviceIn a lab VM, create a dummy service with display name "Cloud Sync Service" pointing at a benign binaryQ2 returns the event within the expected sensor latency.
G5 · Positive test — masqueradeCopy a benign signed binary to %APPDATA%\svchost32\svchost32.exe and run it in a lab VMQ1 returns it. Confirms both the path and the basename regex behave.
G6 · Positive test — Wi-Fi dumpRun netsh wlan show profile on a lab hostQ9 returns it, and your RMM exclusion in the TUNING line does not suppress it.
G7 · Negative testConfirm Q1 does not fire on legitimate svchost.exeZero results — proves the anchored basename regex is not matching a prefix.
G8 · PromotionOnly Q1, Q2, Q3, Q5, Q6, Q7 are eligible for scheduled-search alerting on day oneThe remaining seven stay hunt-only until G3 produces a documented exclusion set.
12

Hardening — Tiered

Immediate — deploy this week
  1. Block the Entra device code flow with a Conditional Access policy targeting all users, all cloud apps, with the Authentication flows condition set to Device code flow, grant = Block. MITRE M1032 / M1018. Authority: Microsoft Entra Conditional Access documentation; the advisory names this as its first identity control. This is the single highest-leverage change in the pack: it neutralises the branch of the campaign that leaves no endpoint evidence.
  2. Require phishing-resistant MFA for all privileged roles (passkeys / FIDO2 / Windows Hello for Business). MITRE M1032. Authority: CIS Microsoft 365 Benchmark, Microsoft Security Baseline for Entra ID. A stolen refresh token cannot be replayed into a role that demands a hardware-bound credential at elevation.
  3. Push the ASR rule "Block executable content from email client and webmail" and "Block JavaScript or VBScript from launching downloaded executable content" to the travel fleet in Block mode. MITRE M1042. Authority: Microsoft Defender ASR rule reference, CIS Windows Benchmark. Neither stops a hand-pasted command by itself, which is why user education (item 5) is listed alongside rather than instead.
  4. Turn on Falcon prevention policy parity for laptops. Confirm the travel fleet is in the same (or stricter) prevention policy as desktops, with Suspicious Process Blocking and Script-Based Execution Monitoring enabled. Authority: CrowdStrike prevention policy best practice. ⚠ best-practice, no formal benchmark.
  5. Issue a travel advisory to staff covering the three behaviours that actually matter: never run a command a web page asks you to copy; never install anything a captive portal offers; never type a code into a Microsoft sign-in page that you did not initiate yourself. MITRE M1017.
Near term — 1 to 4 weeks, pilot on a ring first
  1. Restrict MFA and passkey registration to trusted locations and compliant devices via Conditional Access. MITRE M1032. Authority: Microsoft Entra Conditional Access documentation. Stops an actor holding a live session from adding their own authenticator.
  2. Require device registration to be MFA-gated and limited to compliant devices. MITRE M1018. Authority: Microsoft Entra device settings documentation. Directly targets the T1098.005 step.
  3. Enable a sign-in risk Conditional Access policy set to require reauthentication (or block) at medium risk and above. Authority: Microsoft Entra ID Protection documentation.
  4. Block Wi-Fi association with non-MDM-provisioned networks on the managed fleet, or at minimum require a mandatory always-on VPN or SSE tunnel before any traffic egresses on an untrusted SSID. MITRE M1037. Authority: Microsoft advisory recommendation; CIS Windows Benchmark wireless settings.
  5. Enforce PowerShell Constrained Language Mode + script block logging + AMSI on non-developer endpoints via AppLocker or WDAC. MITRE M1038, M1045. Authority: CIS Windows Benchmark, Microsoft PowerShell security documentation. ChocoShell is entirely in-memory PowerShell; CLM removes most of its reflection surface.
  6. Deploy a managed travel-router or enterprise hotspot programme for staff who regularly work from venues, establishing an encrypted tunnel back to trusted infrastructure. Authority: Microsoft advisory recommendation. ⚠ best-practice, no formal benchmark.
Strategic — 1 to 3 months
  1. Move to passwordless / passkey-first authentication tenant-wide. MITRE M1032. Authority: Microsoft passwordless deployment guidance, NIST SP 800-63B. This campaign's entire value chain terminates in reusable credentials and tokens; passkeys remove the reusable part.
  2. Deploy a Security Service Edge solution (e.g. Global Secure Access or equivalent) so that endpoint egress is inspected and policy-controlled regardless of which network the laptop is sitting on. MITRE M1037. Authority: Microsoft advisory recommendation, NIST SP 800-207 zero-trust architecture.
  3. Application control (WDAC) in enforced mode on the travel fleet, with a policy that denies execution from user-writable paths. MITRE M1038. Authority: Microsoft WDAC documentation, DISA Windows STIG. A CornFlake payload at %APPDATA%\svchost32\svchost32.exe simply does not run.
  4. Revise corporate-event and travel policy to evaluate whether venue-provided wireless is used at all, and to minimise what employee and organisational detail is disclosed during venue booking and conference registration. MITRE M1017. Authority: Microsoft advisory recommendation.
  5. Ingest Entra sign-in and audit logs into the SIEM if not already done, with retention covering at least 90 days. Authority: NIST SP 800-92, CIS Control 8. Without this, Q13 and every native hunt in §7 are unrunnable.
13

Deployable Playbooks

Playbook 1 — Block the Entra device code flow (Conditional Access)

Prerequisites: Entra ID P1 or higher; Conditional Access Administrator or Security Administrator role; a break-glass account excluded from all CA policies; Microsoft.Graph PowerShell module installed.
Reboot required: No.
Rollback: Set the policy state back to disabled (step 4 below), or delete the policy entirely. No client-side change is made, so rollback is immediate and total.

1. Inventory first -- find who legitimately uses the flow before you block it.
   Entra portal -> Sign-in logs -> Add filter -> Authentication protocol
   -> Device code. Export 90 days. Anything you find here becomes either an
   exclusion or a conversation with that team.

2. Create the policy in report-only mode (Graph PowerShell):

   Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"

   $params = @{
     displayName = "Block device code flow"
     state       = "enabledForReportingButNotEnforced"
     conditions  = @{
       users        = @{
         includeUsers  = @("All")
         excludeGroups = @("REPLACE_WITH_BREAKGLASS_GROUP_OBJECTID")
       }
       applications = @{ includeApplications = @("All") }
       authenticationFlows = @{ transferMethods = "deviceCodeFlow" }
     }
     grantControls = @{ operator = "OR"; builtInControls = @("block") }
   }
   New-MgIdentityConditionalAccessPolicy -BodyParameter $params

3. Leave it in report-only for 7 days. Review Sign-in logs -> Report-only tab
   for any impacted sign-in. Add exclusions for genuine device-code apps
   (Azure CLI service accounts, kiosk enrolment) as named app exclusions --
   NOT as user exclusions, which are far broader than intended.

4. Enforce, or roll back:
   $id = (Get-MgIdentityConditionalAccessPolicy |
          Where-Object DisplayName -eq "Block device code flow").Id
   # enforce:
   Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $id `
     -State "enabled"
   # ROLLBACK -- returns the tenant to its previous behaviour immediately:
   Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $id `
     -State "disabled"

5. Verify: attempt a device-code sign-in with a test account. It must fail with
   a Conditional Access block, and the block must appear in Sign-in logs.

Playbook 2 — CornFlake host triage and eradication

Prerequisites: Local administrator on the target host; the host network-contained in Falcon before you begin (the watchdog re-creates persistence, and the RAT has a live remote shell); PowerShell 5.1+ running elevated.
Reboot required: Yes — after step 5, to confirm nothing re-spawns.
Rollback: Not applicable in the usual sense; these steps remove attacker-created objects only. Before deleting anything, run step 1 and preserve its output plus a copy of the payload and sync.dat as evidence. If a service or task turns out to be legitimate in your environment, re-create it from your configuration baseline. The recommended end state is reimage, not repair — the watchdog and the localhost control API mean partial removal is not reliably verifiable.

1. COLLECT BEFORE YOU DELETE (host already contained in Falcon):

   $out = "$env:TEMP\cc-triage-$(Get-Date -f yyyyMMddHHmmss).txt"
   Get-Service -Name "svchost32" -ErrorAction SilentlyContinue |
     Format-List * | Out-File $out -Append
   Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\svchost32" `
     -ErrorAction SilentlyContinue | Out-File $out -Append
   Get-ChildItem "$env:APPDATA\svchost32" -Force -ErrorAction SilentlyContinue |
     Out-File $out -Append
   Get-ScheduledTask | Where-Object {
     $_.Actions.Execute -like "*AppData*" } |
     Select-Object TaskName, TaskPath, @{n='Exec';e={$_.Actions.Execute}} |
     Out-File $out -Append
   Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",
     "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" |
     Out-File $out -Append
   Get-ItemProperty "HKCU:\Environment" -Name windir -ErrorAction SilentlyContinue |
     Out-File $out -Append

2. Copy $out, the payload and sync.dat off the host to evidence storage.

3. Stop and delete the service:
   Stop-Service -Name "svchost32" -Force -ErrorAction SilentlyContinue
   sc.exe delete "svchost32"

4. Remove the scheduled tasks identified in step 1 (review each by name first):
   # Unregister-ScheduledTask -TaskName "REPLACE_WITH_TASKNAME" -Confirm:$false

5. Remove the payload directory and the UAC-bypass key:
   Remove-Item "$env:APPDATA\svchost32" -Recurse -Force -ErrorAction SilentlyContinue
   Remove-ItemProperty "HKCU:\Environment" -Name windir -ErrorAction SilentlyContinue

6. Reboot, then re-run step 1. If ANY artefact has returned, the watchdog is
   still resident: stop repairing and reimage.

7. Regardless of outcome, treat the identity as compromised -- go to Playbook 3.

Playbook 3 — Identity containment for an affected traveller

Prerequisites: Privileged Authentication Administrator (for credential reset) and Cloud Device Administrator (to remove devices); Microsoft.Graph PowerShell module; the user reachable through an out-of-band channel you trust — not their possibly-compromised mailbox or Teams.
Reboot required: No.
Rollback: None required, and none should be attempted — every action here is a deliberate credential invalidation. The user re-authenticates and re-enrols as part of normal recovery. Removing a device that turns out to be the user's own is corrected by having them re-register it through the standard join process.

Connect-MgGraph -Scopes "User.ReadWrite.All","Device.ReadWrite.All",
                        "AuditLog.Read.All","Directory.AccessAsUser.All"

$upn = "REPLACE_WITH_USER_UPN"

1. Revoke every refresh token and active session:
   Revoke-MgUserSignInSession -UserId $upn

2. Force a credential reset (do this via your standard process, in person or
   over a verified out-of-band channel -- NOT by emailing the user).

3. List devices registered to the user and check every one against known
   hardware. Anything unrecognised is the attacker's:
   Get-MgUserRegisteredDevice -UserId $upn |
     Select-Object Id, DisplayName, AdditionalProperties

   # Remove an attacker-registered device:
   # Remove-MgDevice -DeviceId "REPLACE_WITH_DEVICE_OBJECTID"

4. Re-enrol MFA from scratch. Delete existing methods and have the user
   register a passkey in person.

5. Review the mailbox for actor persistence:
   Get-InboxRule -Mailbox $upn | Select-Object Name, Enabled, ForwardTo,
     RedirectTo, DeleteMessage
   Get-Mailbox $upn | Select-Object ForwardingSmtpAddress,
     DeliverToMailboxAndForward
   Get-MailboxPermission -Identity $upn | Where-Object { -not $_.IsInherited }

6. Check for OAuth consent granted during the compromise window:
   Get-MgUserOauth2PermissionGrant -UserId $upn |
     Select-Object ClientId, Scope, ConsentType

7. Only after 1-6 are complete, release the endpoint from containment or issue
   the reimaged replacement.

Playbook 4 — PowerShell Constrained Language Mode on the travel fleet

Prerequisites: AppLocker or WDAC already in audit mode on a pilot ring; an inventory of business-critical PowerShell that runs on user endpoints (CLM breaks .NET reflection, Add-Type, and COM instantiation); Intune or GPO delivery.
Reboot required: No, but users must start a new PowerShell session for the mode to apply.
Rollback: Remove the AppLocker/WDAC policy (or return it to audit mode) and delete the __PSLockdownPolicy environment variable if it was used for the pilot; the next new session returns to FullLanguage. Keep the pilot ring small precisely so this rollback stays cheap.

1. Baseline first -- enable script block logging fleet-wide and collect 14 days:
   GPO: Computer Configuration -> Administrative Templates -> Windows Components
        -> Windows PowerShell -> "Turn on PowerShell Script Block Logging" = Enabled
   Review Event ID 4104 for reflection, Add-Type and COM use in YOUR scripts.

2. Pilot ring: apply an AppLocker policy in ENFORCE for Script rules only.
   CLM is a side effect of AppLocker/WDAC enforcement -- this is the supported
   route. Do not set __PSLockdownPolicy as a production mechanism; it is a
   pilot/testing aid only and is trivially unset by a user.

3. Verify the mode on a pilot host (expected output: ConstrainedLanguage):
   $ExecutionContext.SessionState.LanguageMode

4. Confirm the control does what it is here for -- these must now fail:
   [System.Reflection.Assembly]::LoadWithPartialName("System.Management.Automation")
   New-Object -ComObject WScript.Shell

5. Expand ring by ring, reviewing 4104 logs and helpdesk tickets at each stage.

6. ROLLBACK at any ring: set the AppLocker Script rule enforcement back to
   "Audit only" and refresh policy (gpupdate /force). New PowerShell sessions
   return to FullLanguage immediately; no reboot and no reimage needed.
14

Containment Runbook

PhaseActionsOwnerEvidence to capture
0 · Triage (0–30 min)Confirm the hit is real: for Q1/Q2/Q3 hits, pull the full process tree and the parent chain. Establish whether the user travelled since 2026-05-01 and to which venue type. Decide scope: one host or a travel cohort.SOC L1 → L2Query output, process tree export, Falcon detection ID, user travel dates
1 · Isolate (30–60 min)Network-contain the endpoint in Falcon before touching it — CornFlake has a live remote shell and a watchdog. Do not power it off; memory is where ChocoShell lives. Suspend the user's cloud sessions in parallel (do not wait for endpoint work to finish).SOC L2Containment timestamp, memory image if your process supports it
2 · Identity containment (immediate, parallel)Run Playbook 3: revoke refresh tokens, reset credentials, enumerate and remove attacker-registered devices, re-enrol MFA, review inbox rules, forwarding, delegates and OAuth grants.Identity teamSign-in log export, device registration list, OAuth grant list, inbox rule audit
3 · Scope (1–4 h)Run Q1–Q7 fleet-wide and Q13 over 90 days. Build the traveller cohort from HR/expense data and check every member against §7 native hunts. Check whether other users attended the same venue in the same window.Threat huntingCohort list, per-user hunt results, venue/date correlation table
4 · Eradicate (4–24 h)Reimage the endpoint. Do not repair — the watchdog restores persistence and partial removal is not verifiably complete. Rotate any credential that was stored in a browser on the host, plus every corporate Wi-Fi PSK the host held.Endpoint opsReimage record, credential rotation list, Wi-Fi PSK rotation record
5 · Recover (24–72 h)Return the user on a clean device with a passkey enrolled in person. Confirm no device-code sign-ins for the user after the reset. Keep enhanced monitoring on the account for 30 days.Endpoint ops + SOCClean-device handover record, 30-day monitoring ticket
6 · Harden & report (1–2 weeks)Deploy §12 Immediate items if not already done — item 1 (block device code flow) is the one that closes this specific door. Brief the affected business unit on the travel advisory. Record the venue in an internal watchlist.Security engineeringCA policy ID, ASR deployment record, advisory distribution list

Sequencing matters here. Isolating the endpoint without revoking cloud sessions leaves the attacker holding a valid refresh token and an attacker-registered device — they keep the tenant access and simply lose one laptop. Phase 2 runs in parallel with Phase 1, not after it.

15

Detection Coverage Map

TechniqueBehaviourCQLIOACoverage
T1557Captive-portal DNS/HTTP manipulationGAP — third-party infrastructure, no telemetry. Policy control only (§12).
T1566.002Doppelganger domain served from portalQ6GOOD
T1204.004ClickFix paste-and-runQ8PARTIAL — approximated from the process side; NCSI probe itself is not a sensor event.
T1036.005svchost32 masqueradeQ1IOA-1GOOD
T1543.003"Cloud Sync Service" registrationQ2IOA-2GOOD
T1547.001Run-key persistenceQ3GOOD
T1053.005Scheduled task persistenceQ4PARTIAL — noisy; stack-rank hunt until baselined (G3).
T1204Known payload executionQ5IOA-1GOOD — but brittle: per-build randomisation means hash churn is expected.
T1071.001 / T1041C2 and exfiltrationQ6, Q7PARTIAL — IP/domain coverage only; the operator panel rotates relays by design.
T1548.002UAC bypass chainQ11PARTIAL — registry side covered; the SilentCleanup execution itself is not distinguished.
T1562.001AMSI tamperingQ8 (partial)IOA-4PARTIAL — needs script block logging (Event 4104) for full fidelity.
T1555 / T1555.003 / T1539Wi-Fi, browser and cookie theftQ9, Q10, Q12IOA-3, IOA-6GOOD
T1528 / T1550.001M365 token theft and replayQ12, Q13PARTIAL — depends entirely on Entra log ingest (gate G2).
T1098.005Attacker device registrationGAP — no Falcon telemetry; covered by native hunt N2 only.
T1056.001 / T1113 / T1123 / T1125Keylogging, screen, audio, webcamGAP — no discrete collection events; caught only via the parent payload (Q1).
T1025Removable-media collectionGAP — USB monitoring module produces no distinct signal in this telemetry set.
T1490VSS shadow-copy deletionIOA-5PARTIAL — IOA only, no hunt query in this pack.
FruitStone C2 panelN/A — operator-side only; deliberately no endpoint detection.
Android APK variantGAP — outside Windows sensor scope; route to MTD/MDM.

Thirteen CQL queries ship: six are alert-ready on day one (Q1, Q2, Q3, Q5, Q6, Q7) and seven are hunt-only until the benign baseline in gate G3 produces a documented exclusion set. The material gaps are all structural rather than fixable by another query: the venue network is not instrumented, attacker device registration and the collection modules produce no discrete Falcon event, and the Android variant is outside this telemetry entirely. The single most consequential dependency is gate G2 — if Entra sign-in logs are not ingested, Q13 and the whole identity half of this pack do not run, and the branch of the campaign that survives reimaging goes unwatched. Validate G1 and G2 before trusting any coverage claim on this page.

16

Hunt Summary Ticket

TITLE      : CaptiveCrunch -- Storm-2945 / Midnight Blizzard captive-portal AiTM
SEVERITY   : Critical (active nation-state, SVR-attributed, ongoing since May 2026)
SCOPE      : Windows travel fleet + Microsoft 365 / Entra ID tenant
HYPOTHESIS : Corporate travellers who joined hospitality captive-portal Wi-Fi since
             2026-05-01 were served ClickFix fake-update prompts, ran CornFlake
             (svchost32.exe) and/or ChocoShell, and had M365 tokens stolen; a
             separate branch phished Entra device codes with no endpoint artefact.
QUERIES    : Q1  CornFlake svchost32 execution            CONF high   FP low
             Q2  "Cloud Sync Service" registration        CONF high   FP low
             Q3  Run-key / service-key persistence        CONF high   FP low
             Q4  Scheduled task from %APPDATA%            CONF med    FP med
             Q5  Known SHA256 execution or write          CONF high   FP low
             Q6  Doppelganger domain resolution           CONF high   FP low
             Q7  C2 IP egress                             CONF high   FP low
             Q8  ClickFix browser-parented interpreter    CONF med    FP med
             Q9  netsh wlan show profile                  CONF high   FP med
             Q10 Browser remote-debugging-port relaunch   CONF med    FP med
             Q11 UAC-bypass registry hijacks              CONF med    FP med
             Q12 Credential store / token cache targeting CONF med    FP med
             Q13 Entra device code sign-in anomaly        CONF med    FP med
DO FIRST   : 1. Run Q1, Q2, Q3, Q5 fleet-wide over 90 days -- near-zero FP.
             2. Confirm Entra sign-in logs are ingested (gate G2). If not, fix
                that before anything else; Q13 is the only cover for the
                branch that survives reimaging.
             3. Run Q13 over 90 days and cross-check every hit against the
                traveller cohort.
             4. Audit Conditional Access for a device-code-flow block (§10
                checklist). If absent, Playbook 1 is the priority action.
FINDINGS   : [ ] Q1-Q3 endpoint hits ............ hosts: ______
             [ ] Q5 hash hits .................... hosts: ______
             [ ] Q6/Q7 C2 contact ............... hosts: ______
             [ ] Q13 device-code sign-ins ....... users: ______
             [ ] New Entra device registrations . users: ______
GAPS       : Venue network manipulation (no telemetry); attacker device
             registration (native hunt N2 only); collection modules
             (keylog/screen/audio/webcam/USB) produce no discrete events;
             Android APK variant outside Windows sensor scope.
ACTIONS    : Contain endpoint -> revoke tokens IN PARALLEL -> scope cohort ->
             reimage (do not repair; watchdog restores persistence) ->
             block device code flow -> rotate browser-stored creds and Wi-Fi PSKs.
OWNER     : SOC threat hunting / Identity team (joint -- neither half works alone)
VERSION   : v0.1 · 2026-08-04
17

Changelog

v0.12026-08-04Initial pack. Thirteen CQL queries across delivery, persistence, credential access, C2 and identity. Six alert-ready, seven hunt-only pending baseline. Twelve atomic indicators shipped at detect/high — none at prevent, because Microsoft is the sole publisher and the two-independent-source bar for a blocking action is not met. Six native Entra/M365 hunts, four deployable playbooks with rollback, and a coverage map recording four structural gaps. Name collision with the unrelated Interlock CORNFLAKE loader explicitly noted so the two IOC sets are not merged.
18

References

TierSourceUsed forAccessed
1Microsoft Threat Intelligence — CaptiveCrunch: Midnight Blizzard targets travelers worldwide (2026-07-31)Attribution, full attack chain, CornFlake / ChocoShell / FruitStone internals, all atomic indicators, mitigations, hunting guidance2026-08-04
2Infosecurity Magazine — CaptiveCrunch / Midnight BlizzardCampaign narrative, NCSI-interception delivery, Android APK variant, device-code timeline2026-08-04
2Security Affairs — Russian hackers hijack hotel Wi-Fi to steal Microsoft 365 tokensService name and display name corroboration, ECDH C2, Wi-Fi and token-cache collection detail2026-08-04
2Wiz Threat Intelligence — CaptiveCrunch hospitality-network AiTM campaignIndependent incident tracking; Entra ID as the primary targeted technology2026-08-04
2Check Point Research — Threat Intelligence Report, 3rd August 2026Independent confirmation of actor, malware names and the M365 token-theft objective2026-08-04
refMITRE ATT&CK — T1557 Adversary-in-the-MiddleTechnique mapping and mitigation references2026-08-04
refMITRE ATT&CK — T1528 Steal Application Access TokenDevice code / OAuth abuse mapping2026-08-04
refMITRE ATT&CK — G0016 APT29 (Midnight Blizzard)Actor background and historical TTPs2026-08-04

Source snapshots for every citation above are preserved alongside this pack in CaptiveCrunch-Storm2945-Hunt-sources/, one plain-text file per source, so that every atomic indicator shipped here can be traced back to the text it came from.