AI Security

Ray's Browser Defense Was One User-Agent Check. DNS Rebinding Walked Past It.

Dark cyberpunk illustration of a small glass server module glowing on a desk with a looping amber light trail redirecting one of its cyan network connections back on itself.

Three days. That is the entire window CISA gave organizations to act on CVE-2025-62593 before its August 20 deadline, and the bug is not a new hole in Ray, the open-source AI compute engine teams use to scale Python, machine-learning, and LLM workloads across a cluster. It is proof that the one browser-side defense Ray shipped against this exact class of attack does not hold up.

CVE-2025-62593 carries a CVSS 4.0 base score of 9.4. CISA added it to the Known Exploited Vulnerabilities catalog on August 17 and set a due date of August 20, tagging it with both CWE-94 (code injection) and CWE-352 (cross-site request forgery). Those two weaknesses together describe the attack precisely: a malicious web page can forge a request that runs arbitrary code on a Ray node, without the attacker ever touching your network directly.

If Ray is nowhere in your stack, not for training, not for serving, not tucked behind a higher-level platform your team did not choose, this one is not yours, and you can stop here. If anyone on your team runs ray start on a laptop, operates Ray Serve for inference, or manages a KubeRay deployment, keep reading even if you are confident the Ray dashboard has never been reachable from the internet. That confidence is exactly what this bug defeats.

The endpoint that only checked for the word "Mozilla"

Ray's dashboard exposes two HTTP endpoints, /api/jobs and /api/job_agent/jobs/, that accept job submissions with no authentication by default. That has been true since Ray shipped the feature, and it is also the root of the original ShadowRay campaign against internet-exposed Ray clusters. The gap CVE-2025-62593 closes is narrower: the one defense Ray added to stop a browser from reaching those same endpoints on a dashboard deliberately bound to 127.0.0.1 or a private interface, never exposed to the public internet at all.

That defense checked whether an incoming request's User-Agent header started with the string "Mozilla." The logic assumed a legitimate script or CLI client would never send that string, so blocking it would filter out browser traffic while letting real API clients through. Chrome enforces the header at the browser level and does not let JavaScript override it, so the check holds there. Firefox and Safari do not enforce that restriction on their fetch() implementation, so a script on any page a victim visits can set a spoofed User-Agent starting with "Mozilla" anyway, defeating the one check standing between a malicious web page and the Ray API.

Combine that with DNS rebinding, a technique documented since the early 2000s and automated today by tools like NCCGroup's Singularity, and an attacker does not need the dashboard reachable from the public internet at all. DNS rebinding tricks the victim's browser into believing it is still talking to the attacker's domain while the request actually lands on 127.0.0.1:8265, the local Ray dashboard the page has no business reaching. A developer running Ray on a laptop who opens a compromised or malicious page in Firefox or Safari can hand an attacker remote code execution on their own machine, with the Ray dashboard never having left localhost.

The chain, step by step

  • The victim, running Ray locally or on an internal network, opens a page in Firefox or Safari that hosts the attacker's script.
  • The attacker's DNS server first resolves its own domain normally, then rebinds it to 127.0.0.1 once the browser has loaded the page and trusts the origin.
  • The page's script issues a fetch() request to what it believes is still the attacker's domain; DNS resolves it to the Ray dashboard instead.
  • The script sets a spoofed User-Agent starting with "Mozilla," which Firefox and Safari allow and Ray's check treats as a non-browser client.
  • The request lands on /api/jobs or /api/job_agent/jobs/ with no credentials required, and Ray runs whatever job payload the attacker submitted, with the privileges of whoever started the Ray process.

Nothing in that chain requires the attacker to know the victim's IP address, port-scan their network, or send a single packet to it directly. The victim's own browser does all the routing, which is precisely why a dashboard bound to loopback stopped being a meaningful boundary the moment this technique was pointed at it.

The facts, in one place

What's confirmed

  • CVE: CVE-2025-62593, CVSS 4.0 base score 9.4, CWE-94 (code injection) and CWE-352 (CSRF).
  • Affected: Ray versions before 2.52.0. Fixed: Ray 2.52.0 and later.
  • Credited: researchers JLLeitschuh (Socket) and avilum (Oligo Security), per GitHub Security Advisory GHSA-q279-jhrf-cc6v.
  • CISA KEV: added August 17, 2026; due date August 20, 2026, under BOD 26-04.
  • Fix commit: ray-project/ray@70e7c72.
  • Companion gap: CVE-2026-27482 shows the browser-origin block covers POST and PUT but not DELETE, so the same DNS-rebinding path can also shut down a running Ray Serve deployment or delete jobs outright.

Patch, isolate, or both, because neither alone is enough

