Ray-Project Ray — Dashboard/API Code Injection via DNS-Rebinding User-Agent Bypass
Executive Summary
Ray is an open-source, Python-native distributed compute engine widely used to run AI/ML training and inference clusters. Every Ray head node ships a Dashboard and Jobs API (default port 8265) with no authentication by design — the project's long-standing security model assumed the network around Ray was trusted, and the only defense against a hostile browser reaching that API was a check that the request's User-Agent header started with "Mozilla". That check is trivially bypassed: the fetch spec lets Firefox and Safari override the User-Agent on a request (Chrome has a bug that happens to block this, which is the only reason Chrome users are not equally exposed). Combine that bypass with DNS rebinding — a technique that makes a browser resolve an attacker-registered domain to 127.0.0.1 or another internal address after the same-origin check has already passed — and an attacker-controlled webpage or malicious ad can silently drive a victim's browser to POST a job to /api/jobs/ on any Ray dashboard the victim's machine can reach, with an entrypoint the attacker chooses. Ray executes it. The victim never has to install anything or click "allow" — visiting a page while Ray happens to be running is enough.
The attacker's objective is arbitrary code execution with the privileges of the Ray process — typically a developer's own shell, or the service account running a training/inference cluster — followed by credential, model, and dataset theft, lateral movement into whatever cloud or internal infrastructure that Ray host can reach, and (per the observed "ShadowRay 2.0" activity) GPU-cycle theft via cryptomining on expensive training hardware. CISA added CVE-2025-62593 to KEV on 2026-08-17 citing active exploitation, with a three-day FCEB remediation window (due 2026-08-20) — and per Bitsight reporting cited by multiple outlets, the RondoDox botnet was already probing for this exact flaw two days before the CVE was publicly disclosed on 2025-11-26.
The single highest-value defensive move is not a detection rule — it's closing the precondition: nothing in this attack chain works if the Ray dashboard is bound to loopback only and firewalled from any network the attacker's browser (via the victim) can reach. Everything downstream of that (DNS-rebinding telemetry, unauthenticated-API abuse, anomalous child processes off raylet/gcs_server) is real and worth hunting, but it is defense-in-depth for a bug whose root fix is "don't expose the dashboard, and patch to 2.52.0+ where token auth finally exists."
Defender priority: patch to Ray ≥2.52.0 (2.52.1 preferred) and confirm no Ray dashboard/Jobs API port (8265, plus GCS 6379 and worker ports) is reachable from outside loopback/a private, firewalled subnet — before investing in the behavioral detections in this pack. An internet-exposed, unpatched Ray dashboard is compromised by policy; treat it as such and start incident response, don't wait for a hunt hit.
Source Review & Web Hunter Notes
All sources below were fetched on 2026-08-22 and saved verbatim to Ray-CVE-2025-62593-Hunt-sources/ for provenance. No atomic IOC in this pack is sourced from anywhere outside this table.
| Tier | Source | Key finding | Carry forward |
|---|---|---|---|
| Gov | CISA KEV catalog (official JSON feed) | Authoritative dateAdded 2026-08-17, dueDate 2026-08-20, BOD 26-04 (not the older BOD 22-01 a secondary article cited), CWE-94 + CWE-352, links to the Ray GHSA and the exact fix commit | Yes — used for header meta, ticket, and due-date framing |
| Vendor | Ray Project GHSA-q279-jhrf-cc6v | Full technical root cause (User-Agent "Mozilla" check + fetch-spec override on Firefox/Safari), CVSS 4.0 vector 9.4, affected <2.52.0 / fixed 2.52.0, PoC posts JSON to /api/jobs/, fix commit 70e7c72, notes optional token auth added in 2.52.0 (disabled by default) | Yes — primary technical source for attack mechanics and remediation |
| Independent research | Resecurity technical write-up | Six-stage attack chain, confirms port 8265, exact vulnerable endpoint /api/jobs/, sample malicious request (User-Agent: Other), lists candidate network/log IOCs (all behavioral), notes RondoDox pre-disclosure probing did not successfully bypass the browser check | Yes — drove the CQL hypothesis set and the attack-chain table |
| Journalism | The Hacker News | Confirms CVSS 9.4, KEV date/deadline, cites RondoDox pre-disclosure activity and a "ShadowRay 2.0" GPU-cryptomining campaign against unpatched instances | Partial — campaign names carried forward as context, not as atomic IOCs |
| Journalism | GBHackers | CWE-94/CWE-352 framing, emphasizes impact to credentials/source code/cloud resources/model theft in AI/data-science environments | Partial — used for impact narrative |
| Journalism | Security Affairs | KEV addition context; cites Aviatrix research on downstream impact (privilege escalation, lateral movement, C2, exfiltration); this article's "BOD 22-01" reference was superseded by the KEV JSON's own "BOD 26-04" citation — flagged and resolved in favor of the authoritative feed | Partial — cross-checked, one factual point corrected against the primary KEV source |
Web Hunter decisions: (1) No source — including the vendor advisory — publishes an atomic hash, IP, or C2 domain, because this is a logic/config vulnerability in Ray itself, not a malware family; the IOC table and appendix in this pack are behavioral by design, per the task's own instruction not to fabricate atomic indicators. (2) NVD's CVSS 3.x score (8.8) differs from GitHub's CNA-assigned CVSS 4.0 score (9.4) per Resecurity; this pack uses the CNA/vendor score (9.4) as primary since it's the one GHSA and KEV both track, and notes the NVD figure as a secondary data point. (3) "RondoDox" and "ShadowRay 2.0" are carried forward as named campaigns/context in the exec summary and IOC table, not as verified indicators to detect on directly — they are attributions from secondary journalism, not the primary vendor/gov sources.
Hunt Brief & Attack Chain
Hunt hypotheses
- (High) A Ray dashboard/GCS process is listening on 8265 (or other Ray ports) on a non-loopback interface reachable by a browser — the precondition for the whole chain.
- (High) A shell or interpreter is spawned directly from
rayletorgcs_server— the highest-fidelity signal that a job's entrypoint executed attacker code. - (Medium) A Ray-context Python worker process (job execution, titled
ray::...at the OS level but recorded by the sensor as apythonimage) spawns an unexpected shell whose command line references Ray's own session paths. - (Medium) A browser process (Firefox/Safari) opens a direct connection to a local or internal host on port 8265 — the confused-deputy step where the victim's browser becomes the attacker's proxy.
- (Medium, investigate-only) The same domain resolves to multiple distinct IP addresses — including loopback/RFC1918 — in a short window: the DNS-rebinding fingerprint.
- (Medium) A known GPU-cryptomining binary is spawned from a Ray core process — the "ShadowRay 2.0" post-exploitation pattern.
- (Low-Medium) A Ray core process opens an outbound connection to a common cryptomining-pool port, catching a renamed/repacked miner binary that Q6's name list would miss.
Attack chain
| Step | Telemetry | Hunt angle |
|---|---|---|
| 1 · Recon — attacker identifies a reachable Ray instance or simply mass-serves a malicious ad to any visiting browser | None (pre-attack, off-host) | Proactive exposure hunt — Q1 |
| 2 · Victim visits malicious site/ad while Ray is running locally | Browser process launch, DNS activity | Q5 (DNS pattern), native browser-history review |
3 · DNS rebinding resolves the attacker's domain to 127.0.0.1 or an internal Ray host after the same-origin check passes | DnsRequest | Q5 (flagged unverified field — see card note) |
4 · Browser sends a non-Mozilla User-Agent fetch to the dashboard on 8265, bypassing the browser-request check | NetworkConnectIP4 from the browser process; Ray dashboard access logs (not native Falcon telemetry) | Q4 + native audit-log hunt (§7) |
5 · Attacker's crafted JSON is POSTed to the unauthenticated /api/jobs/ endpoint with a malicious entrypoint | Ray Jobs API access/application log — not present in default Falcon endpoint telemetry | Native audit-log hunt (§7) — coverage gap noted in §15 |
6 · Ray executes the entrypoint; a child process spawns under raylet/gcs_server/the job's Python worker | ProcessRollup2 / SyntheticProcessRollup2 | Q2, Q3 — the core, highest-confidence detections in this pack |
| 7 · Post-exploitation — credential/model/data theft, GPU cryptomining, lateral movement | Process, network | Q6, Q7 |
Consolidated IOC Table
No atomic hashes, IPs, or C2 domains have been published for this CVE by any source reviewed (vendor GHSA, CISA KEV, or the four independent write-ups). This is a design/logic flaw in Ray itself, not a malware family with static artifacts — the attacker's DNS-rebinding domain is registered per-campaign and none of the reviewed sources disclosed one. Every row below is a behavioral or configuration indicator; see §10 for the copy-paste blocks.
| Type | Value | Confidence | Action | Context | Expiry |
|---|---|---|---|---|---|
| Port | TCP/8265 | high | hunt | Default Ray Dashboard & Jobs API port — exposure is the precondition for the whole chain | n/a (config-based, not perishable) |
| Port | TCP/6379 | medium | hunt | Default Ray GCS/Redis port — also should never be internet-reachable | n/a |
| Port range | TCP/10001-19999 (worker ports, cluster-dependent) | low | enrich | Ray worker-to-worker communication range; context only, not a standalone signal | n/a |
| Process name | raylet, gcs_server | high | hunt | Ray core binaries — parent-of-interest for RCE hunts (Q2) | n/a |
| Behavioral | Non-Mozilla User-Agent on a POST to /api/jobs/ or /api/job_agent/jobs/ | high | hunt | The exact browser-check bypass documented in Ray's own advisory and PoC (sample value User-Agent: Other) | 2027-02-22 (+6mo) |
| Associated campaign | RondoDox (DDoS/IoT botnet) | medium | enrich | Per Bitsight (cited by secondary journalism), attempted exploitation 2 days before public disclosure; per Resecurity the observed attempt did not successfully bypass Ray's browser check | 2026-11-22 (+3mo) |
| Associated campaign | "ShadowRay 2.0" (GPU cryptomining) | medium | enrich | Named by The Hacker News as exploiting unpatched Ray instances for cryptomining; no atomic IOCs published — hunt via Q6/Q7 behavior instead | 2026-11-22 (+3mo) |
Affected Surface & Telemetry Matrix
| Surface | Required telemetry | Priority | Gap risk |
|---|---|---|---|
| Linux hosts/servers running Ray <2.52.0 (dev boxes, training/inference nodes) | Falcon process + network sensor telemetry | High | Low — if Falcon sensor deployed, full coverage |
| macOS/Windows developer workstations running Ray locally + Firefox/Safari | Falcon process + network sensor telemetry | High | Low — same as above; Chrome-only shops are lower risk but should still patch |
| Kubernetes clusters running KubeRay (RayCluster/RayJob CRDs) | Falcon Cloud Security / container sensor + node-level telemetry | High | Medium-High — container/pod-level process visibility depends on sensor deployment mode; the Jobs API call itself is invisible to endpoint telemetry regardless |
| Cloud-hosted Ray clusters (AWS/GCP autoscaler-managed) | Falcon sensor on cluster nodes + cloud VPC flow logs | High | Medium — ephemeral autoscaled nodes may lack sensor coverage at spin-up |
| Ray dashboard/Jobs API application logs | Not native Falcon telemetry — requires a custom/third-party log source ingest | Medium | High — this is the only place the exact bypass (User-Agent + endpoint + entrypoint) is directly visible; see native hunt in §7 and gap noted in §15 |
| Network/perimeter firewall & DNS resolver logs | Not Falcon endpoint telemetry — perimeter/DNS infrastructure logs | Medium | High — DNS-rebinding detection is fundamentally a DNS-resolver-side problem; Q5 is a best-effort proxy from endpoint DNS telemetry only |
ATT&CK Mapping
| Tactic | Technique ID | Technique name | Observed behavior |
|---|---|---|---|
| Initial Access | T1189 | Drive-by Compromise | Victim's browser triggers exploitation merely by loading a malicious page/ad while Ray runs locally — no click, download, or install required |
| Initial Access / Execution | T1210 | Exploitation of Remote Services | Unauthenticated Ray Jobs API (/api/jobs/) accepts an attacker-supplied entrypoint via the browser-mediated confused-deputy path |
| Execution | T1059 | Command and Scripting Interpreter | Job entrypoint executes as a subprocess of the Ray runtime with the privileges of the Ray process |
| Defense Evasion (no exact ATT&CK ID) | CAPEC-664 (reference only) | DNS Rebinding | Attacker domain re-resolves to loopback/internal address after the browser's same-origin check has already passed; ATT&CK has no discrete technique for DNS rebinding itself — closest formal mapping is CAPEC-664 |
| Credential Access | T1552 | Unsecured Credentials | Ray processes routinely hold cloud/API credentials, S3/GCS keys, and internal service tokens in environment or config reachable once code executes |
| Collection | T1005 | Data from Local System | Training data, model artifacts, and source code accessible to the compromised Ray process |
| Impact | T1496 | Resource Hijacking | "ShadowRay 2.0"-style GPU cryptomining deployed on compromised training/inference hardware |
Native Audit-Log Hunts
These checks cover the parts of the attack chain (steps 4-5) that Falcon endpoint telemetry does not natively see — the Jobs API call itself only exists in Ray's own dashboard/API access logs.
1. Ray dashboard/API access-log review (if ingested to any log platform)
If the Ray dashboard's HTTP access log is captured anywhere (reverse proxy, application log shipper, or a custom LogScale ingest), grep/search for:
POST /api/jobs/ or POST /api/job_agent/jobs/
User-Agent NOT starting with "Mozilla" // the exact bypass condition per Ray's own advisory
// sample observed value in the vendor PoC: "User-Agent: Other"
Any hit is a near-certain exploitation attempt — the legitimate Ray CLI/SDK does not send browser-style requests to this endpoint from an external origin.
2. Ray version & dashboard-bind configuration audit
Run on every host known or suspected to run Ray (fleet inventory, package manager, or container image scan):
pip show ray 2>/dev/null | grep -i version ray --version ps aux | grep -E "raylet|gcs_server|dashboard" | grep -v grep # Confirm the dashboard is NOT bound to 0.0.0.0 — the flag that widens exposure: ps aux | grep -- "--dashboard-host"
Flag any result showing Ray <2.52.0, or a --dashboard-host value other than 127.0.0.1/localhost on a host with any network reachability beyond a fully trusted, segmented cluster subnet.
3. Ray token-authentication status audit (post-patch verification)
Ray 2.52.0+ ships an optional, disabled-by-default token-auth feature. Confirm it is actually enabled after patching — an unpatched-then-patched host that never turned auth on is still relying on network exposure alone:
ray status env | grep -i RAY_AUTH # or check the equivalent startup flag/config per Ray 2.52+ docs
CrowdStrike LogScale CQL Hunt Queries
Looks for: a listening socket on Ray's default dashboard/Jobs API port bound to any address other than loopback — the precondition the entire attack chain requires. FP: intentional multi-node Ray clusters legitimately bind the dashboard to a routable interface on a trusted, segmented subnet; that is expected and should be excluded by host allowlist, not treated as a finding on its own.
// HUNT: Ray Dashboard/GCS listening on non-loopback interface (port 8265) // MITRE: T1190 // CONF: high FP: medium COST: low | REQUIRES: NetworkListenIP4 // FALSE POSITIVES: legitimate multi-node Ray clusters bound to a private, firewalled subnet // TUNING: exclude known Ray head-node ComputerName values on trusted, segmented cluster subnets // LOOKBACK: 7d (set via console time picker) #event_simpleName=NetworkListenIP4 | LocalPort=8265 | LocalAddressIP4!="127.0.0.1" | table([ComputerName, aid, LocalAddressIP4, LocalPort, ContextBaseFileName, ContextProcessId_decimal])
Looks for: a shell or scripting interpreter whose direct parent is Ray's own core binary — raylet or gcs_server do not normally spawn shells during legitimate cluster operation. This is the highest-fidelity, IOA-eligible detection in the pack. FP: low — these are Ray's internal orchestration binaries, not general-purpose parents.
// HUNT: Shell/interpreter spawned directly from raylet or gcs_server // MITRE: T1210, T1059 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: none expected under normal Ray cluster operation // LOOKBACK: 7d (set via console time picker) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(raylet|gcs_server)(\.exe)?$/i | FileName=/^(cmd|powershell|pwsh|sh|bash|dash|zsh|nc|ncat|curl|wget|python3?|perl|ruby|osascript)(\.exe)?$/i | table([ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, TargetProcessId_decimal, ParentProcessId_decimal])
Looks for: Ray job workers are OS-titled ray::IDLE/ray::<task> via setproctitle, but the sensor records the underlying binary as python/python3. This catches a shell spawned by that python parent where the child's own command line still carries a Ray session-path fingerprint (Ray writes runtime state under ray/session_latest). FP: medium — general-purpose python-spawns-shell is common; the ray substring requirement narrows it but validate against your own ML pipeline tooling.
// HUNT: Ray worker (python) process spawning shell referencing a Ray session path // MITRE: T1210, T1059 // CONF: medium FP: medium COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: legitimate ML pipeline/orchestration scripts that shell out and reference Ray paths // TUNING: exclude known CI/ML orchestration hosts and service accounts that legitimately shell out from Ray jobs; validate in tenant // LOOKBACK: 7d (set via console time picker) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^python3?(\.exe)?$/i | FileName=/^(cmd|powershell|pwsh|sh|bash|dash|zsh|nc|ncat|socat|python3?|perl|ruby|osascript)(\.exe)?$/i | CommandLine=*ray* | table([ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, TargetProcessId_decimal])
Looks for: Firefox/Safari (the two vulnerable browsers per the vendor advisory) opening a connection to port 8265 — the confused-deputy step. FP: high — a developer legitimately opening http://localhost:8265 in their own browser to view the dashboard is completely normal and indistinguishable from this at the network layer alone; this query is a candidate-generation feed for manual triage, not a standalone alert. Cross-reference hits against Q5's DNS pattern and against whether the destination was reached via a known-good bookmark/direct navigation vs. an ad/redirect chain (requires browser history, outside Falcon's scope). ⚠ ContextBaseFileName values for browser subprocesses vary by OS/version (e.g. macOS WebKit content-process naming) — validate the exact process names your fleet reports in tenant before tuning thresholds.
// HUNT: Browser process connecting to local/internal Ray dashboard port 8265 // MITRE: T1189, T1210 // CONF: medium FP: high COST: low | REQUIRES: NetworkConnectIP4 // FALSE POSITIVES: developers legitimately viewing their own Ray dashboard UI in a browser // TUNING: cross-reference against Q5 DNS-rebinding candidates before alerting; exclude known developer workstations with an approved, documented reason to browse to their own dashboard // LOOKBACK: 7d (set via console time picker) #event_simpleName=NetworkConnectIP4 | RemotePort=8265 | ContextBaseFileName=/^(firefox(\.exe)?|safari|com\.apple\.webkit\.webcontent|plugin-container)$/i | table([ComputerName, aid, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort, LocalAddressIP4])
Looks for: a ranked list of domains by query volume in a short window, with their resolved IPs collected for manual eyeballing — DNS rebinding shows up as the same domain resolving to a public IP and then, moments later, to 127.0.0.1 or an RFC1918 address, once the browser's same-origin check has already passed. FP: high — legitimate CDNs, load balancers, and failover DNS also rotate answers; this is a candidate-generation query for an analyst, not an auto-alert. ⚠ Field unverified — validate in tenant: the resolved-IP field on DnsRequest is written here as IP4Records; this is not confirmed against this pipeline's own Falcon data-model reference and must be checked against your tenant's actual DnsRequest schema before relying on it — if the field name differs, the query will parse but silently return nothing.
// HUNT: Same domain resolving to multiple distinct IPs incl. loopback/RFC1918 (DNS-rebinding fingerprint) // MITRE: CAPEC-664 (no exact ATT&CK technique for DNS rebinding) // CONF: medium FP: high COST: medium | REQUIRES: DnsRequest // FALSE POSITIVES: CDNs, load balancers, and DNS-based failover legitimately rotate resolved IPs // TUNING: manually review resolved_ips for a mix of a public IP and a loopback/RFC1918 IP for the SAME domain in the SAME short window; a single stable IP list is not a hit // ⚠ Field unverified — validate in tenant: IP4Records // LOOKBACK: 15m (short window — rebinding relies on rapid re-resolution; a long window dilutes the signal) #event_simpleName=DnsRequest | groupBy([DomainName], function=([count(as=query_count), collect(IP4Records)]), limit=max) | rename(field=IP4Records, as=resolved_ips) | sort(query_count, order=desc) | table([DomainName, query_count, resolved_ips])
Looks for: post-exploitation cryptomining, the pattern named "ShadowRay 2.0" in press coverage of this CVE, where compromised GPU-equipped Ray training nodes are repurposed for mining. FP: low — these binary names have no legitimate reason to run under a Ray process.
// HUNT: Known cryptomining binary spawned from raylet/gcs_server/Ray worker python // MITRE: T1496 // CONF: medium FP: low COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: none expected; a renamed miner binary will evade this — see Q7 for a complementary network-based check // LOOKBACK: 7d (set via console time picker) #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(raylet|gcs_server|python3?)(\.exe)?$/i | FileName=/^(xmrig|ccminer|nbminer|t-rex|phoenixminer|lolminer|nanominer|ethminer|kinsing|kswapd0)(\.exe)?$/i | table([ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, SHA256HashData])
Looks for: a complementary, network-based catch for Q6 against a renamed/repacked miner binary — legitimate mining pools listen on a well-known set of ports. FP: high — several of these ports (8080, 9999) are used by unrelated legitimate services; treat as a low-confidence enrichment signal only, correlated with Q6 or unusual sustained outbound traffic volume/CPU-GPU utilization, neither of which is available via this query alone.
// HUNT: Ray core process connecting to a known cryptomining-pool port // MITRE: T1496 // CONF: low FP: high COST: low | REQUIRES: NetworkConnectIP4 // FALSE POSITIVES: legitimate services on 8080/9999/etc.; this is an enrichment signal, not a standalone alert // TUNING: correlate with Q6 hits or sustained high outbound byte-count before triaging as a positive; exclude known internal service ports in this range // LOOKBACK: 7d (set via console time picker) #event_simpleName=NetworkConnectIP4 | ContextBaseFileName=/^(raylet|gcs_server|python3?)(\.exe)?$/i | RemotePort=/^(3333|4444|5555|7777|14444|14433)$/ | table([ComputerName, aid, ContextBaseFileName, RemoteAddressIP4, RemotePort])
CrowdStrike Custom IOA Recommendations
| IOA name | Source query | Pattern | Exclusions | Deployment path |
|---|---|---|---|---|
| Shell Spawned From Ray Core Process (raylet/gcs_server) | Q2 | Parent image = raylet or gcs_server; child image matches shell/interpreter allowlist | None expected in normal operation — monitor only, IOA-eligible | Promote directly to a blocking or high-severity-alerting Custom IOA; this is the pack's highest-confidence, lowest-FP signal |
| Known Cryptominer Spawned From Ray Process | Q6 | Parent image = Ray core/worker; child image matches known-miner binary-name list | None expected; supplement with hash-based detection where possible since names are trivially changed | Promote to a Custom IOA; pair with Falcon's built-in cryptomining detections if licensed for that module |
| Ray Dashboard Bound Beyond Loopback (Q1) | Q1 | State-based network exposure, not an event-driven behavior | Known, documented multi-node cluster head nodes on trusted subnets | Keep as a scheduled hunt/dashboard query, not an IOA — this is a config-audit signal, better suited to periodic scheduled search than real-time event matching |
| Browser-to-Ray-Dashboard Connection (Q4) / DNS-Rebinding Candidate (Q5) | Q4, Q5 | n/a | n/a | Investigate-only — FP profile is too high for IOA promotion without a browser-history/DNS-resolver correlation layer this pack does not have access to |
Machine-Readable IOC Appendix
This threat has no published atomic IOCs — the CSV block below intentionally carries only REPLACE_WITH_… placeholders and behavioral entries. Populate the placeholders only if your own incident response turns up a campaign-specific artifact (e.g., an attacker DNS-rebinding domain observed in your own logs); do not invent values to fill the block.
type,value,action,severity,expiration,description,tags domain,REPLACE_WITH_OBSERVED_REBINDING_DOMAIN,detect,high,2027-02-22,CVE-2025-62593 DNS-rebinding domain observed in your own IR,campaign:Ray-CVE-2025-62593 ipv4,REPLACE_WITH_OBSERVED_ATTACKER_IP,detect,high,2027-02-22,CVE-2025-62593 attacker infrastructure observed in your own IR,campaign:Ray-CVE-2025-62593 sha256,REPLACE_WITH_OBSERVED_PAYLOAD_HASH,detect,high,2027-02-22,Payload dropped via CVE-2025-62593 job entrypoint observed in your own IR,campaign:Ray-CVE-2025-62593
ParentImage: raylet | gcs_server -> Child: shell/interpreter (cmd, powershell, sh, bash, python) Listening socket: 0.0.0.0:8265 or non-loopback bind of the Ray dashboard/Jobs API HTTP: POST /api/jobs/ or /api/job_agent/jobs/ with User-Agent NOT starting with "Mozilla" DNS: same domain resolving to a public IP then to 127.0.0.1/RFC1918 within a short window Process: xmrig/ccminer/nbminer/t-rex/phoenixminer/lolminer/nanominer/ethminer/kinsing spawned from raylet/gcs_server/python
RondoDox botnet - attempted pre-disclosure exploitation of CVE-2025-62593 (per Bitsight/Resecurity); did not successfully bypass Ray's browser check per Resecurity's analysis. No atomic RondoDox IOC in this pack. "ShadowRay 2.0" - press-coined name for GPU-cryptomining campaigns against unpatched, exposed Ray instances. No atomic indicators published; hunt via Q6/Q7 behavior.
[ ] Ray version >= 2.52.0 (2.52.1 preferred) on every host/image/cluster [ ] Dashboard NOT bound to 0.0.0.0 -- confirm --dashboard-host is 127.0.0.1 or a firewalled private address [ ] Port 8265 (and 6379 GCS) not reachable from any untrusted network or the public internet [ ] Ray 2.52+ token authentication enabled (it ships disabled by default) [ ] KubeRay dashboard Service type is ClusterIP, not LoadBalancer/NodePort, with a NetworkPolicy restricting ingress [ ] Credentials/tokens on any historically-exposed Ray host rotated
Detection Validation Gates
| Gate | Check | Status before deployment |
|---|---|---|
| Telemetry ready | Confirm ProcessRollup2/SyntheticProcessRollup2, NetworkConnectIP4, NetworkListenIP4, and DnsRequest are flowing for every host in scope | Required before Q1-Q4, Q6-Q7 are trustworthy |
| Benign baseline | Run Q1 and Q4 first in investigate mode against known-good Ray dev hosts to characterize normal exposure/browsing patterns before tuning exclusions | Required — both have real FP sources documented above |
| Field verification | Confirm the IP4Records field name on DnsRequest in your own tenant before relying on Q5 (flagged unverified in the query card) | Required before Q5 is trusted |
| Positive test | Where safe, replicate the vendor PoC (POST a benign entrypoint to a lab Ray instance from a script with a non-Mozilla User-Agent) to confirm Q2/Q3 fire on the resulting child process | Recommended in an isolated lab, never against production |
| Promotion | Only promote Q2 and Q6 to blocking/high-severity IOAs (see §9); keep Q1, Q3, Q4, Q5, Q7 as scheduled hunts pending FP tuning | Gate before §9 IOAs go live |
Hardening — Tiered
Upgrade Ray to ≥2.52.0 (2.52.1 preferred) on every host, container image, and cluster template. M1050 Update Software. This is the vendor fix commit (70e7c72) that corrects the browser-request check. Verify: pip show ray | grep -i version on a sample of the fleet.
Bind the Ray dashboard/GCS to loopback or a private, firewalled interface — never 0.0.0.0 on anything internet-reachable. M1030 Network Segmentation / M1035 Limit Access to Resource Over Network. Verify: run Q1 (§8) and confirm zero non-loopback listeners outside a documented, trusted cluster subnet.
Firewall port 8265 (and 6379/GCS, plus the worker port range) from any untrusted network at the host and network layer. M1037 Filter Network Traffic. Verify: external port scan / cloud security-group audit.
Enable Ray 2.52+'s token authentication — it ships disabled by default, so patching alone does not turn it on. ⚠ best-practice, no formal CIS/vendor-baseline benchmark exists for Ray specifically — this is the vendor's own documented hardening recommendation in the GHSA. Verify: §7's token-auth status audit.
KubeRay clusters: set the dashboard Service type to ClusterIP (never LoadBalancer/NodePort) and apply a Kubernetes NetworkPolicy restricting ingress to the dashboard port to trusted namespaces only. M1030 Network Segmentation, per CIS Kubernetes Benchmark's NetworkPolicy guidance. Verify: kubectl get networkpolicy -n <ray-namespace>.
Standardize developer browser policy toward Chromium-based browsers where feasible, or enforce Private Network Access / Local Network Access enforcement where the browser vendor supports it — Chrome's PNA behavior is the reason Chrome-only users are not equally exposed. ⚠ best-practice, no formal benchmark — Firefox does not yet enforce PNA per the sources reviewed; this reduces but does not eliminate risk. Verify: browser enterprise-policy configuration audit.
Deploy the CQL detections in §8 as scheduled searches, working through the validation gates in §11 before promoting Q2/Q6 to IOAs.
Rotate credentials/tokens on any host with historical internet exposure of the Ray dashboard, treating exposure as presumed compromise pending forensic review. Best practice — general credential hygiene, no single formal citation.
Formalize an internal policy requiring network-segmented, authenticated Ray deployment as a standard, not an opt-in — this bug exists because the ecosystem defaulted to "trusted network" as its only control for years. M1030 + M1035, defense-in-depth framing.
Establish quarterly exposure scanning of dashboard/management ports (8265, 6379, and equivalents for other ML-tooling frameworks) across the cloud and dev fleet, not just at patch time. M1016 Vulnerability Scanning.
Architecture review of any other internally-trusted, unauthenticated-by-default dev tooling exposed to developer workstations running browsers — this vulnerability class (browser as confused deputy via DNS rebinding into a "localhost-trusted" service) generalizes beyond Ray.
Deployable Playbooks
Playbook 1 — Upgrade Ray and verify version (Linux/macOS)
Prerequisites: pip/conda access to the target environment; a maintenance window if Ray is actively serving production jobs. Reboot required: No — restart the Ray process/cluster, not the host. Rollback: pin back to the prior version with pip install ray==<previous_version> if the upgrade breaks a dependency; keep the prior requirements.txt/lockfile on hand before upgrading.
pip install -U "ray>=2.52.0" ray --version # Restart any running cluster so the new binary takes effect: ray stop ray start --head # or your cluster's normal start invocation
Playbook 2 — Bind dashboard to loopback and enable token auth
Prerequisites: Ray ≥2.52.0 already installed (Playbook 1). Know whether any legitimate remote user needs dashboard access (if so, front it with an authenticated reverse proxy/VPN instead of a public bind). Reboot required: No — Ray process restart only. Rollback: revert the start flags to the prior invocation; if a documented remote-access need existed, restore the previous reverse-proxy config rather than re-exposing the raw port.
# Bind dashboard to loopback only (default in recent Ray, but verify explicitly): ray start --head --dashboard-host=127.0.0.1 # Enable token authentication (Ray 2.52+, disabled by default -- exact flag/env # var per your installed Ray minor version's release notes; verify against # `ray start --help` on your installed version before scripting this into CI): # RAY_ENABLE_AUTH=1 ray start --head --dashboard-host=127.0.0.1 ray status
Playbook 3 — Host/network firewall rule blocking external access to Ray ports
Prerequisites: sudo/root on the host, or cloud security-group edit access; confirm no legitimate cross-host cluster traffic will be blocked (check worker port range for multi-node clusters first). Reboot required: No. Rollback: remove the added rule (ufw delete deny ... / revert the security-group change) to restore prior connectivity.
# Linux host firewall (ufw example) -- deny 8265/6379 from anything outside a trusted subnet: sudo ufw deny from any to any port 8265 proto tcp sudo ufw allow from 10.0.0.0/8 to any port 8265 proto tcp sudo ufw deny from any to any port 6379 proto tcp sudo ufw allow from 10.0.0.0/8 to any port 6379 proto tcp sudo ufw reload # Cloud security group (AWS CLI example) -- remove any 0.0.0.0/0 ingress on 8265/6379: aws ec2 revoke-security-group-ingress --group-id sg-REPLACE_WITH_SG_ID --protocol tcp --port 8265 --cidr 0.0.0.0/0 aws ec2 revoke-security-group-ingress --group-id sg-REPLACE_WITH_SG_ID --protocol tcp --port 6379 --cidr 0.0.0.0/0
Playbook 4 — Kubernetes NetworkPolicy restricting KubeRay dashboard ingress
Prerequisites: a CNI plugin that enforces NetworkPolicy (Calico, Cilium, etc. -- confirm before relying on this, some default CNIs silently no-op policies); cluster-admin or namespace-admin access. Reboot required: No. Rollback: kubectl delete networkpolicy ray-dashboard-restrict -n <namespace> to remove the policy and restore prior (unrestricted) ingress.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ray-dashboard-restrict
namespace: REPLACE_WITH_RAY_NAMESPACE
spec:
podSelector:
matchLabels:
ray.io/node-type: head
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
trusted-ray-client: "true"
ports:
- protocol: TCP
port: 8265
Playbook 5 — Post-hardening verification spot-check (CQL)
Prerequisites: Playbooks 1-4 applied; Falcon sensor deployed and reporting for the target hosts. Reboot required: No -- this is a read-only verification query. Rollback: none required -- it changes nothing.
// HUNT: Post-hardening spot-check -- confirm Q1's non-loopback exposure is gone #event_simpleName=NetworkListenIP4 | LocalPort=8265 | LocalAddressIP4!="127.0.0.1" | table([ComputerName, aid, LocalAddressIP4, LocalPort]) // Expected result after Playbooks 1-3: zero rows outside documented, trusted cluster subnets.
Containment Runbook
| Phase | Actions | Owner | Evidence to preserve |
|---|---|---|---|
| Isolate | Network-isolate the affected Ray host (endpoint containment via Falcon, or remove from the load balancer/cluster); do not simply restart the Ray process, which destroys process-tree evidence | IR / SOC on-call | Falcon process tree for the suspect job's entrypoint; any Ray dashboard access logs covering the incident window |
| Eradicate | Kill the malicious child process and any persistence it established; patch to Ray ≥2.52.0 and apply Playbooks 2-3 before returning the host to service; rotate every credential/token the Ray process had access to (cloud keys, S3/GCS creds, internal service tokens) | IR + platform/infra owner | Hash and preserve any dropped payload before deletion; document exact credential set rotated |
| Recover | Redeploy from a known-good, patched image/config rather than trusting the compromised host's current state; re-enable with hardened config (loopback bind, firewall, token auth) from day one; re-add to the cluster only after passing Playbook 5's verification query | Platform/infra owner | Redeployment record; Playbook 5 query result showing zero exposure |
| Post-incident | Sweep the rest of the fleet for the same exposure pattern (Q1) and the same process-spawn pattern (Q2/Q3) across the retention window; update the exposure-audit checklist cadence per §12's strategic tier | Threat hunting / detection engineering | Fleet-wide Q1/Q2/Q3 sweep results |
Detection Coverage Map
| Technique / Chain step | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| Step 1 — Recon / precondition | Ray dashboard exposed on non-loopback interface | Q1 | No (scheduled hunt only) | Partial |
| Step 2-3 — DNS rebinding | Domain re-resolving to loopback/RFC1918 | Q5 (unverified field) | No | GAP — DNS-rebinding detection is fundamentally resolver-side, not endpoint-side; Q5 is best-effort only |
| Step 4 — Browser bypasses UA check | Browser connects to local Ray dashboard port | Q4 | No | Partial — high FP without browser-history correlation this pack lacks |
| Step 5 — Malicious job submitted to Jobs API | Non-Mozilla UA POST to /api/jobs/ | None — not native Falcon telemetry | No | GAP — requires Ray dashboard access-log ingest; see native hunt §7 |
| Step 6 — RCE via job entrypoint | Shell/interpreter spawned from raylet/gcs_server/Ray worker | Q2, Q3 | Q2 (recommended) | Good — highest-confidence coverage in the pack |
| Step 7 — Post-exploitation (cryptomining) | Known miner binary or mining-pool connection from Ray process | Q6, Q7 | Q6 (recommended) | Good for named binaries; Partial for renamed/unknown miners |
| Step 7 — Post-exploitation (credential/data theft, lateral movement) | Credential access, data staging from compromised Ray host | None — generalized to your existing credential-access/exfil detections | No | GAP — out of scope for this pack; route to existing credential-theft/exfil detection coverage |
Validation gates: see §11 for the full gate table. Summary — confirm telemetry coverage and baseline FP before enabling any query; verify the flagged IP4Records field on Q5 in your own tenant; only Q2 and Q6 are recommended for IOA promotion at this time.
Hunt Summary Ticket
TITLE: Hunt — Ray-Project Ray Dashboard/API RCE via DNS-Rebinding UA Bypass (CVE-2025-62593)
SEVERITY: Critical (CVSS 9.4, CISA KEV, actively exploited)
SCOPE: All hosts/images/clusters running Ray <2.52.0; all developer workstations running Ray + Firefox/Safari;
all KubeRay/cloud-hosted Ray clusters
HYPOTHESIS: An attacker-controlled webpage/ad drives a victim browser, via DNS rebinding + User-Agent bypass,
to submit a malicious job to Ray's unauthenticated Jobs API, achieving RCE as the Ray process.
QUERIES: Q1 (exposure) / Q2 (raylet-gcs shell spawn, high-conf) / Q3 (worker python shell spawn) /
Q4 (browser-to-dashboard connection) / Q5 (DNS-rebinding candidate, unverified field) /
Q6 (known miner spawn) / Q7 (mining-pool port connection)
DO FIRST: Confirm patch status (Ray >=2.52.0) and dashboard exposure (Q1) fleet-wide -- this closes the
precondition regardless of hunt results. Then run Q2 and Q6 as the highest-confidence sweeps.
FINDINGS: [populate after hunt execution]
GAPS: DNS-rebinding detection (Step 2-3) and Jobs-API abuse detection (Step 5) both lack native Falcon
telemetry coverage -- see Coverage Map (S15) and native audit-log hunts (S7).
ACTIONS: Patch, firewall, enable token auth (S12); deploy Q2/Q6 as IOAs after S11 validation gates pass.
OWNER: [assign]
VERSION: v0.1 · 2026-08-22
Changelog
References
| Tier | Source | Used for | Access date |
|---|---|---|---|
| Gov | CISA KEV Catalog — CVE-2025-62593 | Authoritative dateAdded/dueDate/BOD, header meta, ticket | 2026-08-22 |
| Vendor | Ray Project GHSA-q279-jhrf-cc6v | Root-cause technical detail, PoC, CVSS vector, fix commit, remediation | 2026-08-22 |
| Independent research | Resecurity — Ray CVE-2025-62593 technical write-up | Attack-chain staging, endpoint/port detail, candidate IOC list | 2026-08-22 |
| Journalism | The Hacker News — CISA Flags Actively Exploited Ray Flaw | KEV context, RondoDox/ShadowRay 2.0 campaign naming | 2026-08-22 |
| Journalism | GBHackers — CISA Warns of Active Exploitation of Ray-Project Ray Code Injection Vulnerability | Impact narrative, mitigation checklist framing | 2026-08-22 |
| Journalism | Security Affairs — U.S. CISA adds a Ray-Project Ray flaw to its KEV catalog | KEV addition context; one factual point (BOD number) corrected against primary source | 2026-08-22 |