通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

  • A+
所属分类:安全漏洞
通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

0x00 前言

这是一篇关于postMessage漏洞分析的文章,主要通过hackerone平台披露的Bug Bounty报告,学习和分析postMessage漏洞如何在真实的场景中得到利用的。

 

0x01 什么是PostMessage

根据Mozilla开发文档描述:

The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.

也就是说,window.postMessage()方法可以安全地实现Window对象之间的跨域通信。例如,一个页面和它所产生的弹出窗口之间,或者一个页面和嵌入其中的iframe之间进行。

这里,我们看一个例子:

假设我们有一个主网站1.html与另一个网站2.html进行通信。在第二个网站中,有一个后退按钮,当第一个网站的导航改变时,这个按钮就会改变。例如,在网站1中,我们导航到 changed.html,那么网站2中的后退按钮就会指向 changed.html。为此,使用postMessage方法将网站1的值发送到网站2。

1.html中的代码如下:

<!DOCTYPE html><html><head>    <title>Website 1</title> <meta charset="utf-8" /><script>
var child;function openChild() {child = window.open('2.html', 'popup', 'height=300px, width=500px');
}function sendMessage(){ let msg={url : "changed.html"}; // In production, DO NOT use '*', use toe target domain child.postMessage(msg,'*')// child is the targetWindow child.focus();}</script></head><body> <form> <fieldset> <input type='button' id='btnopen' value='Open child' onclick='openChild();' /> <input type='button' id='btnSendMsg' value='Send Message' onclick='sendMessage();' /> </fieldset> </form></body></html>

网站1中有两个按钮:

  1. 第一个是通过openChild()函数打开一个包含2.html的弹出窗口。

  2. 第二个是通过sendMessage()函数发送消息。要做到这一点,需要设置一个消息,定义msg变量,然后调用postMessage(msg,'*')

2.html中的代码如下:

<!DOCTYPE html><html><head>    <title>Website 2</title>    <meta charset="utf-8" />    <script>    // Allow window to listen for a postMessage    window.addEventListener("message", (event)=>{        // Normally you would check event.origin        // To verify the targetOrigin matches        // this window's domain         document.getElementById("redirection").href=`${event.data.url}`;        // event.data contains the message sent
});function closeMe() { try {window.close(); } catch (e) { console.log(e) } try {self.close(); } catch (e) { console.log(e) }}</script></head><body> <form> <h1>Recipient of postMessage</h1> <fieldset> <a type='text' id='redirection' href=''>Go back</a> <input type='button' id='btnCloseMe' value='Close me' onclick='closeMe();' /> </fieldset>
</form></body></html>

