1. 项目概述:为什么存储型XSS是“潜伏的毒药”
在Web安全测试的实战演练中,DVWA(Damn Vulnerable Web Application)靶场几乎是每个安全从业者或爱好者的必经之路。它像一个精心设计的“漏洞博物馆”,将各种常见的安全问题集中呈现,供我们安全地学习和复现。今天我们要聚焦的,是其中一种极具威胁且容易被忽视的漏洞——存储型跨站脚本攻击。
存储型XSS,与反射型XSS最大的区别在于其“持久性”。反射型XSS的恶意脚本通常“一闪而过”,需要诱骗用户点击一个精心构造的链接;而存储型XSS则像一颗埋藏在服务器数据库里的“定时炸弹”。攻击者将恶意脚本提交到服务器(如留言板、用户资料、文章评论等),脚本被永久存储在数据库中。之后,任何访问到该内容的普通用户,其浏览器都会自动加载并执行这段恶意脚本。这意味着,一次成功的攻击,可以持续、自动地影响所有后续访问者,危害范围呈指数级扩大。
本次实战,我们将利用DVWA靶场的存储型XSS漏洞,完成两个经典目标:一是最直观的弹窗证明,二是更具危害性的Cookie窃取。整个过程力求在10分钟内清晰呈现,不仅让你看到漏洞现象,更要理解其背后的原理、利用手法以及防御思路。这不仅是复现一个漏洞,更是理解一种攻击思维,为构建更安全的Web应用打下基础。
2. 环境准备与靶场设置
2.1 DVWA靶场快速部署
DVWA的部署方式多样,对于想快速上手的同学,最推荐的是使用预配置的虚拟机或Docker镜像。这里以在Kali Linux或Parrot OS这类渗透测试发行版上使用Docker部署为例,因为它最干净、隔离性最好,避免污染本地环境。
首先,确保你的系统已经安装了Docker和Docker Compose。然后,创建一个简单的docker-compose.yml文件:
version: '3' services: dvwa: image: vulnerables/web-dvwa ports: - "80:80" restart: unless-stopped保存文件后,在终端中进入该文件所在目录,执行命令docker-compose up -d。稍等片刻,Docker就会从仓库拉取镜像并启动容器。此时,在浏览器中访问http://你的服务器IP或localhost,就能看到DVWA的登录页面了。
注意:首次访问DVWA,页面可能会提示你需要运行一个安装脚本来配置数据库。点击“Create / Reset Database”按钮即可。完成后,使用默认账号
admin和密码password登录。
2.2 关键安全等级设置
登录DVWA后,左侧菜单栏有一个非常重要的设置项:“DVWA Security”。点击进入,你会看到一个安全等级下拉菜单,包含“Impossible”、“High”、“Medium”、“Low”四个级别。
- Impossible:几乎修复了所有漏洞,用于学习安全代码的写法。
- High:存在漏洞,但加入了较强的过滤和防护机制。
- Medium:存在漏洞,加入了一些基础的过滤(如大小写转换、字符串替换)。
- Low:漏洞完全暴露,没有任何防护,最适合初学者理解漏洞本质。
为了本次10分钟速通实战,我们必须将安全等级设置为“Low”。这样,我们注入的恶意脚本才能不被过滤地存储和执行,让我们专注于攻击逻辑本身。请务必在开始前确认此项设置。
2.3 浏览器与调试工具准备
工欲善其事,必先利其器。一个合适的浏览器和其开发者工具是我们的“手术刀”。
- 浏览器选择:推荐使用Google Chrome或Microsoft Edge(Chromium内核)。它们内置的开发者工具功能强大且直观。
- 打开开发者工具:在浏览器页面按
F12键即可打开。我们主要使用两个面板:- Console(控制台):用于查看JavaScript代码的输出、报错信息,也可以直接在这里执行JS代码进行测试。
- Network(网络):用于监控浏览器发送的请求和接收的响应。在后续的Cookie窃取环节,我们可以在这里看到我们的“攻击服务器”是否收到了数据。
- 关闭不必要的浏览器扩展:某些广告拦截器或安全扩展可能会干扰我们的XSS Payload(攻击载荷)执行,建议在测试时使用浏览器的“无痕模式”或暂时禁用这些扩展。
环境就绪,靶场待命,接下来让我们直击核心,开始漏洞的挖掘与利用。
3. 存储型XSS漏洞原理深度拆解
在动手之前,我们必须搞清楚,存储型XSS这颗“毒药”是如何被酿造并发挥作用的。理解原理,才能举一反三,而不是死记硬背几个Payload。
3.1 漏洞产生的根本原因
存储型XSS漏洞产生的根源,可以归结为一个简单的安全原则被破坏:“不可信数据未经验证和净化就直接输出到网页上下文中”。
我们来拆解一下DVWA(Low安全级别下)存储型XSS模块的典型数据流:
- 输入:用户在表单(比如“Name”和“Message”输入框)中提交数据。
- 传输:数据通过HTTP POST请求发送到服务器(例如
dvwa/vulnerabilities/xss_s/)。 - 存储:服务器端PHP脚本(如
xss_s.php)未对输入内容进行任何过滤,直接将其插入SQL语句,保存到数据库中。 - 输出:当其他用户访问该页面时,服务器从数据库取出这条数据,未做任何转义处理,直接将其作为HTML代码的一部分,拼接进返回给浏览器的网页中。
- 执行:浏览器接收到HTML,将其解析为DOM。由于数据被当作HTML代码的一部分,其中的JavaScript代码(如
<script>alert('XSS')</script>)就被浏览器当作合法的脚本指令执行了。
关键在于第3步和第4步。服务器既没有在存储前对输入进行“消毒”(Sanitization),比如过滤或转义特殊字符(<,>,&,",'等),也没有在输出时进行“转义”(Escaping),即将这些字符转换为它们的HTML实体(如<转成<,>转成>)。这使得用户输入能够“突破”数据区域的限制,升级为可以控制页面行为的代码。
3.2 与反射型、DOM型XSS的对比
为了更深刻理解存储型的特性,我们简单对比三种主要XSS类型:
| 类型 | 触发方式 | 数据存储位置 | 影响范围 | 典型场景 |
|---|---|---|---|---|
| 反射型XSS | 用户点击恶意链接 | 不存储,在URL或POST数据中 | 单个用户(点击者) | 搜索框错误提示、URL参数回显 |
| 存储型XSS | 访问存在恶意内容的页面 | 服务器数据库 | 所有访问该页面的用户 | 论坛留言、用户昵称、博客评论 |
| DOM型XSS | 用户与页面交互 | 不存储,在客户端处理过程中 | 单个用户 | 前端JS操作URL片段(hash)、本地存储数据 |
从对比中可以看出,存储型XSS的危害性是最大的。攻击者只需要成功提交一次恶意代码,就可以实现“一劳永逸”的攻击效果,后续所有访问者都会在不知情的情况下“中招”,非常适合用于挂马、蠕虫传播和持久性的信息窃取。
3.3 DVWA漏洞点定位分析
在DVWA中,切换到“XSS (Stored)”页面。Low级别的页面通常包含一个简单的留言板。查看页面源代码,我们可以找到类似下面的表单:
<form name="XSS" action="#" method="POST"> <input type="text" name="txtName" placeholder="Your name"> <textarea name="mtxMessage" placeholder="Your message"></textarea> <input type="submit" name="btnSign" value="Sign Guestbook"> </form>提交后,留言内容会显示在页面上方。通过浏览器开发者工具的“Elements”面板,查看显示留言的HTML结构,你会发现你的输入(比如名字和消息)被直接包裹在<div>或<td>标签内输出。例如:
<div id="guestbook_comments"> <p><strong>黑客</strong> 说:<br> 这是一条测试留言<script>alert(1)</script></p> </div>这里,我们输入的<script>alert(1)</script>被原封不动地输出,浏览器解析时遇到了<script>开始标签,就会将其后的内容作为JavaScript代码执行。这就是最经典的漏洞点——数据被直接注入到了HTML正文中。
4. 实战复现一:基础弹窗验证
弹窗是证明XSS漏洞存在最直观、最经典的方式。它虽然不造成直接危害,但却是漏洞利用的“敲门砖”。
4.1 构造并注入Payload
- 访问DVWA的“XSS (Stored)”页面(安全等级已设为Low)。
- 在“Name”输入框中,可以输入任意名字,比如
TestUser。 - 在“Message”输入框中,输入我们的第一个Payload:
为了更具辨识度,可以将<script>alert('XSS by YourName')</script>YourName替换为你自己的标识。 - 点击“Sign Guestbook”提交。
4.2 现象观察与原理验证
提交后,页面会刷新。如果一切正常,你会立即看到一个弹窗,内容正是“XSS by YourName”。
此时,不要急着关闭弹窗。我们来做几个关键验证:
查看页面源代码:在浏览器中右键点击页面空白处,选择“查看页面源代码”。搜索你刚才输入的名字“TestUser”,你会看到类似这样的代码:
<pre>Name: TestUser</pre> <pre>Message: <script>alert('XSS by YourName')</script></pre>看,我们的
<script>标签被完整地写入到了HTML源码中。浏览器在解析这份源码时,遇到了<script>标签,就会执行其中的JavaScript代码,于是触发了alert函数。验证持久性:关闭当前浏览器标签页,重新打开一个新的浏览器窗口,再次登录DVWA并访问“XSS (Stored)”页面。无需任何操作,弹窗会再次出现!这就是“存储型”的威力——恶意代码已经永久保存在服务器数据库里,每次页面加载都会从数据库读取并输出,导致弹窗反复执行。
实操心得:很多新手在这一步可能会失败,常见原因有两个。第一,DVWA的安全等级没有设置为“Low”。第二,某些浏览器的内置XSS过滤器(如Chrome的XSS Auditor,现已废弃,但部分机制仍存在)可能会拦截非常简单的Payload。如果遇到弹窗未出现,首先检查安全等级,其次可以尝试更复杂的Payload,如
<img src=x onerror=alert(1)>,或者暂时关闭浏览器的安全防护进行测试。
4.3 绕过基础过滤的Payload思维
在“Medium”安全等级下,DVWA会尝试进行一些过滤。例如,它可能会使用str_replace(“<script>”, “”, $input)来尝试删除<script>标签。这时,我们的基础Payload就会失效。
如何绕过?思路是构造一个不会被简单字符串匹配删除的Payload。
- 大小写混淆:
<ScRiPt>alert(1)</sCrIpT>。str_replace是大小写敏感的,它只找全小写的<script>。 - 嵌套标签:
<scr<script>ipt>alert(1)</script>。假设过滤逻辑是删除一次“