二维码劫持原理及恶意行为分析

*严正声明:本文仅用于教育和技术讨论目的,严禁用于非法用途。

*本文作者:VoltCary,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

之前看过其他的二维码登陆劫持漏洞,有的地方写的不是很详细,花了不少时间去研究二维码的原理,才弄懂漏洞。为了照顾更多入门新手,以本人的理解重新总结一遍,二维码登陆原理不是这里的主题,不过有必要熟悉一下流程。

1.打开登陆地方,选择扫描二维码登陆,此时加载二维码;
2.客户端开始轮询,即与服务器建立长连接,来检测二维码状态;
3.用户打开手机扫描二维码,此时二维码为“scan”状态,网页向服务器不断向授权服务器轮询授权码;
4.用户手机看到“确认登陆”按钮,点击此按钮向授权服务器申请授权,允许此二维码授权,二维码为“confirm”状态(如果超时失效,为“timeout”状态);
5.用户点击“确认登陆”后,网页轮询到授权码,此时带授权码申请凭证,成功登陆网站,进入个人中心。

问题解构

利用长轮询实现微信网页版扫码登录:

https://blog.csdn.net/x2145637/article/details/52795809 

这里有个疑问,二维码几十秒就会过期,怎么办?可以自己写个浏览器插件实时提取出请求里面返回的状态参数,二维码会有一定的过期时间,过期会有对应的状态,监控此状态即可,比如状态参数securityId和二维码字符barcode,并更新securityId状态到本地,若timeout则刷新二维码;攻击者从本地将barcode当字符串生成自己的二维码图片放在自己的网站上,js一直轮询本地的securityId状态

这里用微信二维码作为例子,查看二维码状态情况;

访问 https://wx2.qq.com/ ,页面如下:

访问此页面时,其实浏览器与服务器之间会建立一个长连接,用于监控二维码状态。

监控接口为:

https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=YZm-nRUSFQ==&tip=1&r=-104081975&_=1550587274887

201相当于“scan”状态,手机扫描成功时二维码的状态,手机上点击后监控状态返回如下:

200相当于“confirm”状态,即用户点击确认返回的状态。并且直接返回凭证window.redirect_uri,此时只要复制此凭证到其它浏览器访问,直接可登陆网页版微信。

二维码链接为:

https://login.weixin.qq.com/qrcode/Qe3ev-uOPg==

即二维码字符为Qe3ev-uOPg==,当二维码过期,只需更新此字符Qe3ev-uOPg==即可,更新二维码字符接口:

https://login.weixin.qq.com/jslogin

appid=wx782c26e4c19acffb&fun=new&lang=zh_CN

通过上述可实时更新二维码到自己网站,不用担心过期问题。

无“确认登陆”按钮

第一种漏洞,用户扫描二维码直接登陆,没有任何提示,大家都知道,一般扫描二维码会有“确认登陆”等提示,如果没有此提示,容易被攻击者钓鱼伪造,诱导用户扫描,比如某APP扫描二维码有红包领取,用户只要扫描则被劫持,导致账号被攻击者登陆。这里用乌云的案例演示。。

可以欺骗劫持进入来往用户的帐号: http://www.anquan.us/static/bugs/wooyun-2013-040673.html

来往登陆二维码扫描时无任何提示,伪装为加好友的二维码,用户以为是加好友的二维码,,其实是登陆的二维码,当用户扫描时,攻击者那边可直接登陆用户账号,目前这种漏洞不多了,扫码时基本上都会有登陆等提示

登陆票据盗取

这个漏洞刚开始看的时候,以为是CSRF,,因为大佬们把它归类为CSRF类型,但是我看的时候发现与传统的CSRF完全不一样,导致一直以CSRF的思维去研究,被误导了。其实就是票据盗取,构造凭证URL,从而劫持用户的账号。

登陆确认票据盗取,如果没有任何签名保护,攻击者可以直接点击获取的票据拼接链接进行登陆,其实这里应该是在轮询步骤出现漏洞,用户扫描二维码后,客户端不断轮询请求服务器,而此次只是验证某个令牌等参数来确认用户,只要获取此令牌参数值,则可以冒充用户。

取个乌云上的例子,比如 登陆确认请求如下:

http://szsupport.weixin.qq.com/cgi-bin/mmsupport-bin/qrcodelogin?username=*********&key=*************&clientversion=25030133&devicetype=android8&lan=zh_CN&uuid=AXBIICc4sUSDsFnefkNP&pass_ticket=DebNjGnP2dJnq1bMvHvgL%2BezqqE70Ry9iWB625%2FRT8RRnwCD3tlq3qxuxG5YPzhx

其中uuid为二维码字符,usename为微信号,uuid值怎么获取,文章前面部分已经讲过,usename改为要劫持用户的微信号,key值是未知的,这里只要知道key的值可通过此链接登陆目标微信号。

Referer获取key思路:URL跳转、引用站外图片处,把跳转的URL、图片改为攻击者自己网站的URL,只要受害用户点击 构造好的URL,会跳转到攻击者网站,此时在攻击者服务器请求包的Referer里,就会看到key值,当然如果跳转处URL有key才可以,否则就去寻找带有key传递的地方,进行构造,只要能构造出向攻击者服务器发送一个请求即可。

上述获取key值之后,拼接登陆请求的链接,在浏览器里访问如下:

点击“确认登陆”直接登陆受害用户微信账号。

二维码CSRF漏洞

为啥这里是CSRF?上面却只是票据盗取,因为上面的“登陆确认”是不需要用户触发,构造URL后由攻击者直接触发。这里的CSRF漏洞是因为攻击者无法代替用户直接触发“登陆确认“按钮,必须以用户自己的身份触发,类似平常的增加、删除的CSRF,此处只是针对“登陆确认”的CSRF,当然,这里还需要用户先扫描二维码,而不能让用户直接触发“登陆确认”的请求,如果点击二维码链接就相当于扫描二维码的话,可直接构造POC。

当用户扫描二维码,构造一个隐藏iframe表单CSRF POC,用户扫描二维码时,此POC自动提交“确认登陆”的请求,造成CSRF漏洞,这样说大家都能看懂吧。这里我网上找了个例子做演示。

挖洞经验 | Facebook的手机扫码登录漏洞 https://cloud.tencent.com/developer/article/1044256

解释一下这个案例,洞主把“确认登陆”的链接抓包出来,用浏览器打开此链接,点击“Allow Login”抓包,构造CSRF的POC,此为第一个POC,由于缺少用户扫码步骤,文章前面我提到过,用户要先扫描二维码,再触发“Allow Login”的CSRF,因此才有了第二个POC,增加了扫码的步骤。

*本文作者:VoltCary,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。