☰
DVWA靶场实战指南:从环境搭建到Web漏洞修复全解析
2026/10/22 16:44:26 网站建设 项目流程

聊到Web安全入门,DVWA基本是绕不开的第一站。它是部署在本地的“漏洞练习沙盒”,内置了SQL注入、XSS、CSRF、命令注入、文件上传等一整套经典漏洞场景,专门给学习者创造一个可以放心动手的实验环境。我接触过的很多搞安全的同行,包括带过的几个新人,最开始都是在DVWA上把每个漏洞点亲手打一遍、把修复代码读一遍,才真正建立起对Web漏洞的“体感”。这篇文章就把我在DVWA实战里的完整思路和踩坑记录梳理一遍,从环境部署到模块拆解,再到常见故障排查,尽量讲透为什么这么做、注意什么,给后来的学习者一条少走弯路的参考路径。

1. 靶场认知与环境准备

1.1 DVWA是什么,以及为什么拿它练手

DVWA全称Damn Vulnerable Web Application,翻译过来就是“设计得过于脆弱的Web应用”。它本质上是一个照着真实业务逻辑写出来的“负样本工程”——有用户登录、有CURD操作、有上传下载、有会话机制,但每一个功能点都刻意留下了经典漏洞。和真实系统唯一的关键区别是:它是给你打来练习用的,所有攻击演示在本地环境自生自灭,不会影响到任何真实用户和数据。

我自己的体会是,DVWA最大的价值在于它同时提供了“攻击视角”和“防御视角”。每个模块都有PHP源码可直接查看,你可以看到漏洞形成的代码原因,也能看到不同安全级别下防御代码的差异。很多新手在真实渗透测试里最容易犯的毛病就是“跑完扫描器不知道发生了什么”,在DVWA里这个问题基本不存在——打穿一个漏洞之后直接打开源代码,漏洞原理一目了然。这种“先打击后复盘”的循环,才是建立起漏洞直觉最快的路径。

适合拿DVWA练手的人群挺明确的:刚学Web安全的初学者、准备面试安全岗位的应届生、负责Web开发的程序员想理解攻击原理,都适合。它不要求你有多深的基础,PHP语法懂一点更好,不懂也不妨碍理解漏洞逻辑。

1.2 环境搭建与数据库初始化

部署DVWA有两条主流路线:集成环境手动部署,以及容器化一键启动。手动部署的典型组合是Apache/Nginx + PHP + MySQL。PHP版本建议5.6到7.4之间,太高版本可能因为函数废弃导致部分模块报错;MySQL/MariaDB选5.x或8.x都行。把DVWA源码解压放到网站根目录,重命名为dvwa,访问http://127.0.0.1/dvwa就能进入安装引导。

初始化时它会要求配置数据库,位置在config/config.inc.php。核心配置项是数据库地址、用户名、密码和库名:

$_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'root'; $_DVWA[ 'db_password' ] = '你的密码'; $_DVWA[ 'db_port' ] = '3306';

改完配置后点击页面上的“Create/Reset Database”按钮,它会自动建库建表并写入初始数据。这一步完成以后,默认账号admin/password就能登录了。这里有个常见坑:如果点完按钮页面一直卡在白色或提示无法连接数据库,九成是config.inc.php里的密码写错,或者MySQL服务没启动。另外,config/目录在线上部署时必须要限制访问,config.inc.php里有明文数据库口令,这个习惯要从靶场阶段就养成。

容器化部署相对省心,一条命令能把环境跑起来。DVWA官方维护了Docker镜像,用的组合一般是Apache+PHP+MariaDB一体。容器方式的好处是打坏了直接删掉重建,恢复成本几乎为零,适合高频练习。我个人建议学习期用集成环境手动部署一次,过程中会逼着你接触PHP运行环境、数据库连接、权限目录这些真实部署知识,这些坑在容器里都被隐藏掉了。

2. 模块功能与难度分级解读

2.1 四档难度到底差在哪

DVWA页面右上角可以切换安全级别:Low、Medium、High、Impossible。这四档不是简单调参数,而是直接对应四种截然不同的代码写法。

