公司动态
从XSS漏洞挖掘到CSP绕过:以test.ctf8为例的Web安全实战解析
1. 从一次真实的CTF解题说起当XSS遇上“test.ctf8”最近在复盘一些经典的Web安全挑战偶然翻到一个名为“test.ctf8”的XSS注入题目。这个题目本身并不复杂甚至可以说是一个典型的入门级XSS场景但恰恰是这种“典型”让我觉得有必要把它拿出来好好聊聊。很多刚接触CTFCapture The Flag夺旗赛或者Web安全的朋友一看到XSS跨站脚本攻击就觉得是弹个框、偷个Cookie思路容易固化。实际上即便是最简单的题目背后也藏着对浏览器解析逻辑、JavaScript执行环境以及输入输出过滤机制的深刻理解。今天我就以“test.ctf8”这个靶场为例完整拆解一遍我的解题思路和操作过程希望能帮你跳出“弹框即成功”的思维定式真正理解XSS漏洞的利用链是如何一环扣一环构建起来的。这个靶场的核心就是一处存在反射型XSS漏洞的搜索接口。你的目标很明确通过构造特殊的输入让前端页面执行你预设的JavaScript代码从而触发某种“动作”来获取Flag。这个“动作”可能是弹窗、可能是请求外部资源、也可能是读取页面上的特定信息。我们一步步来。2. 环境侦察与漏洞点定位一切从“输入”开始面对任何Web题目第一步永远是信息收集。对于XSS我们最关心的是我们的输入在哪里被输出以及输出时上下文环境是什么我首先访问了test.ctf8这个靶场地址。页面通常是一个简单的搜索框可能附带一些说明文字。我的第一个动作永远是尝试最基础的探测。2.1 基础探测确认漏洞存在我在搜索框里输入了一个最简单的测试载荷scriptalert(1)/script。点击搜索后页面刷新结果区域显示了搜索结果通常是“未找到”之类的提示但并没有弹窗。这并不代表没有漏洞反而可能意味着存在基础的过滤比如尖括号被转义或删除。接下来我尝试了纯文本和特殊字符来探测过滤规则输入test正常回显说明输入输出功能正常。输入“双引号和‘单引号观察它们是否被转义为HTML实体如quot;和#39;。如果被转义说明服务端可能做了HTML编码直接插入script标签会失败。输入test观察和是否被过滤。如果test被原样输出说明标签未被过滤如果变成了lt;testgt;或test消失则说明有过滤。在test.ctf8这个场景下我发现输入test后页面上显示的就是test这个文本尖括号没有被转义成实体。这是一个非常积极的信号意味着标签的闭合符号可能没有被严格处理。2.2 深入分析输出上下文关键在于“落脚点”XSS能否成功很大程度上取决于我们的输入被“塞”到了HTML文档的哪个位置。我们需要查看页面源代码CtrlU。在搜索test后查看源码我通常会搜索我的输入内容如“test”在HTML中的位置。假设我发现了这样的结构input typetext value你输入的内容或者div搜索结果你输入的内容/div又或者scriptvar keyword 你输入的内容;/script这三种情况分别对应着不同的注入上下文HTML标签属性内如value我们需要先闭合当前的属性值然后引入新的事件属性。例如如果输入点在value内部我们需要构造“ onmouseover”alert(1)最终形成value“” onmouseover“alert(1)”。HTML标签之间如div内部我们可以直接插入新的HTML标签如img srcx onerroralert(1)。JavaScript字符串内如script标签里我们需要先闭合字符串和语句然后注入新的JS代码。例如输入点在var a‘input’;我们需要构造‘;alert(1);//最终形成var a‘’;alert(1);//’;。在test.ctf8中通过查看源码我确认我的输入被直接输出在了一个div标签内部类似于div class“result”你的输入/div。这属于上述第二种情况HTML文本节点上下文。这意味着我可以尝试插入新的HTML标签来执行JavaScript。注意现代浏览器对直接写在HTML中的script标签内容内联脚本有严格的限制通过innerHTML动态插入的script标签默认不会执行。因此即使我们能插入scriptalert(1)/script它也未必会执行。我们需要使用带有事件处理器如onerror,onload,onmouseover的标签或者能触发脚本执行的属性如svgscript.../script/svg但同样受限。3. 载荷构造与绕过尝试思维不能停既然确认了是HTML上下文且尖括号似乎可用我开始系统性地尝试构造有效的XSS载荷。3.1 第一轮尝试经典事件处理器我首先尝试了最常用的IMG标签img srcx onerroralert(1)输入后页面没有弹窗。查看网络请求或控制台发现图片加载失败src“x”是个无效地址但onerror事件并没有触发。这很奇怪。我打开浏览器开发者工具F12的控制台Console看到了一个错误信息“Refused to execute inline event handler because it violates the following Content Security Policy directive...”CSP内容安全策略这是XSS挑战中一个非常常见的防御机制。服务器通过HTTP响应头Content-Security-Policy来告诉浏览器哪些来源的资源可以被加载和执行。常见的指令如script-src ‘self’表示只允许执行同源本站的脚本unsafe-inline则禁止执行内联的事件处理器和script标签。我们需要查看响应头。在开发者工具的“网络”Network选项卡中找到我们搜索请求的响应查看Response Headers。果然发现了类似Content-Security-Policy: script-src ‘self’;的头部。这意味着我们注入的onerroralert(1)这种内联事件处理器是被禁止的。3.2 第二轮尝试绕开CSP限制CSP的存在并不意味着XSS完全无解它只是提高了门槛。我们需要在CSP规则允许的范围内找到执行代码的方法。仔细分析CSP头script-src ‘self’允许加载和执行与当前页面同源的JavaScript文件。没有unsafe-inline禁止内联脚本和事件处理器。通常也没有unsafe-eval禁止eval()等动态代码执行。那么思路就变成了能否将我们的恶意脚本变成一个同源的可执行JS文件然后让页面加载它这通常有两种方式利用现有的同源JS文件如果网站本身有一个JS文件比如/static/js/app.js并且我们可以控制其部分内容例如通过参数污染、缓存投毒或上传点我们就可以尝试修改它。但在简单的CTF题中这种可能性较小。引入一个会被当作JS解析的同源资源有没有可能我们输入的内容最终被服务器存储或反射到一个路径下并且这个路径的响应Content-Type是application/javascript这样浏览器就会把它当作JS文件来执行。这时我想到了JSONP或AngularJS这类技术可能带来的变种。但在这个简单的搜索反射题里更常见的绕过方式是利用允许的标签加载资源。虽然script-src ‘self’限制了脚本源但img-src图片源或default-src默认源可能配置得更宽松。如果CSP允许从任何地方加载图片img-src *我们可以构造一个图片标签将其src指向一个我们控制的服务器从而将Cookie等信息带出但这道题的目标通常是弹窗或执行代码获取FLAG而非外带数据。然而题目要求是执行代码。另一个经典绕过是使用link标签配合href和onload但onload作为内联事件处理器同样被CSP禁止。换个角度CSP的script-src ‘self’是否真的滴水不漏我们能否创造一个“同源”的脚本如果服务器对搜索内容处理不当可能会产生一个存储型XSS的错觉。例如搜索内容被错误地记录在某个公开访问的日志页面而这个页面的响应类型是text/html但其中包含了我们的脚本。不过这需要二次触发不符合反射型XSS的直观解题路径。我重新审视页面。有没有可能我忽略了输出点的其他特性我再次查看搜索结果的页面源码不仅看div还看head部分。突然我注意到一个细节页面引入了一个同源的JS文件像是script src“/js/search.js”/script。灵感来了如果我能控制这个src的值呢虽然我不能直接修改script标签但有没有可能存在DOM型XSS即前端JS代码会读取URL中的参数如location.search然后通过document.write()或innerHTML等方式动态写入页面。如果这个写入过程没有经过正确的编码就可能造成注入。3.3 第三轮尝试挖掘DOM型XSS可能我检查页面中其他的script块。果然在页面底部发现了一段内联脚本script function displayResult() { var kw new URLSearchParams(window.location.search).get(‘keyword‘); if (kw) { document.getElementById(‘result‘).innerHTML “您搜索的关键词是” kw; } } window.onload displayResult; /script破案了这才是真正的漏洞点。之前的服务端回显那个div可能做了基础的HTML实体转义所以直接注入标签失败。但这段前端JavaScript代码直接从URL的keyword参数中获取值然后未经任何过滤就直接用innerHTML赋值给了id“result”的元素。这是一个典型的DOM型XSS。CSP的script-src ‘self’限制的是脚本的来源但它不限制通过innerHTML属性设置的HTML内容中的内联事件处理器因为innerHTML属性设置的脚本其执行是由浏览器在解析HTML时触发的这与CSP评估的“脚本来源”是两条线。CSP主要防止的是未经授权的脚本文件加载和内联脚本块的执行但对于通过innerHTML动态添加的、带有事件处理器如onload,onerror的HTML元素只要该元素最终被插入到DOM中其事件处理器在触发时就能执行代码。因此我们的绕过路径变得清晰利用DOM型XSS构造一个通过innerHTML插入后能自动触发事件的标签。4. 最终载荷构造与Flag获取现在目标明确让kw变量的值包含一个能通过innerHTML插入后立即执行JS的HTML片段。由于是innerHTML插入script标签仍然不会执行。我们需要一个能自动触发事件的标签。img的onerror需要加载失败但我们可以让src为空或无效来确保失败。svg标签内的script在某些情况下可以通过innerHTML执行但依赖浏览器版本和CSP细节不是最稳妥的。一个更可靠的、能自动触发的标签是iframe的onload但需要src指向一个页面不够直接。另一个经典向量是body onloadalert(1)但我们需要注入整个body标签吗不一定。这里我使用一个非常简洁有效的Payloadimg src1 onerroralert(1)当这段字符串被document.getElementById(‘result‘).innerHTML ...设置时浏览器会解析它创建一个img元素并尝试加载src“1”。这个资源显然不存在加载失败会立即触发onerror事件从而执行alert(1)。我在搜索框输入这个Payload或者直接在URL中构造http://test.ctf8/?keywordimg src1 onerroralert(1)访问这个URL页面加载后displayResult函数执行将Payload通过innerHTML插入图片加载失败成功弹窗但这只是证明漏洞存在。CTF题目的目标通常是获取一个名为flag的变量值或者读取页面中的特定信息。所以我们需要将alert(1)替换为窃取信息的代码。由于CSPscript-src ‘self’的存在我们无法直接使用fetch或XMLHttpRequest发送数据到外部服务器除非CSP的connect-src指令允许通常不会。但题目环境往往是封闭的Flag就在当前页面的某个地方比如一个隐藏的div或者前端的JavaScript变量里。假设Flag存储在前端的一个变量里比如window.flag “flag{this_is_flag}”;。我们可以修改Payload来读取它img src1 onerroralert(window.flag)如果Flag在页面的某个元素里比如div id“flag” style“display:none”flag{...}/div我们可以用img src1 onerroralert(document.getElementById(‘flag‘).innerText)在test.ctf8的具体场景中我最终使用的Payload是img src1 onerroralert(document.body.innerText)因为有时Flag会以注释的形式藏在HTML源码里或者直接写在页面某个角落。document.body.innerText可以获取页面所有文本内容便于快速查找。弹窗后在长长的文本中我顺利找到了格式为flag{...}的字符串。5. 复盘与延伸不止于解题这道“简单”的题目实际上串联了多个关键知识点信息收集与上下文判断不能一上来就盲打Payload必须通过基础测试纯文本、特殊符号判断过滤规则和输出位置。查看页面源码和开发者工具是必修课。理解CSP及其绕过CSP是现代浏览器缓解XSS的核心机制。遇到弹窗失败第一时间要检查Network响应头。理解script-src、unsafe-inline等指令的含义是绕过的基础。本例中CSP限制了脚本来源但未能阻止通过innerHTML插入的HTML元素事件处理器执行。区分反射型、存储型与DOM型XSS服务端反射回显在HTML中和前端DOM操作innerHTML、document.write是两种不同的注入场景过滤和绕过的策略也不同。本题就是典型的DOM型XSS漏洞发生在前端JS逻辑中。有效Payload的构造在HTML上下文中script标签通过innerHTML插入不执行是常识。需要熟练使用带有onerror、onload、onmouseover等事件处理器的标签如img、iframe、svg、body。确保事件能被触发如src指向不存在的资源以触发onerror。目标导向的利用证明漏洞弹窗只是第一步。最终目的是获取Flag。需要根据题目环境灵活调整Payload来读取页面中的特定数据全局变量、DOM元素内容、Cookie等。一个实用的技巧在复杂的CTF环境或真实渗透测试中如果CSP非常严格连innerHTML插入的事件处理器都通过更严格的CSP策略如使用nonce或hash限制了还可以考虑基于DOM的客户端原型污染或利用Trusted Types等更前沿的绕过技术但那属于更高阶的范畴。对于大多数入门到中级的题目掌握上述链条已经足够。通过“test.ctf8”这道题我们完成了一次完整的XSS漏洞挖掘、分析、绕过和利用的旅程。它提醒我们Web安全是一个立体战场前端、后端、浏览器策略任何一环的疏忽都可能被利用。下次遇到XSS挑战不妨按这个流程走一遍探环境、定上下文、查策略、构载荷、达目的。