Three responses are on the table once you confirm Ray is in your environment, and only one of them actually closes the gap.

Network isolation alone does not work here. Keeping the dashboard off the public internet, bound to loopback or a private subnet, was the exact assumption CVE-2025-62593 defeats. A browser attacker never needs a route to your network; it needs the victim's browser to visit a page, and DNS rebinding does the rest. If isolation is the only control in place today, treat that as the finding, not the mitigation.

Upgrading closes the gap, but only if you also turn the new control on. Ray 2.52.0 added optional token-based authentication across the dashboard, CLI, and internal RPC surface. It ships off by default, so a version bump alone leaves the endpoints exactly as open as before.

# Confirm the version actually running, not what requirements.txt claims.
python3 -c "import ray; print(ray.__version__)"

# From a workstation on the same segment as the Ray head node (not the node
# itself), confirm whether the Jobs API answers without credentials today.
curl -s -o /dev/null -w "%{http_code}\n" http://<ray-head-host>:8265/api/jobs/

# Upgrade, then turn on the token auth Ray shipped in 2.52.0 - it is opt-in.
pip install --upgrade "ray>=2.52.0"
export RAY_AUTH_MODE=token
ray start --head --dashboard-host=127.0.0.1
# Ray writes the generated token on the head node. Distribute it via
# RAY_AUTH_TOKEN or the X-Ray-Authorization header - never commit it to a repo.

Monitoring is what catches what the first two miss. Treat every Ray head node as a sensitive asset in your logging pipeline. Alert on job submissions to /api/jobs or /api/job_agent/jobs/ that do not carry a valid token once you have enabled auth, and review job-submission history for the days before you patched. Treat a hit there as a start-of-incident finding, not something to triage later at leisure.

The deployment model changes what "patched" means to check. A solo developer's ray start on a laptop is a single upgrade and one environment variable. A KubeRay deployment on Kubernetes needs the RayCluster and RayService custom resources rebuilt from an updated image, and the dashboard-host and auth settings verified in the Helm values or CR spec, not just the base image tag. If your workloads run on Anyscale-managed compute, confirm with your account team which layer owns the patch and whether token auth is already enforced at the platform level; do not assume a managed control plane closes a gap that lives in the open-source Ray image underneath it.

Why "it's only on localhost" keeps failing as a security boundary

Ray's own history makes the pattern hard to miss. The original unauthenticated Jobs API, tracked as CVE-2023-48022, is still disputed by the Ray team as intended behavior rather than a vulnerability, and it still carries no official patch. Oligo Security researchers Avi Lumelsky and Gal Elbaz published a follow-up in November 2025, ShadowRay 2.0, documenting more than 200,000 Ray servers still exposed to the open internet, a tenfold increase since the issue was first reported in 2024. One compromised cluster they traced represented more than $3 million a year in on-demand compute cost; attackers used others to mine Monero, exfiltrate 240GB of compressed proprietary data, and run a self-propagating botnet.

CVE-2025-62593 is the same underlying philosophy failing a second, quieter way. Ray's team did add a defense once the Jobs API's total lack of authentication became impossible to ignore, but the defense assumed a browser could be reliably distinguished from a script by one header, and that a dashboard kept off the public internet was safe from a browser-based attacker by default. Both assumptions were reasonable enough five years ago to ship. DNS rebinding is not new, and the Firefox and Safari fetch() behavior it relies on here has been documented, spec-compliant browser behavior for years; Ray's mitigation simply did not account for it. The companion bug, CVE-2026-27482, makes the same class of gap again in the same dashboard: the browser-origin block covers POST and PUT but not DELETE.

Every AI or ML tool that ships a local dashboard and treats "not internet-facing" as its whole security boundary is one DNS-rebinding proof-of-concept away from the same finding. Ray is simply the one CISA put a deadline on this week.

Enable token auth before you trust the loopback binding

Patch every Ray node to 2.52.0 or later this week; CISA's due date is August 20 and the fix is a version bump plus one environment variable, not a re-architecture. Set RAY_AUTH_MODE=token on every cluster you control, including the ones you were confident nobody could reach, because this vulnerability specifically targets the developer machines and internal clusters that confidence protects only on paper. Then check whether the Jobs API answered without a token before you patched. If your logs show unauthenticated calls to /api/jobs or /api/job_agent/jobs/ predating today, treat it as a confirmed access event and rotate whatever the Ray process could reach, cloud API keys, model registry tokens, and any service account in its environment, not just the Ray cluster itself.

Know what AI infrastructure is actually reachable on your network?

We map the AI and ML tooling running in an environment, dashboards, agent frameworks, and compute clusters, the way an attacker would, and help close what should not be open before CISA has to put a deadline on it. Book a session to scope an assessment.