Low级别的代码是“裸奔”状态,SQL查询直接拼接用户输入,前端也没有任何过滤,连最基本的安全意识都没有。Medium级别增加了一些基础防御手段,比如用mysql_real_escape_string()转义用户输入,或者对上传文件限制Content-Type类型,听上去像个样子了,但实际都能被绕过——比如转义不等于参数化,枚举黑白名单总会漏。High级别就开始上“正经防御”了,比如SQL查询用预编译绑定参数、文件上传校验文件内容头部、CSRF校验Token,遇到这类防御,常规脚本打法已经很难奏效,需要结合业务逻辑找漏洞。Impossible级别基本上就是“修复完的基准答案”,不仅参数化查询,还会加二次确认、随机Token、服务端校验等。

我建议练法是从Low开始,先把每个漏洞的利用流程跑通,再对比Medium和High的防御代码,琢磨“这段代码为什么可以被绕过”,最后打开Impossible源码看看生产级修复长什么样。这一步对写自己代码的安全意识提升非常明显。

2.2 模块清单与真实风险对应

DVWA默认内置了十几个测试模块,每个模块都对应开发实践中真实出现过的风险点,这里先给个整体地图,后面再逐个拆实战:

模块对应漏洞类型真实世界的典型场景
SQL Injection注入攻击登录框、搜索框被拼SQL获取全库
XSS (Reflected/Stored/DOM)前端脚本注入论坛评论、搜索回显区被恶意脚本抢占
Command Injection命令注入Ping工具、调系统命令的业务接口被追加系统命令
CSRF跨站请求伪造诱导登录用户在不知情下改密码、转账
File Upload文件上传绕过头像上传功能被传WebShell
File Inclusion文件包含动态加载页面参数被篡改为本地/远程文件
Brute Force暴力破解登录接口无频率限制可被批量猜测
Insecure CAPTCHA验证码逻辑绕过验证码只校验第一步或返回参数可控
Weak Session ID会话可预测会话ID可被猜测导致身份伪造

每个模块都建议至少打两轮:第一轮不管防御代码,先体验利用过程;第二轮开源码看修复差异。两轮下来才算真正“消化”了这个漏洞点。

3. 核心模块实战拆解

3.1 SQL注入:从探测到脱库的完整链路

SQL注入是DVWA里最值得反复练的模块。Low级别的注入点在一个用户查询接口,输入用户ID就返回对应信息。手动探测经典路径如下。

先用单引号改变SQL语句结构。输入1',如果页面报错或返回异常,说明查询语句结构被破坏了,存在注入可能。接着判断字段数,用ORDER BY逐级试探:

1' ORDER BY 1 -- 1' ORDER BY 2 -- 1' ORDER BY 3 --

MySQL数据库下--后面要带一个空格表示注释掉剩余SQL,DVWA里如果注释没加空格经常会报语法错误,这是新手最容易卡住的位置之一。当ORDER BY N正常而N+1报错时,N就是查询的字段总数。这个模块固定是2个字段,所以到1' ORDER BY 3 --时报错。

字段数确定后,要用UNION SELECT找回显位置:

1' UNION SELECT 1,2 --

页面某处显示出数字1或2,那个位置就是数据可以回显的区域。接下来把数字换成数据库函数,就能直接拖数据了:

1' UNION SELECT database(),user() -- 1' UNION SELECT version(),@@datadir --

看到当前库名之后,查表名、字段名、字段内容就是下半场。表名查询:

1' UNION SELECT table_name,table_schema FROM information_schema.tables WHERE table_schema='dvwa' --

拿到表名如users,再查字段:

1' UNION SELECT column_name,column_type FROM information_schema.columns WHERE table_name='users' --

最后一步拖出用户和密码哈希:

1' UNION SELECT user,password FROM users --

Low级别的接口就这么几行全穿了。整个过程中最有价值的复盘点不是“我注入了”,而是观察代码。打开源码看底层拼接逻辑,你会看到类似$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"这样的语句,任何用户输入都会被直接嵌入SQL字符串——这才是漏洞根源。

