VORVEXAPEX

CVE-2025-62593: a malicious ad running commands on your AI cluster

The service was bound to 127.0.0.1, with no port open to the internet, and an ordinary website still managed to send it a command. The security check that failed fit on a single line of code.

By Equipe VorvexPublished on August 20, 20266 min read

Almost every attack surface inventory starts with the same question: what is exposed to the internet? It is the right question, and it leaves out exactly the kind of flaw CISA added to the Known Exploited Vulnerabilities catalog on August 17, 2026. CVE-2025-62593 affects Ray, the distributed computing framework used to train and serve AI models, and it needs no port open to the world. It needs a developer to open a browser tab.

Diagram showing a developer laptop with an open browser on one side and a malicious web server on the other, with a local machine learning dashboard bound to the loopback address inside the laptop. Arrows show the attacker domain first resolving to the attacker public IP address and then re-resolving to the loopback address, so the browser request lands on the local dashboard.
In DNS rebinding, the domain name stays the same as far as the browser is concerned, but it starts pointing inside the victim's own machine.

The flaw in one sentence

CVE-2025-62593 is a code injection issue (CWE-94, combined with CWE-352, cross-site request forgery) that lets a malicious website send requests to the Ray dashboard and API running on a developer's machine, all the way to arbitrary command execution. NVD assigns a CVSS 4.0 score of 9.4, critical, with the vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H, and a CVSS 3.1 score of 8.8, high, with CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H. The gap between the two scores comes down to user interaction, which in practice means clicking a link or seeing an ad.

  • Affected: every Ray version prior to 2.52.0
  • Fixed in: Ray 2.52.0
  • Vulnerable endpoints: /api/jobs/ and /api/job_agent/jobs/, on the dashboard's default port 8265
  • Browsers that allow the attack: Firefox and Safari; Chrome escapes because of an implementation bug in its own fetch API
  • Published on NVD on November 26, 2025, added to the CISA KEV catalog on August 17, 2026

The flaw was found by avilum, of Oligo, and reported with a proof of concept by JLLeitschuh, of Socket. The credit matters here because the fix was not just a targeted patch: version 2.52.0 also introduced token authentication for the dashboard, a feature that simply did not exist before. Worth noting that this authentication ships disabled by default, so updating is not the last step, it is the first one.

The defense that was not a defense: checking the User-Agent

Ray knew that requests coming from a browser were dangerous for an API that executes code. The protection it implemented was to check whether the request's User-Agent header started with the word Mozilla, on the assumption that only browsers send that value and that browsers cannot change it.

python
# Ray < 2.52.0: a unica barreira contra requisicao vinda de navegador.
def is_browser_request(req: Request) -> bool:
    """Checks if a request is made by a browser like user agent."""
    return req.headers["User-Agent"].startswith("Mozilla")

# A premissa: um navegador nao consegue alterar o proprio User-Agent.
# Falsa no Firefox e no Safari, onde a fetch API aceita definir o header.
# No Chrome, um bug de implementacao acabou impedindo a alteracao,
# ou seja, a protecao so funciona por acidente, em um navegador so.
The entire check fits on one line, and the assumption behind it is false in two of the three most used browsers.

The second half of that assumption is that there is a list of headers a page's JavaScript cannot set, the so-called forbidden headers. User-Agent was on that list for a long time, and then it was removed. Firefox and Safari implement the current specification and allow User-Agent to be set on a fetch call. Chrome still blocks it, but because of an implementation defect, not a security decision. A protection that depends on someone else's bug to keep working is not a protection, it is a window of time.

DNS rebinding: the browser as a bridge into the network

Breaking the User-Agent check is not enough on its own, because the browser's same-origin policy stops a site's JavaScript from reading the response from another domain. DNS rebinding exists precisely to get around that: instead of trying to reach another domain, the attacker keeps the same name and changes the IP address it resolves to.

text
# Sequencia de um DNS rebinding, do ponto de vista do navegador.

1) A vitima abre https://site-do-atacante.exemplo
   DNS: site-do-atacante.exemplo -> 203.0.113.10   (TTL de 1 segundo)

2) A pagina carrega e o JavaScript apenas espera o TTL expirar.

3) O navegador resolve o MESMO nome de novo:
   DNS: site-do-atacante.exemplo -> 127.0.0.1

4) fetch("http://site-do-atacante.exemplo:8265/api/jobs/", { ... })
   Para a politica de mesma origem, continua sendo a mesma origem.
   Para a pilha de rede, o pacote agora vai para o servico local.
The domain name never changes, so the browser treats it all as the same origin. What changes is where that name points.