网站2中有一个链接和一个按钮:

  1. 链接处理重定向,href字段根据window.addEventListener("message", (event)接收到的数据而变化。接收到消息后,从event.data中读取事件中的数据并将url并传递给href

  2. 按钮调用函数closeMe()关闭窗口。

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

 

0x02 一个基础漏洞的简例

XSS漏洞的实现

PostMessages如果执行不当,可能导致信息泄露或跨站脚本漏洞(XSS)。
在这种情况下,
2.html在没有验证源的情况下就准备接收一个消息,因此我们可以将网页3.html作为iframe加载2.html,并调用postMessage()函数来操作href值。

<!DOCTYPE html><html><head>    <title>XSS PoC</title> <meta charset="utf-8" />

</head><body>
<iframe id="frame" src="2.html" ></iframe>
<script>
let msg={url : "javascript:prompt(1)"}; var iFrame = document.getElementById("frame") iFrame.contentWindow.postMessage(msg, '*');</script></body></html>

在这里例子中,恶意的msg变量包含数据{url: "javascript:prompt(1)"};,该数据将被发送到2.html2.html处理后,将<a href中的值更改为msg.url的值。iframe用于在网站中加载攻击。当用户单击返回链接时,将实现一个XSS。

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

缓解措施

根据Mozilla文档中的说法。

如果不希望收到来自其他网站的消息,不要为任何消息添加事件监听器的。这可以完全避免此类安全问题。

如果希望从其他站点接收消息,需要对源和可能的源验证发送方的身份,因为任何窗口都可以向任何其他窗口发送消息,并且不能保证未知的发送者不会发送恶意消息。但是,在验证了身份之后仍然应该验证接收到的消息的语法,否则,也可能出现跨站点脚本攻击。

当使用postMessage向其他窗口发送数据时,一定要指定一个准确的目标,而不是*。恶意网站可以在不知情的情况下改变窗口的位置,安全的设置可以拦截使用postMessage发送的数据。

根据文档的方案,应当将1.html中的:

child.postMessage(msg,'*')

修改为:

child.postMessage(msg,'2.html')

2.html中的:

window.addEventListener("message", (event)=>{    ...}

修改为:

window.addEventListener("message", (event)=>{    if (event.origin !== "http://safe.com")    return;    ...}


漏洞检测

检测postMessage漏洞的方法是读取JavaScript代码。因为当定义了一个监听器后,需要按照事件数据流来分析代码是否以容易被攻击的函数结束。这里推荐两种方法来检测函数调用:

  • 1 . J2EEScan,从git仓库(https://github.com/ilmila/J2EEScan)可以获得更新版本,而不是从 Burp AppStore。
  • 2 . BurpBounty (https://github.com/wagiro/BurpBounty),定义一组用于搜索关键字的被动响应字符串,如 postMessage 、addEventListener("message 、 .on("message"。


0x03 hackerone 漏洞报告分析

如果你在hackerone平台搜索PostMessage漏洞报告关键字,将看到一些报告,有一些漏洞被发现的时间距离现在并不遥远,并且获得了丰厚的奖励。这里重点分析3篇Hackerone披露的报告,并提供一些利用/绕过postMessage漏洞的技巧。

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

DOM Based XSS in www.hackerone.com via PostMessage and Bypass (#398054)

在hackeronep披露的 #398054报告中,通过Marketo中的不安全消息事件侦听器,Dom XSS在Hackerone中被成功利用。代码流程如下图所示:

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

通过分析报告可以看出,如果响应的设置没有错误,它就会创建一个名为u的变量,并将其设置为findCorrectFollowUpUrl方法的返回值。这将对一个名为followUpUrl的响应对象的属性进行处理,该属性是在表单提交完成后重定向的URL。

但是HackerOne窗体并没有用到这个,攻击者通过将其设置为绝对URL,就可以控制u变量的值。后来这个变量被用来改变窗口的location.href。当向Hackerone窗口发送下图所示的mktoResponse消息时,窗口被重定向到JavaScript URL,并执行代码alert(document.domain)

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

这部分代码由三部分组成:

1、mktoResponsePostMessage的第一个JSON元素,以调用函数:
else if (d.mktoResponse){ onResponse(d.mktoResponse)}
2、为了能执行这个函数,需要一个JSON结构数据,其元素有forerrordata。如果errorfalse,则repuest.success执行:
var requestId = mktoResponse["for"]; var request = inflight[requestId]; if(request){ if(mktoResponse.error){ request.error(mktoResponse.data); }else{ request.success(mktoResponse.data);
3、在这个函数中,followUpUrl值将关联到u,并传递给location.href。因此,有效payloadjavascript:alert(document.domain)触发XSS执行:
var u = findCorrectFollowUpUrl(data); location.href = u;

这个漏洞提交之后,Hackerone团队修改了OnMessage函数,添加了一个对源的验证:

if (a.originalEvent && a.originalEvent.data && 0 === i.indexOf(a.originalEvent.origin)) {    var b;    try {        b = j.parseJSON(a.originalEvent.data)    } catch (c) {        return    }    b.mktoReady ? f() : b.mktoResponse && e(b.mktoResponse)}

Bypass #398054 (#499030)

@honoki在报告#499030找到了上述#398054漏洞修复后的绕过办法。

在上述的修复代码中,变量i解析为https://app-sj17.marketo.com/indexOf检查字符串中是否包含源。因此注册一个marcarian域名.ma,验证将被绕过:

("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")

如果之前的漏洞攻击代码托管在注册域名https://app-sj17.ma下,XSS依旧会被成功执行。

CVE-2020-8127: XSS by calling arbitrary method via postMessage in reveal.js (#691977)

在报告#691977中,@s_p_q_r提交了一个通过PostMessage成功利用的DOM XSS。代码流程如下图所示:

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

首先,使用addKeyBinding方法调用setupPostMessage来定义带有恶意负载的JSON元素。然后,调用函数showHelp()在浏览器中展示出registeredKeyBindings[binding].description中定义的malicios有效payload。要利用此漏洞,使用以下代码:

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

这个代码片段中有三个部分:

1、将第一个JSON元素作为"method":"addKeyBinding",用于调用方法并应用到args:
if( data.method && typeof Reveal[data.method] === 'function' ) { Reveal[data.method].apply( Reveal, data.args );
2、为了到达函数addKeyBinding与参数args,构造一个JSON对象,包含callbackkeydescription
function addKeyBinding( binding, callback ) {
if( typeof binding === 'object' && binding.keyCode ) { registeredKeyBindings[binding.keyCode] = { callback: callback, key: binding.key, description: binding.description }; }
3、调用toggleHelp()函数,在没有验证的情况下展现了包含payload的JSON数据,触发JavaScript执行:
function showHelp() {
...
for( var binding in registeredKeyBindings ) { if( registeredKeyBindings[binding].key && registeredKeyBindings[binding].description ) { html += '<tr><td>' + registeredKeyBindings[binding].key + '</td><td>' + registeredKeyBindings[binding].description + '</td></tr>'; } }
...
}
 

0x04 绕过PostMessage漏洞的技巧

1 . 如果indexOf()被用来检查PostMessage的源,如果源包含在字符串中,有可能被绕过,如Bypass #398054 (#499030)中分析的那样。

2 . 如果使用search()来验证源,也有可能是不安全的。根据String.prototype.search()的文档,该方法接收一个常规的压缩对象而不是字符串,如果传递了正则表达式以外的任何东西,也将被隐式转换为正则表达的内容。例如:

"https://www.safedomain.com".search(t.origin)

在正则表达式中,点(.)被视为通配符。换句话说,源的任何字符都可以用一个点来代替。攻击者可以利用这一特点,使用一个特殊的域而不是官方的域来绕过验证,比如www.s.afedomain.com就可以绕过上述语法的验证。

3 . 如果使用了escapeHtml函数,该函数不会创建一个新的已转义的对象,而是重写现有对象的属性。这意味着,如果我们能够创建具有不响应hasOwnProperty的受控属性的对象,则该对象将不会被转义。例如,File对象非常适合这种场景的利用,因为它有只读的name属性,使用这个属性,可以绕过escapeHtml函数:

// Expected to fail:result = u({  message: "'"<b>\"});result.message // "'&quot;&lt;b&gt;"// Bypassed:result = u(new Error("'"<b>\"));result.message; // "'"<b>"

 

0x05 hackerone上PostMessage漏洞报告推荐

Hackerone report #168116
(Twitter: Insufficient validation on Digits bridge)

Hackerone report #231053
(Shopify: XSS on any Shopify shop via abuse of the HTML5 structured clone algorithm in postMessage listener on “/:id/digital_wallets/dialog”)

Hackerone report #381356
(HackerOne: Client-Side Race Condition using Marketo, allows sending user to data-protocol in Safari when form without onSuccess is submitted on www.hackerone.com)

Hackerone report #207042
(HackerOne: Stealing contact form data on www.hackerone.com using Marketo Forms XSS with postMessage frame-jumping and jQuery-JSONP)

Hackerone report #603764
(Upserve: DOM Based XSS via postMessage at https://inventory.upserve.com/login/)

Hackerone report #217745
(Shopify: XSS in $shop$.myshopify.com/admin/ via “Button Objects” in malicious app)


参考文献

https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

https://medium.com/javascript-in-plain-english/javascript-and-window-postmessage-a60c8f6adea9

https://hackerone.com/hacktivity?querystring=postmessage

(请点击“阅读原文”查看链接)
通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过


- End -

精彩推荐

使用无括号的XSS绕过CSP策略研究

攻防演练中防守方的骚姿势

ZeroLogon的利用以及分析

RTL-SDR接收NOAA气象卫星

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过


戳“阅读原文”查看更多内容

本文始发于微信公众号(安全客):通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: