V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mikewang
V2EX  ›  信息安全

Clash 检测工具的原理

  •  
  •   mikewang · 2024-09-30 04:02:41 +08:00 · 11787 次点击
    这是一个创建于 385 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我在 /t/1076579 给出了 Clash 检测的在线工具,有评论希望我能说明以下其中的原理。

    对此比较感兴趣的,可以阅读一下本文。


    1. 基于跨域缺陷的利用

    首先,需要了解两个术语:「同源策略」和「跨域资源共享」。

    同源策略( Same-origin Policy )

    当 B 网站的脚本,想请求 A 网站的资源时,浏览器的「同源策略」会禁止这一行为:防止 B 网站伪造您的请求,同时防止您在 A 网站上隐私的泄露。

    举例:
    www.google.com 下按 F12 ,输入 await fetch("https://bing.com") 回车,然后你会看到一条报错信息。浏览器默认阻止这一行为。

    跨域资源共享( CORS )

    而这一限制有时也会过于严格,因为 A 网站和 B 网站可能是来自同一家。因此 A 网站可以设置一个列表,允许特定的网站去访问它的数据。这就是「跨域资源共享」,这个列表就是「 Access-Control-Allow-Origin 」。

    举例(不好的例子):
    Clash / Clash Meta 核心设置了「Access-Control-Allow-Origin: *」,通配符允许了所有网站的脚本都能调用它。
    假设 9090 是您的 Clash API 端口( Clash Verge 默认是 9097 )。在 www.google.com 下按 F12 ,输入 await fetch("http://127.0.0.1:9090") 回车,你会发现请求成功了。

    Clash 为什么这么做?

    Clash 系列核心没有用户界面( GUI ),但它可以在本地运行一个 HTTP 服务,方便各种 GUI 通过 HTTP 请求调用它的接口。

    其中一些 GUI 以网页形式呈现,例如「https://d.metacubex.one/」。Clash 系列核心必须开一个“后门”,也就是允许跨域资源访问,否则这些 GUI 将无法运作。

    不过,这个“后门”显然开得过大了。应该增加一个配置项,只允许特定网站,而不应使用 * 通配符。

    怎么利用呢?

    无密码保护的情况下,任意网站调用 await fetch("http://127.0.0.1:{{port}}") 就能利用。
    常见的端口有 90909097。如果不是,还可以遍历 1 - 65535 全部尝试一次。

    成功利用可以获得对 Clash 核心的全部操控权限,包括获取服务器列表,修改代理规则等。

    2. 基于已知路径的识别

    上一节中,能利用的前提是「无密码保护」。有密码的情况下,访问接口会得到 401 的错误响应。单从 401 错误,没有证据表明用户在使用 Clash 。但我们可以通过已知路径,去识别是否为 Clash 。

    Clash 的接口路径

    在源码 /hub/route/server.go 中,可以获得接口的路径:

    • /logs
    • /traffic
    • /memory
    • /version
    • ...

    明显的特征是:访问这些路径,会得到 401 响应,而访问其他路径则会得到 404

    另外,不同的 Clash 版本,支持的 API 也不完全相同。通过探测支持的 API (返回 401 而不是 404),我们能进一步推断出 Clash 的版本号。

    利用前提

    利用的前提依然是上一节的跨域缺陷,因为如果不具备这一条,请求会直接出错失败,而不会得到 HTTP 响应码。

    3. 基于代理端口的识别

    前两节都是针对 API 端口的利用。我们还可以通过检测常见的代理端口(默认的 7890 和 Clash Verge 默认的 7897),判断用户是否在使用 Clash 。

    普通端口的情况

    对于普通的端口,不总是能碰到跨域缺陷,甚至可能都不是 HTTP 端口。对于非 HTTP 端口,不论怎么请求肯定都会是失败的。

    耗时检测

    参考以下 JavaScript 函数:

    async function portTime(port) {
        const st = performance.now();
        try {
            await fetch("http://127.0.0.1:" + port);
        } catch (error) {}
        const et = performance.now();
        return et - st;
    }
    

    该函数可以检测访问一个端口的耗时,并忽略失败。

    一般情况下,用户本地的端口大多处于关闭状态。对于关闭状态的端口,耗时均是差不多的。我们可以随机抽取几个端口进行检测,认为是关闭端口的耗时。

    如果 78907897 端口的访问耗时明显区分于抽选端口的耗时,我们可以推测 Clash 代理的端口是打开的。

    耗时检测为什么可行?

    上文提到过,只有 Access-Control-Allow-Origin 允许,才能进行跨域资源的访问,否则会出错。

    实际上,出错不意味着没有请求发生。浏览器首先需要发送 HTTP OPTIONS 请求,才能知道 Access-Control-Allow-Origin 的情况。这个请求称为「 Preflight request 」。跨域检测通过之后,浏览器才会再去执行脚本指定的 HTTP 请求。

    因此,即便浏览器报错拦截,实际上还是有请求发生了。这就是耗时检测的原理。

    可利用情况

    在 Windows 平台,操作系统收到 TCP RST 报文后,并不会立即认为端口关闭,而会重试 2 秒后返回错误。因此对于耗时检测,耗时两秒左右的端口是关闭的,毫秒级别耗时的端口是打开的。

    在 macOS 和 Linux 平台,情况则相反,端口关闭的耗时比端口打开的短。不过由于区分度太小,准确性较低。

    46 条回复    2024-12-03 22:54:48 +08:00
    codehz
        1
    codehz  
       2024-09-30 08:28:13 +08:00
    其实还有一个保护叫做 PNA - Private Network Access ,防止处于相对更公共 ip 地址范围的网页访问更私有的网址,甚至连导航都不行
    只不过有一个小问题:它默认允许访问 127.0.0.1
    totoro625
        2
    totoro625  
       2024-09-30 08:29:43 +08:00   ❤️ 1
    设置密码,改默认端口即可,最好改到其他软件的常用端口上,如 22/3389 这类

    PS:自己的配置文件自定义了端口、密码、证书加密,然后 GUI 客户端帮我删除了这些设置。
    我只想要一个便捷的 TUN 功能。
    wjx0912
        3
    wjx0912  
       2024-09-30 08:38:51 +08:00
    有点慌。就直接说吧,clash 这个端口问题有木有办法解决?
    est
        4
    est  
       2024-09-30 08:40:24 +08:00   ❤️ 1
    其实给子域名绑一个 ip 指向 127.0.0.1 也不跨域。
    leokun
        5
    leokun  
       2024-09-30 08:46:33 +08:00
    clash 应该强制同源,仅可使用 「非浏览器客户端」打开控制端口,例如自带的 webview ,elctron 等
    Greendays
        6
    Greendays  
       2024-09-30 08:48:20 +08:00
    Clash For Windows 有一个随机混合端口功能,打开后应该能降低风险
    zeusho871
        7
    zeusho871  
       2024-09-30 08:50:24 +08:00 via Android
    @wjx0912 设置密码了 它只能知道你开了 clash
    lisxour
        8
    lisxour  
       2024-09-30 09:22:17 +08:00
    那这应该是 GUI 工具的问题
    ohellohell
        9
    ohellohell  
       2024-09-30 09:23:22 +08:00
    有没有办法避免
    @wjx0912 看起来没啥好办法啊,总归能检测到
    FengMubai
        10
    FengMubai  
       2024-09-30 09:26:06 +08:00
    @Greendays 改端口不解决问题, 大可以遍历全部端口
    vvhy
        11
    vvhy  
       2024-09-30 09:31:13 +08:00 via Android
    clash meta 本来就支持通过 external-ui 配置本地的 webui 资源,不知道为什么不默认禁止跨域
    不过我觉得加个密码就行了,端口响应 401 就认为是 clash 有点不靠谱
    Greendays
        12
    Greendays  
       2024-09-30 09:33:01 +08:00
    @FengMubai 这样会慢很多。不过我改了端口后也确实能用楼主的工具检测出来。加密码才是最保险的。
    murmur
        13
    murmur  
       2024-09-30 09:34:07 +08:00
    @leokun 只有浏览器才有这么多安全限制,自己写代码访问 http 可以构造一切头部,就包括 webview ,可以轻松关掉跨域限制,你要什么 ua ,头部,我给你构造就是了

    而且不仅是网站在扫代理,各种游戏也会扫代理,以前就爆出腾讯的国外某款射击游戏会扫描全盘,看你有没有装什么远控或者代理投屏软件
    mikewang
        14
    mikewang  
    OP
       2024-09-30 09:36:44 +08:00 via iPhone   ❤️ 1
    #4 @est

    1. 访问子域名(例如 example.com 访问 sub.example.com )是跨域的。
    2. 子域名可以修改 document.domain 属性为父域名,避开跨域检测( sub.example.com 可改为 example.com )。但是这种操作已被废弃,较新的浏览器都会禁止修改 document.domain 属性。

    因此设置域名指向 127.0.0.1 的方法应该不可行。
    ohellohell
        15
    ohellohell  
       2024-09-30 09:39:25 +08:00
    @ohellohell 看了下用 adblock 拦截 127.0.0.1:9090 就可以了...
    est
        16
    est  
       2024-09-30 09:45:14 +08:00
    @mikewang 漏洞攻击不都是针对当前版本嘛。
    sky96111
        17
    sky96111  
       2024-09-30 09:50:56 +08:00   ❤️ 1
    选择 1 ,用 FlClash 这类直接嵌入内核的软件,避免了外部通信,默认关闭外部面板访问。
    选择 2 ,用 sing-box 内核,1.10.0 版增加了 access_control_allow_origin
    cleanery
        18
    cleanery  
       2024-09-30 09:50:58 +08:00
    mihomo 没有像 clash 一样 allow *,建议直接用 mihomo ,别用 clash 了
    wjx0912
        19
    wjx0912  
       2024-09-30 09:58:30 +08:00   ❤️ 2
    clash verge 设置 api key 可以解决:
    ![]( )
    tabris17
        20
    tabris17  
       2024-09-30 10:02:18 +08:00
    用的是 FlClash ,无法检测出来。提示:
    Access to fetch at 'http://127.0.0.1:7890/' from origin 'https://mikewang000000.github.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
    mikewang
        21
    mikewang  
    OP
       2024-09-30 10:06:33 +08:00 via iPhone
    #18 @cleanery mihomo 是刚修好的,他们做了改进。
    文中提到的 clash 系列就包括 mihomo ,也就是 meta 。
    至于原版的 clash 核心,早删库了,应该不会复活了吧(
    yianing
        22
    yianing  
       2024-09-30 10:10:50 +08:00
    @wjx0912 #3 最新的 1.18.9 支持设置 external-controller-cors 了
    74123gzy
        23
    74123gzy  
       2024-09-30 10:20:44 +08:00
    页面上可以加个概率,还有检测项,告诉用户哪些行为看起来有点像 clash
    74123gzy
        24
    74123gzy  
       2024-09-30 10:22:09 +08:00
    类似 whoer 那样的,
    顺便我用的 switchyomega ,完全没检测出来
    Leung818
        25
    Leung818  
       2024-09-30 11:43:10 +08:00
    Mac clashX version:1.116 要咋处理呢?我在设置里把代理端口和 API 端口都改成高位的了,确实没有被检测出来,但同时我也没法在菜单栏看到订阅信息了
    zsh2517
        26
    zsh2517  
       2024-09-30 12:10:25 +08:00
    有个想法但是没有测试:假设电脑的网卡 IP 是 192.168.1.2 且保证局域网是安全的,那么直接设置绑定地址为 192.168.1.2:xxxx 然后使用 web 界面时通过 192.168.1.2 的 IP 替代 127.0.0.1 是否可行?
    cybort
        27
    cybort  
       2024-09-30 12:36:59 +08:00 via Android
    凡是 webui 能做的事情,其他网站也能操作
    twoz
        28
    twoz  
       2024-09-30 12:40:58 +08:00 via Android
    装在路由器里面可以检测吗
    Forestar
        29
    Forestar  
       2024-09-30 13:00:33 +08:00
    意思是纵使你设置密码,但也仅仅是不让配置信息暴露,通过特征分析还是知道你开了 clash 的?
    billlee
        30
    billlee  
       2024-09-30 13:14:35 +08:00
    uBlock Origin 的 Block Outsider Intrusion into LAN 规则也失效了,没拦下来
    0x73346b757234
        31
    0x73346b757234  
       2024-09-30 17:27:05 +08:00
    iyg429
        32
    iyg429  
       2024-09-30 19:45:57 +08:00
    我用的小火箭 检测不出来
    RH
        33
    RH  
       2024-09-30 22:36:26 +08:00
    @billlee 才知道还有这个,刚打开这个就检测不出来了
    RlyehHime
        34
    RlyehHime  
       2024-10-01 00:57:53 +08:00
    我用的 edge+clash nya ,没检测出来
    leoking6
        35
    leoking6  
       2024-10-01 16:11:23 +08:00   ❤️ 1
    联想到一个插件,Smart Referer 。arkenfox/user.js 项目推荐使用这个插件,可以有效限制跨域。strict 模式下,可将 sub-domain 也视为不同 domain ,适用于拦截类似 github.io 这种,子域名实际由不同主体持有的情况下的跨域。
    leoking6
        36
    leoking6  
       2024-10-01 16:41:25 +08:00
    @leoking6 update 一下,抱歉,这个是限制 referer 的。搞错了。
    Byleth
        37
    Byleth  
       2024-10-01 22:07:04 +08:00
    @0x73346b757234 这个东西一刀切拦截所有连接,会让 vite 热更新之类的功能失效,如果你在浏览器里调试的话
    fengjianxinghun
        38
    fengjianxinghun  
       2024-10-02 10:11:11 +08:00 via iPhone
    webui 这种好像无法真正避免?就算了加了同源也会暴露你在用了。
    abonan
        39
    abonan  
       2024-10-02 18:24:53 +08:00
    直接把回环地址改成其他的就行,只要前面是 127 开头
    xuing
        40
    xuing  
       2024-10-06 18:01:47 +08:00
    @abonan 治标不治本,最好的还是 17 楼提到的通过 access_control_allow_origin 限制允许跨域的网站或者直接避免外部通信。
    xuing
        41
    xuing  
       2024-10-06 18:04:57 +08:00
    @vvhy 你没有仔细看,楼主说的真的很详细了,是可以通过接口路径来判断的
    vvhy
        42
    vvhy  
       2024-10-06 20:44:40 +08:00
    @xuing #41 一分钟写个 clash

    ```
    package main

    import (
    "net/http"
    "github.com/gin-gonic/gin"
    )

    func main() {
    r := gin.Default()
    r.Use(func(c *gin.Context) {
    c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
    c.Next()
    })
    r.GET("/version", func(c *gin.Context) {
    c.JSON( http.StatusOK, gin.H{"version": "1.0.0"})
    })
    r.NoRoute(func(c *gin.Context) {
    c.JSON( http.StatusUnauthorized, gin.H{"message": "Unauthorized"})
    })
    r.Run(":8000")
    }
    ```
    vvhy
        43
    vvhy  
       2024-10-06 20:52:55 +08:00
    现在网页还正常吗,正经 clash 7890/9090 不加密码也显示检测不到
    mikewang
        44
    mikewang  
    OP
       2024-10-06 21:56:57 +08:00
    @vvhy 合入了 https://github.com/MikeWang000000/ClashScan/pull/1 ,增大了并发数。如果原来能检测,后来检测不到了,可能是并发过大,浏览器没扛住导致的漏检
    xuing
        45
    xuing  
       350 天前
    @vvhy #42 然后呢?检测会认为你这个是“clash”,但并不影响检测真正的“clash”,甚至还能知道具体的版本。能不能仔细看看文章。
    xxxbin
        46
    xxxbin  
       320 天前
    为啥还在考虑端口哦。。。
    打开后台前才启动 http 服务不就行么。
    超时就关掉。
    这东西没必要一直运行的
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   5803 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 56ms · UTC 06:06 · PVG 14:06 · LAX 23:06 · JFK 02:06
    ♥ Do have faith in what you're doing.