ARTICLE DETAIL

资讯详情

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

XSS攻击实战:从靶场到绕过黑名单的完整思维训练

XSS攻击实战:从靶场到绕过黑名单的完整思维训练 1. 项目概述从靶场到实战的XSS思维构建最近在带新人入门Web安全发现很多朋友对XSS跨站脚本攻击的理解还停留在“弹个窗”的层面。这其实挺危险的因为XSS的威力远不止于此它可以是窃取Cookie的“小偷”也可以是诱导用户操作的“傀儡师”。为了让大家能系统性地、由浅入深地理解XSS的各种攻击手法和绕过技巧我决定带大家一起来“闯关”一个经典的XSS靶场——BUUCTF平台上的XSS挑战第一关。这不仅仅是一次CTF解题更是一次完整的XSS攻击思维训练。我们将从最基础的反射型XSS入手逐步分析前端的过滤与防护机制并尝试用各种姿势去绕过它。整个过程我会把我踩过的坑、调试的思路以及最终构造Payload的思考过程毫无保留地分享出来。无论你是刚接触安全的新手还是想巩固XSS知识点的朋友相信这篇“闯关实录”都能给你带来实实在在的收获。2. 环境准备与目标分析2.1 靶场环境搭建与访问BUUCTF是一个在线的CTF学习与竞赛平台它集成了大量高质量的题目环境。我们这次要挑战的“XSS闯关1”通常是一个Web题目这意味着我们不需要在本地搭建复杂的PHP或Java环境直接通过浏览器访问BUUCTF提供的题目链接即可。这种在线靶场非常方便省去了配置环境的麻烦能让我们把全部精力集中在漏洞分析与利用上。访问题目后我们首先看到的应该是一个简单的Web页面。作为经验丰富的“黑客”我们的第一反应不是盲目测试而是“侦察”。按下F12打开开发者工具这是我们的“数字显微镜”。我们要快速浏览几个关键部分页面源代码Elements查看HTML结构寻找可能存在用户输入和输出的地方比如input,textarea, 或者像是div欢迎用户XXX/div这样的动态内容区域。网络请求Network刷新页面观察浏览器发送了哪些请求接收了哪些响应。特别关注GET或POST请求中的参数以及服务器返回的HTML、JSON数据。有时候漏洞点可能隐藏在某个异步请求Ajax的响应里。控制台Console看看有没有现成的JavaScript错误或日志信息偶尔能提供一些线索。2.2 核心目标与攻击模型定义在动手之前必须明确我们的攻击目标是什么。对于XSS闯关类题目最终目标通常是“弹窗”即执行alert(1)或类似的JavaScript代码以证明我们能够注入并执行任意脚本。但“弹窗”只是证明漏洞存在的标志其背后的攻击模型才是我们需要理解的精髓。对于这第一关我们假设它是最常见的反射型XSS。其攻击模型可以这样描述攻击者我们即安全测试人员。攻击向量一个包含恶意脚本的URL参数。受害者访问该特定URL的用户通常是我们自己在测试环境下。漏洞点服务器端在生成HTML页面时未对用户输入URL参数进行充分的过滤或转义便直接将其嵌入到返回给浏览器的页面内容中。攻击流程我们构造一个特殊的URL - 诱使或自己访问用户点击该URL - 用户的浏览器访问服务器 - 服务器将我们输入的恶意参数拼接到页面中返回 - 用户的浏览器将这部分内容解析为HTML和JavaScript并执行 - 我们的恶意脚本如alert(document.cookie)在用户浏览器上下文中运行。理解这个模型后我们的任务就清晰了找到那个服务器会原样“反射”回页面的输入点然后想办法让我们的脚本逃过可能的过滤最终被浏览器成功解析执行。3. 初探与基础注入尝试3.1 寻找用户输入接口进入靶场页面一个标准的做法是寻找所有可能的交互点。最常见的就是搜索框、留言板、用户信息填写等表单。我们尝试在页面上唯一的输入框假设是一个搜索框里输入一些测试字符串比如test123然后点击提交。提交后页面刷新我们立刻观察URL的变化。如果URL变成了类似http://target.com/page?keywordtest123的样子那么keyword这个GET参数就是我们的重点怀疑对象。同时我们要紧盯页面内容看看我们输入的test123这个词出现在了页面的什么地方。是在标题里还是在搜索结果展示区域或者是在一段提示文字中比如“您搜索的关键词是test123”注意除了GET参数也不要忽略POST表单。虽然URL上看不到但通过开发者工具的“网络”面板我们可以查看提交表单时发送的请求体Request Body里面可能包含username、comment这样的参数。对于反射型XSSGET参数因其直接体现在URL中更容易被用于构造攻击链接。3.2 测试过滤与转义机制找到输入输出点后先别急着上复杂的Payload。我们应该进行一轮基础的探测以摸清服务器端或前端可能存在的防御措施。我通常会按顺序输入以下测试字符串并观察它们被输出到页面时的状态test这是一个最简单的HTML标签测试。提交后查看页面源代码CtrlU或F12看Elements搜索test。如果它在源码里显示为test说明尖括号被原样输出了这是一个积极的信号。如果显示为lt;testgt;说明发生了HTML实体转义尖括号被转换成了安全的字符实体这是最基本的防护。“test输入一个双引号。观察它是否被转义为quot;或者是否破坏了页面的HTML结构比如导致某个属性提前闭合。这有助于判断我们能否跳出现有的HTML属性。‘test输入一个单引号目的同上。test输入反引号在某些JavaScript上下文中可能有用。test输入一个常见的HTML事件属性比如onmouseover。如果这个字符串被原样输出那可能意味着简单的关键字过滤还没触发。在BUUCTF XSS1的典型环境中我们输入test后很可能在页面源代码中发现它被输出为test。这意味着尖括号没有被转义这是XSS漏洞存在的第一个强烈征兆。服务器信任了我们的输入并将其作为HTML的一部分交给了浏览器。3.3 构造首个攻击Payload既然尖括号可用我们就可以尝试构造一个最简单的XSS Payload。最直接的想法是插入一个script标签。我尝试在输入框提交scriptalert(1)/script满怀期待地按下回车后……页面可能毫无反应或者弹窗一闪而过又被过滤了我们需要查看页面源代码来确认到底发生了什么。在源码中搜索script我们可能会看到令人沮丧的一幕我们输入的Payload变成了类似scriptalert(1)/script的样子。是的script这个标签关键词被服务器或前端WAFWeb应用防火墙干掉了它被从输入中删除或破坏了。这是一个非常常见的初级防御。管理员心想“我把所有script标签都删掉不就行了吗” 这为我们上了第一课XSS的绕过很大程度上是一场关于“字符串匹配与欺骗”的游戏。4. 绕过策略深度解析与实战4.1 利用HTML事件属性绕过标签过滤当script标签被直接过滤时我们的思路需要拓宽。XSS并非只有script这一条路。HTML元素有很多“事件属性”Event Attributes当特定事件如点击、鼠标移动、加载发生时这些属性里的JavaScript代码就会被执行。我们尝试一个经典的Payloadimg src1 onerroralert(1)构造思路解析img src1我们插入了一个图片标签。src1是一个故意错误的URL这会导致图片加载失败。onerroralert(1)这是关键。onerror是img标签的一个事件属性当图片加载失败时浏览器就会执行这里的JavaScript代码。整体逻辑浏览器解析到img标签尝试加载src1这个不存在的资源必然失败随即触发onerror事件执行alert(1)。提交这个Payload如果成功我们会立即看到一个弹窗。但事情往往没那么简单。在靶场中我们可能会发现onerror这个关键词也被过滤了。查看源码可能看到img src1 oerroralert(1)中间的n字母神秘消失了。这说明防御系统可能采用了一个简单的“黑名单”机制查找并删除诸如onerror、onclick、onload等敏感字符串。4.2 对抗黑名单混淆与变形技术面对黑名单过滤我们需要对Payload进行混淆让它在字符串匹配时“看起来不像”黑名单里的词但浏览器解析时又能“变回”原来的功能。这里有几个实用的技巧技巧一大小写混淆有些过滤逻辑是大小写敏感的。我们可以尝试Img SrC1 OnErRoralert(1)。HTML本身不区分大小写OnErRor对浏览器来说和onerror是一样的但可能绕过简单的str_replace(onerror, , $input)。技巧二插入无关字符或标签利用HTML和JavaScript的解析特性插入一些会被浏览器忽略的字符。换行和Tab在某些上下文中on\nerror或on\terror可能被过滤引擎视为两个词但浏览器在解析HTML属性时会忽略这些空白符换行、制表符在某些属性值内可能不行最好在事件名和等号之间测试。更可靠的是在标签名内插入。无效属性尝试img/src1/onerroralert(1)。这里用/分隔有些旧的解析器可能会困惑但现代浏览器通常能正确解析。更常见的是使用空格的各种变体如img src1 onerror alert(1)多个空格。利用HTML实体在事件名中插入HTML实体浏览器在解析时会先解码。例如img src1 onerroralert(1)。服务器端看到的是onerror但浏览器解析HTML时会将#x6e;解码为字母n从而得到完整的onerror。这是绕过基于字符串匹配过滤的强力手段。技巧三尝试其他标签和事件不要只盯着img和onerror。XSS的向量非常丰富svg标签svg onloadalert(1)。SVG本质是XML但其内嵌的HTML允许事件处理。body事件如果我们能控制页面开头部分可以尝试body onloadalert(1)。input标签input typetext onfocusalert(1) autofocus。autofocus属性让输入框自动获得焦点从而触发onfocus事件。注意需要用户交互或自动聚焦。details标签details open ontogglealert(1)。open属性使其默认展开触发ontoggle。在BUUCTF XSS1的实战中经过多次尝试我发现使用img src1 onerroralert(1)的某种变形是可行的。关键在于理解过滤规则。如果它只是简单删除onerror这个词我们可以用oscriptptnerror这样的方式让过滤引擎在删除script后剩下的字符恰好能组合成onerror。但更常见的有效Payload是img src1 oonerroralert(1)。为什么这个可能成功假设过滤逻辑是查找“onerror”并将其替换为空字符串。那么对于输入oonerror过滤过程是找到中间的“onerror”并删除剩下的部分就是oalert(1)。最终浏览器得到的HTML是img src1 oalert(1)这显然不行。所以更可能的过滤是“删除一次”。如果过滤代码写得不够严谨比如preg_replace(‘/onerror/i’, ”, $input)并且只执行一次那么对于oonerror它找到的是从第二个字母开始的onerror并将其删除结果就变成了o。这也不对。实际上一个经典的绕过是img src1 onerroralert(1)注意onerror里有两个n。如果过滤是删除字符串“onerror”那么处理onerror后就变成了onerror删除中间的“onerror”后前后两个n合并正好是onerror这就是利用过滤逻辑自身创造有效字符串的巧妙方法。我在多个靶场和真实世界的简单WAF中都见过这种绕过生效。4.3 高级绕过利用JavaScript协议与字符编码如果事件属性也被防得死死的我们还可以考虑其他注入点。比如常见的输出点还在HTML标签的属性值里例如链接的href或者图片的src。假设页面有这样的代码a href”用户输入”点击这里/a。如果我们能控制href的内容我们可以尝试注入javascript:alert(1)。这样用户点击链接时就会执行JavaScript代码。但“javascript:”这个协议头很可能也被过滤了。我们可以尝试大小写Javascript:alert(1)插入空白java script:alert(1)浏览器通常会忽略协议名中的空格利用HTML实体编码javasc#x72;ipt:alert(1)。#x72;是字母r的十六进制HTML实体。浏览器在解析href属性值时会先进行HTML解码。利用URL编码有时服务器会对输入进行URL解码。我们可以输入%6a%61%76%61%73%63%72%69%70%74%3a%61%6c%65%72%74%28%31%29这是javascript:alert(1)的URL编码。如果服务器解码后直接放入href就可能成功。此外对于整个Payload我们可以考虑多重编码。例如先将alert(1)进行Unicode转义变成\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029然后再将其放入事件处理程序中。这可以用来对抗一些在JavaScript上下文中进行的过滤。5. 实战通关与深度复盘5.1 最终有效Payload构造与验证在BUUCTF XSS1的具体环境中经过一系列测试包括测试script、onerror、javascript:等关键词的过滤情况我最终找到了突破口。综合网络上的常见Writeup和我个人的测试经验本题一个稳定有效的Payload是img srcx onerroralert(1)或者其细微变种。关键在于本题的过滤可能非常简单仅仅过滤了script标签但对其他HTML标签和事件属性没有设防。因此使用img标签的onerror事件是一个直接有效的方案。验证步骤在靶场输入框输入上述Payload。提交后观察页面是否立即弹出显示“1”的警告框。弹出则攻击成功漏洞存在。同时按下F12打开开发者工具切换到“控制台”Console标签页。如果弹窗成功这里通常不会有错误。如果Payload因语法问题未执行控制台会显示JavaScript错误信息这是调试Payload的宝贵依据。5.2 漏洞根源与防御方案探讨成功弹窗后我们不仅要庆祝通关更要深入思考这个漏洞到底是怎么产生的如何修复漏洞根源分析输出位置不当用户输入的数据被直接放置在HTML文档体中例如在一个div或p标签内部而不是作为纯文本处理。缺乏有效的输出编码服务器端在将用户输入嵌入到HTML响应中之前没有根据其所在的上下文进行正确的编码。对于HTML正文上下文特殊字符,,,”,’应该被转换为对应的HTML实体lt;,gt;,amp;,quot;,#x27;。依赖不完善的黑名单题目可能只过滤了script标签这是一种脆弱的安全措施。攻击向量是无穷的黑名单永远无法穷尽。正确的防御方案原则输入验证 输出编码。输入验证在服务器端对用户输入进行严格的格式、长度、类型检查例如邮箱格式、数字范围。但这主要用于保证业务逻辑正确不能完全依赖它来防XSS。输出编码这是防御XSS的黄金法则。在将任何不可信数据输出到页面时必须根据其出现的上下文进行编码。HTML上下文使用HTML实体编码。PHP可以用htmlspecialchars($string, ENT_QUOTES, ‘UTF-8’)。ENT_QUOTES参数很重要它会同时编码单双引号。HTML属性上下文同样使用HTML实体编码要特别注意属性值是否被引号包围。始终用引号单或双包围属性值。JavaScript上下文将数据放入script标签内或事件属性中时需要进行JavaScript Unicode转义。URL上下文在href或src属性中如果值以用户输入开头要确保协议白名单只允许http://,https://并对输入进行URL编码。使用现代前端框架如React, Vue, Angular等它们默认提供了基于模板的自动转义机制能很大程度上避免XSS但开发者仍需警惕使用v-html或dangerouslySetInnerHTML等危险API。设置安全HTTP头Content-Security-Policy (CSP)这是一个强大的缓解措施。通过CSP策略可以告诉浏览器只允许加载来自特定来源的脚本、图片、样式等。例如设置script-src ‘self’就可以阻止内联脚本包括我们用的onerroralert(1)的执行从根本上扼杀这类XSS。在CTF中如果题目设置了严格的CSP我们的攻击难度会急剧上升。5.3 从闯关中学到的核心思维通过这一关我们巩固了几个至关重要的安全思维模式上下文意识看到用户输入出现在页面时立刻问自己它出现在哪里是HTML标签之间、属性值里、JavaScript字符串中还是CSS样式里不同的上下文需要不同的绕过技术和编码方式。黑名单的不可靠性永远不要指望用黑名单挡住所有攻击。思维要灵活尝试大小写、插入字符、编码、利用冷门标签和事件。浏览器是最终裁判过滤逻辑可能在服务器端、可能在WAF、也可能在前端JavaScript。我们的Payload最终是要在浏览器里执行的。因此一定要在浏览器的开发者工具中查看“处理后的”源代码以及控制台的错误信息。有时候服务器返回的和你看到的不一样可能被前端JS二次处理。循序渐进测试从最简单的test开始逐步增加复杂度。先探明过滤规则是删除、转义还是替换再针对性地构造Payload。盲目尝试复杂Payload效率很低。通关BUUCTF XSS1只是起点。它揭示了最基础的反射型XSS漏洞模型。后续的关卡可能会引入更复杂的场景存储型XSS将恶意脚本存入数据库影响所有查看页面的用户、DOM型XSS漏洞纯在前端JavaScript逻辑中不经过服务器、严格的CSP策略、复杂的过滤和净化库如DOMPurify等。每一关都是对攻击者创造力的一次考验也是对开发者安全意识的一次警示。希望这篇详细的闯关笔记能为你打开XSS世界的大门在安全的道路上走得更稳、更远。
返回列表