DVWA Medium DOM XSS安全测试与防护分析
注意:有关DOM XSS漏洞的基础知识与原理已在上篇文章指出,此处不再解释。
可参考该文章https://www.cnblogs.com/lky-sec/articles/21412117
本文仅用于网络安全学习与防御研究。所有测试均在本人本地部署、合法授权的 DVWA 靶场中进行,禁止将相关方法用于任何未授权系统。
一 测试范围与环境
本次测试目标为本地DVWA靶场中的XSS(DOM)模块。
| 项目 | 配置 |
|---|---|
| 测试环境 | 本地 DVWA 靶场 |
| 安全等级 | Medium |
| 测试系统 | Kali Linux |
| 测试工具 | Firefox、Burp Suite |
| 测试方式 | 黑盒测试、源码审计 |
| 授权情况 | 本人本地靶场,合法授权 |
二 页面与请求分析
1. 设置难度等级
进入 DVWA Security,将安全等级设置为 Medium,随后进入 XSS(DOM)模块。

2. 观察模块正常功能
页面提供语言选择下拉框,可以选择 English、French、Spanish、German 等语言。
选择不同语言后,可以观察到浏览器地址栏中的 default 参数发生变化。

3. 分析URL参数与DOM变化(也可使用浏览器开发者工具进行查看页面源码)
开启Burp Suite Proxy模块进行拦截,提交一次正常请求。

右键将正常请求发送到 Burp Suite Repeater。

发送请求,观察Response包页面源码。

发现程序首先通过:document.location.href获取当前页面URL,并查找其中的default=参数:
var lang = document.location.href.substring(
document.location.href.indexOf("default=")+8
);
随后通过:
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
将default参数对应的内容动态写入页面DOM。
因此,该模块的数据流可以表示为:
URL中的default参数
↓
document.location.href读取
↓
substring()提取参数内容
↓
document.write()写入页面DOM
这说明default参数属于用户可控数据,并会直接影响页面DOM结构
三 手工黑盒测试
1.测试Low难度Payload
输入<script>alert("x")</script> 无弹窗,且URL处返回English。

将该命令大小写混合,输入<ScRiPt>alert("x");</ScRiPt> 无弹窗,且URL处返回English。

结合两次输入即其对应返回结果,猜测此处对<script>标签进行过滤,且不区分大小写。
2.使用不依赖script标签的HTML事件属性
HTML 中部分元素支持事件属性,例如:
onclick
onload
onerror
这些属性会在对应事件发生时执行其中的 JavaScript,因此 JavaScript 并不一定必须写在<script>标签内部。
例如:输入<img src=x onerror=alert("x")>
其中:
<img>:HTML 图片标签。
src=x:指定一个通常无法正常加载的图片地址 x。
onerror:图片加载失败时触发的错误事件。
alert("x"):事件触发后执行的 JavaScript 代码。
其执行过程为:
<img>标签被浏览器解析
↓
浏览器尝试加载src=x
↓
图片加载失败
↓
触发onerror事件
↓
执行alert("x")
提交后未出现弹窗。结合前端 JavaScript 可以发现,default 参数最终通过 document.write() 被写入 <option> 元素内部,因此 <img> 标签仍处于<select>结构中,无法作为正常图片元素触发 onerror 事件。
因此需要先闭合原有的 option 和 select 标签,使后续 HTML 内容脱离下拉框结构。
3.脱离下拉框结构
输入</option></select><img src=x onerror=alert("x")> 或者</select><img src=x onerror=alert("x")> 弹窗成功


四 黑盒测试结果分析
| 测试Payload | 测试结果 | 结果分析 |
|---|---|---|
<script>alert("x")</script> |
未弹窗,URL返回English |
Low难度Payload失效,说明Medium等级增加了针对script标签的限制 |
<ScRiPt>alert("x");</ScRiPt> |
未弹窗,URL仍返回English |
改变大小写后仍被限制,初步判断过滤规则对script标签匹配不区分大小写 |
<img src=x onerror=alert("x")> |
未弹窗 | Payload未使用script标签,但由于内容仍位于select/option结构中,img标签无法作为正常图片元素执行 |
</option></select><img src=x onerror=alert("x")> |
成功弹窗 | 通过闭合原有DOM结构,使img标签脱离下拉框并正常触发onerror事件 |
</select><img src=x onerror=alert("x")> |
成功弹窗 | 浏览器能够自动结束当前option元素,仅闭合select同样可以脱离原有DOM结构并执行事件代码 |
五 代码审计
1. 查看Medium源码
点击view source查看源码
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];
# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}
?>
2. 源码分析
Medium 等级首先获取用户通过 URL 提交的 default 参数:$default = $_GET['default'];
随后使用:stripos($default, "<script")检测参数中是否包含 <script 字符串。
stripos() 进行的是不区分大小写的字符串查找,因此:
<script>
<ScRiPt>
<SCRIPT>
均能够被检测到。
当发现参数中存在<script时,程序执行:
header ("location: ?default=English");
exit;
将页面重新重定向至:?default=English
这与前面黑盒测试中普通 <script> 和大小写混合 <ScRiPt> 均被重定向至 English 的现象一致。
但该防护仅针对 <script 字符串进行检测,并没有限制其他 HTML 标签或事件属性。因此,不包含 <script> 的内容不会触发该过滤规则,例如:
<img src=x onerror=alert("x")>
结合前面已经分析的前端 document.write(),此类内容仍可能被写入页面 DOM 并被浏览器解析。
因此,该漏洞的主要原因是:Medium 等级仅通过 stripos() 对 script 标签进行黑名单检测,防护范围有限,未从根本上阻止用户可控数据进入 DOM 结构。
3. 修复方案
1)对 default 参数采用白名单校验,仅允许 English、French、Spanish、German 等合法值。
2)避免使用 document.write() 将用户可控数据直接写入 DOM。
3)使用 textContent、createElement() 等安全 DOM API 生成页面内容。
4)对需要输出到页面的动态数据进行上下文相关编码。
5)避免使用 stripos() 等黑名单方式过滤特定标签或关键字。
6)配置合理的 Content Security Policy(CSP),限制内联脚本和事件属性执行。
7)对异常 default 参数及可疑 XSS 请求进行日志记录与安全监控。
六 Low与Medium差异
| 对比项 | Low | Medium |
|---|---|---|
| 服务端防护 | 无 | 增加服务端检测逻辑 |
default 参数处理 |
不处理 | 获取 $_GET['default'] 并进行检查 |
| 过滤方式 | 无 | 使用 stripos() 检测 <script |
| 是否区分大小写 | 不涉及 | 不区分大小写 |
遇到 <script> 时的处理 |
不拦截 | 重定向到 ?default=English |
普通 <script>alert("x")</script> |
可利用 | 被拦截 |
大小写混合 <ScRiPt>...</ScRiPt> |
可利用 | 仍被拦截 |
非 script 型 Payload |
可利用 | 仍可能利用 |
| HTML 事件属性利用 | 可利用 | 仍可能通过 onerror 等方式绕过 |
| DOM 写入根因 | 用户输入可进入 DOM | 用户输入仍可进入 DOM |
| 主要防护措施 | 无任何防护 | 仅对 <script> 进行黑名单检测 |
| 主要安全问题 | 用户输入可直接影响 DOM 结构 | 黑名单检测范围有限,未从根本上阻止用户输入进入 DOM |
| 漏洞是否存在 | 存在 | 仍然存在 |
| 根本修复方式 | 避免将用户输入直接写入 DOM,并采用白名单校验 | 避免将用户输入直接写入 DOM,并采用白名单校验 |

浙公网安备 33010602011771号