The result is that the victim's browser starts acting as an HTTP client inside the trusted network, taking orders from a script the attacker controls. That holds for Ray on port 8265, but it holds just as well for any admin panel, home router, development database or internal API that trusts the fact that it is only listening on loopback or on the local network. As far as the browser is concerned, loopback was never a trust boundary.

From /api/jobs to arbitrary commands

Once the attacker reaches /api/jobs/ and /api/job_agent/jobs/, the rest is the product working as designed. Ray's jobs API takes an entrypoint field with the command to run on the cluster and runs it. That is what it exists to do. The vulnerability is not in the execution, it is in an arbitrary web page being able to reach that call.

http
POST /api/jobs/ HTTP/1.1
Host: 127.0.0.1:8265
Content-Type: application/json
User-Agent: laboratorio-interno

{"entrypoint": "id", "runtime_env": {}, "metadata": {}}

# O campo entrypoint e uma linha de comando executada no cluster.
# Aqui usamos 'id' apenas para ilustrar. O ponto do post nao e o
# comando, e o fato de essa requisicao poder partir de uma aba
# de navegador aberta em um site qualquer.
The API does exactly what it promises. The problem is reach: with rebinding, the request is written by the site the victim opened.

In impact terms, the workstation of someone building models tends to be one of the most interesting machines in a company. It holds cloud provider API keys, private repository tokens, data lake credentials, GPU access and, quite often, a local copy of training data. A command executed there is rarely the attacker's end goal, it is the starting point.

RondoDox, ShadowRay 2.0 and CISA's three-day deadline

The timeline of this flaw has an unusual detail. According to Bitsight's analysis, the RondoDox botnet started attempting to exploit CVE-2025-62593 on November 24, 2025, two days before the vulnerability was published on NVD. That is not the usual pattern of reverse engineering a patch after the announcement: whoever ran that botnet was watching the repository, not the bulletin.

On August 17, 2026, CISA added the flaw to the KEV catalog, and under directive BOD 26-04 US federal agencies were given three days to remediate, with a deadline of August 20, 2026. Three days is the window reserved for what is already being exploited consistently, and it is a reasonable priority signal for organizations that are not US federal agencies too.

It is worth separating this flaw from a second story involving the same product that often gets confused with it. The ShadowRay 2.0 campaign, documented by Oligo, exploits CVE-2023-48022, which covers Ray clusters whose jobs API is reachable without authentication and exposed directly on the internet. That CVE is marked as disputed, because Anyscale maintains it is documented product behavior that assumes execution inside a controlled network. The campaign turns exposed clusters into a cryptocurrency mining botnet, and Oligo reports that the number of reachable Ray environments jumped from a few thousand to the hundreds of thousands.

The two stories point to the same lesson from opposite directions. In CVE-2023-48022, the cluster is exposed and the absence of authentication is by design. In CVE-2025-62593, the service is not exposed and is reached anyway, through the browser. In both cases, what breaks is the assumption that the network solves the authentication problem.

What this changes in pentest scope

Illustration of a developer workstation drawn as a large box containing several small service icons bound to local ports, with a browser icon inside the same box connected by an arrow to the public internet outside the box, and a return arrow showing external instructions reaching the internal services through the browser.
Services listening only on localhost are still reachable by the browser running on the same machine. The browser is the bridge that crosses the network boundary.

In the scopes that reach us, the workstation almost never makes the cut, and local development services make it even less often. The reasoning is always the same: it is not exposed. This CVE is a direct counterexample, and it is not the only one. Container orchestration dashboards, Jupyter notebooks, documentation servers, build tools with an HTTP API, debugging proxies, all of it tends to come up on localhost without authentication because it feels like a private environment.

  • Update Ray to 2.52.0 or later everywhere, including workstations, which are the direct target of this flaw
  • Enable Ray's token authentication, available from 2.52.0 onward but shipped disabled by default
  • Inventory the services listening on localhost across development machines and treat each one as reachable by the browser, not as a private environment
  • Check whether any Ray cluster is reachable from the internet, the CVE-2023-48022 and ShadowRay 2.0 scenario, which is a separate and more severe problem
  • If a development workstation is suspected of compromise, rotate cloud keys, repository tokens and data credentials before calling the case closed
  • Put workstations and AI infrastructure explicitly in scope for the next pentest, not as supporting items, but as targets

This post is educational and defense oriented. We do not publish working exploits: the snippets above reproduce code already disclosed in the official advisory and the documented use of the product's API. What we want to make clear is the assumption that failed. When a service's protection is the address it listens on, anyone with a browser inside the network is already on the inside.

← Back to blog

Want that same depth applied to your environment?

Tell us what you need to validate and the team scopes a penetration test to match.

Talk on WhatsApp

Ready to assess your company's risk?