Medium级别的源码会在查询前对输入加一层mysqli_real_escape_string()转义。这种转义能处理单引号、双引号等特殊字符,但对数字型注入几乎无效——因为参数不带单引号包裹时,注入载荷完全不需要引号。Medium界面提供下拉框选择ID,但Burp抓包改成1 UNION SELECT user,password FROM users照样能打,原因就是前端限制不是安全限制。

High级别实际防御已经比较到位了,把查询改成预编译的预处理语句:

$stmt = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = ?' ); $stmt->bind_param( 's', $id );

参数值被当作数据而不是SQL代码传给数据库引擎,注入载荷就没有执行空间了。看这段代码就能彻底明白:为什么预编译是SQL注入的标准解药,而不是“过滤敏感字符”。我在实操中还会补充一个习惯:每次做完注入练习,把回显的数据截图留档,把使用的Payload和原理写在测试笔记里,这种记录习惯对面试复盘非常有用。

3.2 XSS三兄弟:反射、存储、DOM的区别与利用

XSS模块在DVWA里被拆成三个独立练习,分别是反射型、存储型和DOM型。同样是执行前端恶意脚本,三者触发机制完全不同,别混为一谈。

反射型XSS的触发路径是“把输入原样反射到当前页面”。Low级别的反射型XSS直接在一个输入框提交后把内容嵌入页面,输入<script>alert(document.cookie)</script>刷新后弹窗。这类XSS之所以叫“反射型”,是因为恶意脚本不做持久化,而是藏在URL参数里诱导目标用户点击。要理解它的杀伤力,可以模拟一个钓鱼场景:构造一个看似正常的链接,把参数值换成恶意脚本,发给登录该应用的用户,脚本在目标浏览器上下文里执行,可以读取当前站点的Cookie。

存储型XSS更危险,脚本被持久化到数据库里,任何访问该页面的用户都会触发。DVWA里有个类似留言板的模块,我提交<script>alert(document.cookie)</script>后,刷新页面每次都会弹窗。因为服务端只是把留言内容存库,页面渲染时又原样输出了HTML标签。真实世界的论坛、评论区一旦存在这类漏洞,恶意代码可以长期驻留,影响所有浏览用户。存储型XSS练完后,建议顺手把恶意的<script>包在<img onerror>、<svg onload>等不同标签里都测一遍,因为很多过滤正则只盯着script关键字,却漏了其他可执行事件。

DOM型XSS的特别之处在于“服务端完全不知道发生了什么”。它的漏洞出现在前端JavaScript处理URL、hash或DOM属性时。DVWA的DOM型页面会读取URL中的参数,然后用document.write或innerHTML把它写进页面。我在地址栏构造?default=<script>alert(1)</script>,脚本在页面本地逻辑里被执行,网络请求里看不到任何恶意痕迹。这也是为什么DOM XSS更难被服务端WAF拦截。

难度升级方面,Medium会给反射型XSS加正则过滤<script>,但双写<scr<script>ipt>绕过成功率很高,因为过滤只处理了一遍;High的过滤正则开始针对所有HTML标签做黑白名单判断,普通脚本确实进不去了,但像<img src=x onerror=alert(1)>这种历史经典载荷仍然可能存活。存储型XSS在High级别过滤链路完善后会难打很多,但DOM型一直打到High都可能有突破口——因为服务端过滤管不到纯前端触发的注入。

防御段源码值得认真读,Impossible级别不仅用htmlspecialchars()把HTML字符实体化,还在输出前做了编码处理。简单说:用户输入是“数据”还是“代码”,取决定输时的转义处理,这个认知比背几个绕过Payload有用得多。

3.3 命令注入:一个分号打穿系统

命令注入模块模拟的是一个类似“Ping检测”的工具:输入一个IP,后端调用系统Ping命令并回显结果。我刚开始练这个模块时,第一反应是“这有什么可研究的”,直到我在输入框里填入127.0.0.1; whoami,发现页面直接回显了当前系统用户名——那一刻才意识到,系统命令一旦被拼进命令行有多致命。

Low级别的代码长这样:

