驻马店市可以做网站的公司,襄阳万家灯火网站建设,马鞍山建设局网站,gta5网站正在建设中1. 从零开始#xff1a;XSS漏洞与xss-labs靶场初探 大家好#xff0c;我是老张#xff0c;在安全圈摸爬滚打十几年#xff0c;从最早的SQL注入玩到现在的各种前端漏洞#xff0c;XSS#xff08;跨站脚本攻击#xff09;绝对算得上是Web安全里的“老朋友”了。很多刚入门…1. 从零开始XSS漏洞与xss-labs靶场初探大家好我是老张在安全圈摸爬滚打十几年从最早的SQL注入玩到现在的各种前端漏洞XSS跨站脚本攻击绝对算得上是Web安全里的“老朋友”了。很多刚入门安全测试的朋友一听到XSS就觉得头大什么反射型、存储型、DOM型还有各种过滤绕过听起来就复杂。其实吧XSS的核心思想特别简单就是想办法让浏览器执行你输入的代码。你可以把它想象成你在一个网站的留言板里本来只能写文字但你通过一些“小技巧”让留言板不仅显示了你的文字还偷偷执行了你藏在里面的JavaScript代码。为了让大家能在一个安全、合法的环境里练习和掌握这些“小技巧”xss-labs这个靶场就诞生了。它就像是一个精心设计的闯关游戏从最简单的直接弹窗到后面各种奇奇怪怪的过滤和限制一共20关每一关都模拟了真实开发中可能出现的疏漏。我当年第一次玩的时候也是从第一关的“秒过”到后面几关的抓耳挠腮这个过程虽然痛苦但通关后的成就感和对XSS理解深度的提升是看多少理论文章都换不来的。这个靶场最大的好处是提供了源码你可以清楚地看到开发者到底在哪个环节“偷了懒”没做过滤或者过滤得不彻底这才给了我们可乘之机。接下来我就带着大家用我踩过坑、总结出来的经验一层层剥开xss-labs的关卡把那些看似复杂的绕过技巧用大白话讲明白。2. 基础热身前五关的“直球”与简单闭合刚开始的几关主要是让你熟悉最基本的XSS注入姿势和HTML结构的闭合概念。如果你连这几关都觉得费劲那可能得先补补HTML和JavaScript的基础了。2.1 第一关毫无防备的欢迎第一关的页面通常会有一个显眼的提示比如“欢迎用户XXX”。查看源码你会发现类似这样的代码$str $_GET[name]; echo h2欢迎用户.$str./h2;后端直接用$_GET拿到了URL里name参数的值然后原封不动地拼接进HTML里输出。这里没有任何过滤就像你家大门敞开着。所以我们的攻击载荷Payload简单到令人发指?namescriptalert(xss)/script直接在URL里输入这个页面就会弹出一个写着‘xss’的对话框。这一关的意义在于建立信心让你明白XSS的本质数据被当成代码执行了。用户输入的script标签没有被当作普通文本处理而是被浏览器识别为合法的脚本标签并执行了。2.2 第二关初遇输入框与闭合陷阱第二关通常是一个搜索页面你输入关键词它会显示“没有找到和XXX相关的结果”。关键点在于你输入的内容不仅显示在页面上方还会回填到搜索框的value属性里。查看源码你会看到类似input namekeyword value你输入的内容如果你直接输入scriptalert(1)/script会发现脚本没有执行。因为你的代码被放在了value属性的双引号里面成了字符串的一部分而不是独立的HTML标签。这时候就需要“闭合”掉原有的属性。我们的目标是构造出这样的结构input namekeyword valuescriptalert(1)/script这样第一个提前闭合了value属性和input标签让我们的script标签“逃”了出来成为页面的一部分。所以Payload是scriptalert(1)/script我刚开始学的时候对这个闭合概念琢磨了好久。你可以把它想象成造句原本的句子是“他的爱好是‘______’。” 我们在横线里填“打篮球”句子是通顺的。但如果我们填‘。我喜欢游泳。’句子就变成了“他的爱好是‘。我喜欢游泳。’”。虽然看起来怪但语法上第一个单引号结束了“爱好”的描述句号结束了句子“我喜欢游泳”成了新的句子。HTML的闭合也是这个道理。2.3 第三关单引号的战场与事件驱动到了第三关你会发现用第二关的Payload不行了。查看源码发现开发者学聪明了用htmlspecialchars($str)函数对输入进行了转义把、等符号变成了lt;、gt;这样它们就无法构成标签了。但是仔细看value的属性是用单引号包裹的value$str。而htmlspecialchars函数默认不转义单引号除非设置ENT_QUOTES参数。既然尖括号被废了我们就不能直接用script标签了。这时候要祭出另一大杀器HTML事件属性。比如onmouseover鼠标悬停、onclick点击、onfocus获得焦点等。这些属性可以直接写在HTML标签里值是一段JavaScript代码。我们的思路是闭合掉value的单引号然后插入一个事件属性。构造的Payload会让最终的HTML变成input namekeyword value onmouseoverjavascript:alert(1)这里闭合了前面的单引号然后我们添加了onmouseover事件。所以Payload是 onmouseoveralert(1)输入后把鼠标移到这个输入框上就会触发弹窗。这一关的关键在于观察属性值的引号类型并利用未转义的引号进行闭合。2.4 第四关双引号的回旋镖第四关和第三关类似但value属性换成了双引号包裹value$str。同样和被过滤了。有了第三关的经验我们很容易构造出针对双引号的Payload onmouseoveralert(1)这样形成的HTML是input namekeyword value onmouseoveralert(1)原理一模一样只是引号配对从单引号换成了双引号。很多新手会在这里卡住就是因为没注意看源码里用的是哪种引号。养成习惯每次先右键“查看页面源代码”这是安全测试的基本功。2.5 第五关标签属性过滤与伪协议第五关开始增加难度了。你会发现输入onmouseover会被破坏变成o_nmouseover说明后端对on这个事件关键字开头进行了过滤。script标签同样被过滤。这时候a标签的href属性搭配JavaScript伪协议就成了突破口。href属性通常用来放链接比如hrefhttp://example.com。但它也支持javascript:伪协议后面可以跟JS代码点击链接就会执行。我们的目标是构造一个这样的a标签a hrefjavascript:alert(1)点击我/a但是我们需要让这个a标签出现在页面上。通常还是利用输入框的闭合。先闭合掉前面的input标签然后插入我们的a标签。Payload可能像这样a hrefjavascript:alert(1)点我/a这样页面在渲染完原来的输入框后会接着渲染一个超链接点击这个链接就能触发XSS。这里有个小细节href属性的值可以不用引号包裹虽然不规范但浏览器能识别这有时能绕过一些针对引号的过滤。这一关教会我们当事件和脚本标签被禁时要想想HTML中还有哪些属性可以执行代码。3. 中级博弈编码、隐藏输入与HTTP头注入闯过前五关你已经掌握了XSS的基本套路闭合、事件、伪协议。接下来的关卡开发者加入了更多过滤规则我们需要更巧妙的绕过技巧。3.1 第六关大小写混淆绕过第六关你会发现href也被过滤了变成了hr_ef。但很多简单的过滤是大小写敏感的。它们可能只检测小写的href、script、on等关键字。浏览器的HTML解析器是不区分大小写的sCriPt和script效果一样。所以我们可以尝试大小写混合来绕过a HrEfjavascript:alert(1)测试/a或者sCriPtalert(1)/sCriPt如果这关的过滤只是简单的字符串匹配那么大小写变换就能轻松绕过。这是非常经典且基础的一种绕过方式在实战中遇到简单的WAFWeb应用防火墙规则时也可能生效。3.2 第七关双写绕过如果大小写也被禁了呢第七关可能就会把script直接替换成空字符串。你输入script输出就没了。对付这种“删除”型过滤常用的一招叫双写绕过。它的原理是过滤函数执行一次把中间的script删掉但剩下的字符正好又能组合成一个新的script。 假设过滤函数是str_replace(script, , $input)。 我们输入scscriptriptalert(1)/scscriptript。 过滤过程函数找到中间的script并删除字符串变成了scriptalert(1)/script。 看完美的script标签又出现了Payload就是scscriptriptalert(1)/scscriptript同样如果过滤on你可以写成oonn过滤后剩下on。这一招的关键在于预判过滤逻辑并构造一个字符串使得在删除特定子串后剩余的字符能拼凑出我们想要的关键字。3.3 第八关HTML实体编码与解码第八关的场景可能是一个“添加友情链接”的功能你的输入会放在a href...里面。你会发现直接写javascript:alert(1)script可能被过滤或转义。这时候可以利用浏览器自动解码HTML实体的特性。我们可以把javascript这个单词中的某些字母转换成HTML实体形式。例如t的实体编码是#116;(十进制) 或#x74;(十六进制)。那么javascript可以写成javascrip#116;:alert(1)或者#x6a;#x61;#x76;#x61;#x73;#x63;#x72;#x69;#x70;#x74;:alert(1)当浏览器渲染时它会将这些实体解码回对应的字符从而得到完整的javascript:协议并执行。这种绕过方式非常隐蔽因为后端看到的是一堆乱码般的实体但前端浏览器却能正确解析。3.4 第九关协议验证与注释符第九关在第八关的基础上增加了一个验证要求输入的内容里必须包含http://否则认为不合法。这可能是开发者为了防止非HTTP链接而做的检查。我们的Payload需要包含http://但又不能让它真的影响我们的JavaScript执行。一个巧妙的办法是利用JavaScript注释符//。//在JavaScript中表示单行注释其后的内容会被忽略。我们可以这样构造javascrip#116;:alert(1)//http://或者更完整的实体编码版#x6a;#x61;#x76;#x61;#x73;#x63;#x72;#x69;#x70;#x74;:alert(1)//http://这样整个字符串既包含了http://满足了后端检查又因为//的存在使得http://被当作注释而不会被执行真正起作用的只有前面的javascript:alert(1)。3.5 第十关挖掘隐藏输入与属性覆盖第十关页面上可能没有可见的输入框。这时一定要查看HTML源码。你很可能发现类似这样的代码input namet_sort value typehiddentypehidden意味着这是一个隐藏的输入框在页面上不显示但其值仍然可以通过参数传递。我们的目标就是把这个隐藏框变成可见的并给它注入事件。通过URL参数?t_sortxxx可以给这个隐藏框赋值。思路是闭合value属性的引号然后覆盖type属性。Payload如下t_sort onclickalert(1) typetext提交后生成的HTML会变成input namet_sort value onclickalert(1) typetexttype从hidden被我们改成了text输入框就显示出来了并且带有点击事件onclick点击即可触发XSS。这一关教会我们前端看不到的东西不代表不存在所有通过参数传递到前端的数据都可能成为攻击面。4. 高级渗透HTTP请求头与框架漏洞利用从第十一关开始攻击面从直接的URL参数扩展到了HTTP请求头。这意味着你需要借助工具如Burp Suite、浏览器插件来修改发送给服务器的请求数据包。4.1 第十一关Referer头注入第十一关你可能发现普通的参数注入点都被htmlspecialchars转义了。但查看源码或许会发现一个值是从$_SERVER[HTTP_REFERER]中获取的。Referer是HTTP请求头的一部分表示当前请求是从哪个页面链接过来的。这个头信息通常由浏览器自动发送但我们可以用工具篡改它。使用Burp Suite拦截发送到第十一关的请求在HTTP请求头中找到Referer这一行将其值修改为我们的PayloadReferer: onclickalert(1) typetext原理和第十关一样通过修改Referer头我们让服务器将这个恶意字符串赋值给了某个隐藏的输入框的value进而通过属性覆盖实现XSS。这提醒我们所有用户可控的输入源都需要被谨慎处理包括看似自动生成的HTTP头部。4.2 第十二关User-Agent头注入第十二关的思路和第十一关如出一辙只是注入点换成了User-Agent请求头。User-Agent用来标识客户端浏览器、爬虫等的类型。同样用Burp Suite拦截请求修改User-Agent头的值User-Agent: onclickalert(1) typetext在实际渗透测试中User-Agent、X-Forwarded-For、Cookie等头部字段都是常见的测试点因为它们有时会被记录到日志或显示在管理后台而又缺乏严格的验证。4.3 第十三关Cookie注入第十三关将目标指向了Cookie。Cookie也是请求头的一部分用于维持会话状态。拦截请求后在Cookie字段中插入Payload。但需要注意的是Cookie的值通常有严格的格式直接插入引号或空格可能会破坏Cookie。一种常见的测试方法是在现有的Cookie值后面追加我们的Payload或者寻找那些可能被回显到页面上的Cookie参数虽然不常见。这一关的解法可能类似于前两关只是修改的头部字段不同核心思想是测试每一个用户可控的输入点。4.4 第十四关外部资源与iframe漏洞思路解析第十四关通常比较特殊它可能涉及通过iframe标签引入外部资源。页面源码中可能有一行iframe srchttp://某个外部域名/某个文件/iframe。这一关的漏洞点可能在于那个外部资源本身存在XSS漏洞或者该外部站点允许用户上传特定文件如图片并在渲染时执行了文件中的脚本。由于原靶场依赖的外部资源可能已失效这里主要理解其攻击模型信任边界问题。网站引入了第三方内容但没有对其进行充分的隔离或过滤导致第三方内容中的恶意代码在本地站点的上下文中执行。防御这种攻击需要严格设置iframe的sandbox属性或者对引入的内容进行同源检查。4.5 第十五关AngularJS ng-include指令利用第十五关引入了前端框架AngularJS1.x版本的一个特性ng-include。查看源码你可能会发现类似span classng-include:用户输入/spanng-include指令用于动态包含外部HTML片段。如果用户输入可控就可能造成XSS。但AngularJS 1.x默认会对ng-include的值进行部分编码/校验。一种常见的利用方式是让ng-include去包含本靶场中之前已经存在XSS漏洞的页面并传递恶意参数过去。例如Payload可能构造为level1.php?nameimg src1 onerroralert(1)这样ng-include会去加载level1.php并传入一个带有onerror事件的img标签参数。由于第一关没有过滤这个标签会被执行从而在当前页面第十五关的上下文中触发XSS。这属于一种间接攻击利用了应用程序其他部分的漏洞。5. 终极挑战空格过滤、Flash与综合防御思考最后几关的过滤往往更加变态和综合需要结合多种技巧甚至利用一些特定环境下的特性。5.1 第十六关空格过滤与回车符绕过第十六关的过滤可能非常严格比如将script、on、src等关键字直接替换为空甚至把空格也替换成HTML实体nbsp;。当空格被过滤后如何分隔HTML标签的属性就成了问题。这时候可以用换行符的URL编码%0a来代替空格。浏览器在解析HTML时换行符和空格、制表符一样通常都被视为空白符可以分隔属性。例如一个正常的img标签攻击载荷是img src1 onerroralert(1)当空格被过滤时可以写成img%0asrc1%0aonerroralert(1)%0a在HTTP请求中代表换行后端处理时可能不会将其等同于空格进行过滤但浏览器解析HTML时会将其识别为属性间的分隔符。此外也可以尝试用Tab符的编码%09。这一关考验的是对HTML解析规则和HTTP编码的深入理解。5.2 第十七关与第十八关embed标签与事件注入这两关通常围绕embed标签展开。embed标签用于嵌入外部应用或插件内容如Flash。源码中可能将用户输入拼接进embed标签的src属性如embed srcxsf01.swf?arg01aaaarg02bbb虽然参数值可能被htmlspecialchars转义导致无法闭合引号插入新标签但embed标签本身支持一些事件属性如onmouseover。关键在于这些事件属性可以直接加在标签上而不需要破坏原有的属性结构。Payload可能类似于arg01aaaarg02bbb onmouseoveralert(1) width100 height100最终生成的HTML可能是embed srcxsf01.swf?arg01aaaarg02bbb%20onmouseoveralert(1)%20width100%20height100注意这里arg02参数的值里包含了事件和额外的属性。因为src属性没有引号闭合或者我们的Payload巧妙地成为了其值的一部分浏览器可能会将onmouseover等解析为embed标签本身的属性。这需要根据具体的过滤逻辑进行测试核心思路是利用标签本身支持的事件。5.3 第十九关与第二十关Flash XSS原理与现状最后两关通常涉及Flash XSS这是历史上一个重要的攻击向量。Flash文件SWF可以接受外部传入的参数通过flashvars如果SWF文件内部使用getURL()、ExternalInterface.call()等函数时没有对传入的参数进行安全检查就可能导致JavaScript执行。例如Payload可能形如arg01versionarg02%3Ca%20href%22javascript:alert(1)%22%3Eclick%3C/a%3E这里arg02被URL编码了一个包含javascript:的a标签。如果SWF文件将这个参数直接传递给getURL()函数就可能打开这个JavaScript链接。然而需要特别强调的是Flash插件在2020年底已被各大主流浏览器彻底禁用并停止支持。因此Flash XSS在现代Web环境中已经基本成为历史。但学习它仍有意义一是理解“客户端组件安全”的重要性浏览器插件、PDF阅读器、Office在线预览等任何能解析内容的客户端组件都可能成为攻击面二是其利用思路参数注入到客户端执行具有普适性。对于现代前端类似的原理可能出现在对SVG、PDF、某些视频格式甚至CSS解析不当的场景中。6. 防御策略从靶场到实战的思考通关xss-labs的过程其实就是站在攻击者的角度学习他们如何寻找和利用漏洞。而作为开发者我们需要做的就是堵上这些路。防御XSS绝不是简单加一个过滤函数就能搞定它需要一套组合拳。第一坚持“数据与代码分离”原则。这是最根本的。任何时候都不要把用户输入的数据当成代码来执行。在Web前端这意味着要对输出到不同上下文的数据进行正确的编码。输出到HTML正文使用HTML实体编码。把转成lt;转成gt;转成amp;转成quot;转成#x27;。PHP的htmlspecialchars($str, ENT_QUOTES, UTF-8)就是个好例子ENT_QUOTES参数确保了单双引号都会被编码。输出到HTML属性同样使用HTML实体编码。尤其要注意属性值是否被引号包裹如果没有攻击者更容易注入新属性。输出到JavaScript代码或事件中这非常危险。绝不能简单拼接。应该使用JSON.stringify()将数据转换为JSON字符串或者确保数据被放在引号内并对特殊字符进行Unicode转义如\u003c代表。更好的做法是避免在JS中拼接HTML改用安全的DOM API如textContent或setAttribute。输出到URL进行URL编码encodeURIComponent。第二实施严格的内容安全策略CSP。CSP是一个强大的浏览器安全特性通过HTTP头告诉浏览器只允许加载和执行来自哪些源的脚本、样式、图片等。一个严格的CSP可以极大地缓解甚至完全阻止XSS攻击。例如禁止内联脚本unsafe-inline只允许加载特定域下的外部脚本。即使攻击者成功注入了scriptalert(1)/script如果CSP禁止内联脚本执行这段代码也不会运行。第三使用安全的框架和库。现代前端框架如React、Vue、Angular新版都提供了默认的XSS防护机制。例如React在渲染数据时默认会进行转义。但要注意使用dangerouslySetInnerHTMLReact或v-htmlVue等API时就等于跳过了这种保护必须对输入内容进行严格的净化Sanitize。可以使用像DOMPurify这样的专业库来处理富文本内容。第四对输入进行验证和过滤。虽然“输出编码”是黄金法则但“输入验证”作为第一道防线也很有必要。根据业务逻辑验证输入数据的类型、长度、格式如邮箱、电话。但切记验证不能替代输出编码因为数据可能在多个地方被使用验证规则也可能被绕过。第五警惕DOM型XSS。这种XSS的漏洞点在前端JavaScript代码中例如使用innerHTML、document.write()、eval()等不安全的API直接操作DOM且数据源来自location.hash、document.referrer、window.name等用户可控的地方。防御DOM型XSS除了避免使用不安全的API还要对来自这些源的数据进行严格的检查和编码。在我经历过的真实项目里最棘手的XSS漏洞往往不是那种明显的参数注入而是发生在复杂的富文本编辑器、动态生成的PDF报表、或者第三方组件库的封装层里。这时候光靠黑名单过滤关键字是没用的必须建立起白名单思维明确允许哪些标签、哪些属性其余的一律拒绝。同时定期进行代码审计和安全测试利用自动化工具扫描再辅以人工的渗透测试才能构筑起相对稳固的防线。xss-labs靶场就像一本生动的教科书它展示的每一种绕过技巧都在提醒我们防御的复杂性。通关不是终点理解每一种攻击背后的原理并思考如何在你的代码中避免它才是这个靶场带给我们的最大价值。