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

33次阅读
没有评论

xss-labs Level16 代码审计报告

目标文件: level16.php | 漏洞类型: 反射型 XSS —— 黑名单过滤的换行符绕过(空白字符替换不全) | CWE: CWE-79 / CWE-80 | 危害等级: 中危
风险路径: GET /level16.php?keyword=<payload> → <center> 直接回显


一、漏洞代码定位

level16.php 第 17-22 行:

$str = strtolower($_GET["keyword"]);                // L17 先压平
$str2 = str_replace("script", "&nbsp;", $str);      // L18 script→&nbsp;
$str3 = str_replace(" ", "&nbsp;", $str2);          // L19 空格→&nbsp;
$str4 = str_replace("/", "&nbsp;", $str3);          // L20 斜杠→&nbsp;
$str5 = str_replace("\t", "&nbsp;", $str4);         // L21 制表符→&nbsp;
echo "<center>".$str5."</center>";                  // L22 危险:过滤后直接回显,无编码

漏洞点: L22。输出未做任何 HTML 编码;过滤链只封堵了 script、空格、/、tab 四种字符,遗漏了换行 \n(LF)与回车 \r(CR)。


二、漏洞原理

  • 过滤思路是"把属性分隔符(空格/tab)替换掉,让你没法分隔属性"。
  • 但 HTML 允许用空白(空格、Tab、换行、回车)分隔属性,攻击者用换行 \n(URL 编码 %0a)或回车 \r(%0d)代替空格即可:
    • <img src=x onerror=alert(1)> 中的空格 → 换行,变为 <img\nsrc=x\nonerror=alert(1)>。
    • script 过滤不影响 onerror;/ 过滤不影响本 payload(无斜杠)。
  • 已实测: 空格会被替换为 &nbsp;,但换行 \n 原样保留,<img\nsrc=x\nonerror=alert(1)> 通过过滤,浏览器按属性分隔正常解析并执行。

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

Payload(URL 解码后,%0a 为换行):

?keyword=<img%0asrc=x%0aonerror=alert(1)>

等价变体: <svg%0aonload=alert(1)>、<body%0aonload=alert(1)>。

Yakit 可重放原始请求:

GET /level16.php?keyword=%3Cimg%0asrc%3Dx%0aonerror%3Dalert(1)%3E HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: text/html
Connection: close

触发方式: 页面回显该标签,<img> 加载失败触发 onerror=alert(1) → 命中通关逻辑跳转 level17。


四、修复建议

// 根除:输出编码,而不是“替换几个空白字符”
$str = $_GET["keyword"] ?? "";
echo "<center>".htmlspecialchars($str, ENT_QUOTES, 'UTF-8')."</center>";

要点:

  1. 再次强调: 黑名单式替换永远不完整(空格、tab、LF、CR、 、多字节空白等均可当分隔符),正确做法是输出编码。
  2. 若确有富文本需求,应走白名单解析器/模板(如 HTMLPurifier),而不是正则替换。
  3. 加 CSP script-src 兜底。

五、备注

  • 已实测: 空格 → &nbsp;、tab → &nbsp;(被替换),但换行 \n 与回车 \r 未被过滤,可作为属性分隔符。
  • 通用绕过清单(空格被过滤时): %09(tab)、%0a(LF)、%0d(CR)、%0c(FF)、%0b(VT)、/、\ 等,需逐一判断过滤链是否覆盖。
正文完
 0