$target = $_REQUEST[ 'ip' ]; $cmd = shell_exec( 'ping -c 3 ' . $target );

这里没有任何过滤处理,用户输入被直接追加到系统命令后面。分号;在Linux shell中表示“前面命令执行完了,继续执行后面的命令”,于是ping -c 3 127.0.0.1; whoami实际执行了两条命令。类似的连接符还有&&、||、管道|和反引号`,比如利用管道读取文件:

127.0.0.1 | cat /etc/passwd

Medium级别源码开始对分号和&&做黑名单过滤,但黑名单总有漏网之鱼,管道符没被处理,所以用管道符依旧可以执行后续命令。High级别防御升级到按空格、分号和关键系统命令字过滤,听着挺全,但Linux下可以用$IFS$9代替空格来绕过关键字限制,比如构造127.0.0.1$IFS$9;cat$IFS$9/etc/passwd。这些“绕过思路”的价值不在于背命令,而在于理解黑名单防御的本质缺陷:你永远不可能枚举完所有可能的输入变体。

Impossible级别的修复方案就转向了白名单思路,先把输入转换成长整型,然后校验IP地址格式:

$target = $_REQUEST[ 'ip' ]; $ip = ip2long( $target ); // 转为整数 if ( $ip !== false ) { $cmd = shell_exec( 'ping -c 3 ' . long2ip( $ip ) ); }

输入从源头上被限制在这个业务真正需要的格式里,不存在携载其他命令的空间。这种“白名单校验输入格式”的思路其实贯穿所有安全修复,也是我最推荐读者在源码里反复体会的一点。

3.4 CSRF:改密码的陷阱与防护

CSRF是很多新手觉得“没什么大不了”的漏洞,但它恰恰是所有依赖Cookie鉴权的Web应用最容易中招的问题。原理简单说:目标用户在目标站点处于登录状态,浏览器自动携带Cookie发起请求,攻击者只要诱导用户在另一个页面发出构造好的请求,目标站点就会误以为是用户本人在操作。

DVWA的CSRF模块是一个密码修改功能。Low级别下,修改密码的请求是GET方式,接口长这样:

GET /dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change

我用Burp抓到这条请求后,直接在本地搭一个恶意页面:

<img src="http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change">

只要已登录用户浏览器加载这个页面,浏览器就会带着目标站点的Cookie发起这次GET请求,密码就被静默修改了。整个过程用户毫无感知,除非他后来尝试用旧密码登录才发现异常。

Medium级别把GET改成了POST,但这并没有根本解决问题——攻击者可以构造隐藏表单加JavaScript自动提交,照样实现跨站请求。真正有效的是High和Impossible级别的防护:在表单里加入一个随机的不可预测的CSRF Token,服务端校验请求中携带的Token是否匹配当前会话才能通过修改。因为Token是每次会话动态生成的,攻击者无法提前预知,也就无法构造有效请求。

这个模块练完后我建议回头看一下你们自己的业务代码:凡是涉及修改密码、绑定手机、转账等敏感操作的接口,如果没有同步校验Token或同源头,就有同样的风险窗口。靶场里打一次构造恶意页面的过程,比读十篇CSRF科普文章都直观。

3.5 文件上传:前端校验是纸糊的墙

文件上传模块模拟的是头像上传功能,在真实业务里这几乎是最快被“打穿”的功能点之一。Low级别没有任何有效防御,上传一个shell.php,里面写入最基础的PHP探针代码,访问上传路径就能在靶场里看到代码被执行。整个流程零阻碍,但复盘时看点在于:为什么“上传脚本文件”是危险的?因为Web服务器把PHP文件交给PHP解释器执行,攻击者上传的代码就和站点自身代码拥有同等的执行权限。

Medium级别开始有校验了,但校验的是Content-Type和文件后缀黑名单。前端可绕过,Burp拦截上传请求后把Content-Type从text/php改成image/png即可。更值得研究的是High级别的黑名单拦截,它检查了.php、.php3、.phtml等一大批后缀,却有可能漏掉一些环境默认可执行的畸形后缀。而且黑名单永远面临“命名变体”的难题,这也是为什么生产环境推荐白名单+存储目录禁止执行脚本权限的方案。

XSS、内容检测、双扩展名和大小写绕过这些思路,在这个模块里都能用一用。比如上传一个.phtml或.php.jpg,或者用十六进制编辑器在图片尾部拼接PHP代码,构造所谓的“图片马”。用代码审计视角看Impossible级别源码,它做了三件事:校验MIME类型、校验文件头魔数、限制扩展名白名单。这套组合拳基本堵死了常见的脚本文件伪装手段。

安全提醒必须在这里多说一句:所有上传的“WebShell”只允许在本地DVWA靶场环境里运行,任何情况下都不要把这类文件传到真实服务器或测试他人系统,这种行为已经超出学习边界,可能涉及违法。学攻击是为了理解防御,不是为了实施破坏。

3.6 文件包含与暴力破解的补充分析

文件包含模块在DVWA里体现为动态加载文件。Low级别的漏洞点是页面直接拼接用户参数作为包含路径,传入../../../../etc/passwd这样的路径就能读取系统文件。这里建议同步练习PHP封装协议,用php://filter读取源码文件,例如:

php://filter/read=convert.base64-encode/resource=../index.php

拿到base64编码后解码,就能看到服务端源码。这个技巧在CTF里非常高频,也是理解“动态包含”危险性的钥匙。High级别开启了allow_url_include限制并增加文件路径前缀检查,难度上去了,但配套的代码审计思路也更接近生产环境的防御设计。

暴力破解模块本质是登录接口无频率限制。Low级别可以手动把Burp的拦截代理打开,用抓包工具重放登录请求,把密码字段做成字典跑批。这里顺便提一个新手常犯错误:直接在浏览器手工猜密码,练完根本看不出漏洞特征。正确做法是利用抓包工具设置一个Payload字典,观察不同密码在响应长度上的差异,锁定正确的密码。Medium/High级别开始引入登录失败锁定和Token机制,但有些版本存在逻辑漏洞,Token在失败后没有更新,又可以重复提交,这个“刷新Token”的细节也值得记进实验笔记。

4. 常见故障与排查实录

4.1 环境层面的高频问题

白屏和数据库连接失败是新手反馈最多的两个问题。页面能打开但点初始化按钮后空白,先检查PHP错误日志,比较典型的原因是mysqli扩展未启用,或者PHP版本高于7.4导致某些老函数废弃直接抛Fatal error。数据库连接失败优先看config.inc.php里的账号密码是否和服务端一致,其次是MySQL服务有没有起来,最后再排查端口被占用。

难度切不了也很常见。DVWA的难度选择写入config/config.inc.php里的$_DVWA['default_security_level']变量,改了之后需要清Cookie再刷新才会生效。如果页面反复跳到登录页,同样是Cookie问题。

4.2 攻击链路打不通怎么定位

SQL注入拿到单引号报错,但UNION SELECT一直不出数据,原因基本是字段数猜错了,或者注释符后面缺了空格。建议把ORDER BY探测和注释符空格固定成条件反射。XSS弹不出窗,先看浏览器是否拦截了alert()——现代浏览器对alert有频率限制,练习时可以换成console.log(document.cookie)或print()。存储型XSS刚提交不弹,先刷新一次页面再看,因为脚本是随页面后续访问才执行的。文件上传后访问报404,先确认上传目录的绝对路径和文件是否真的写入磁盘,有时候是文件名被重写了。

4.3 防御优先的思维习惯

最后说一个我测完所有模块后最深的体会:不要把DVWA当成“攻击工具箱”,它最值钱的部分是配对的修复代码。把每个漏洞在Impossible级别下的修复方案读懂,你的收获会远超能背多少Payload。而且务必在本地环境结束一切测试,不要顺手把这些Payload搬到别人的网站上验证,网络安全领域最忌讳的就是把学到的技术用错地方。我在后续练其他靶场时,一直保持着“先打穿,再读修复,再总结”的三步节奏,这套方法论确实帮我在真实业务代码里更敏锐地看出问题接口。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询