DVWA SQL注入手工实战:从Low到Impossible全级别详解

发布时间:2026/10/19 9:51:01
DVWA SQL注入手工实战:从Low到Impossible全级别详解 1. 为什么手工注入在工具泛滥的今天依然值得死磕DVWADamn Vulnerable Web Application是很多人入门安全测试的第一站而SQL Injection模块更是经典中的经典。我见过太多人打开sqlmap输入一个URL回车拿到数据然后觉得自己“会注入了”。但真到了实战环境WAF拦着、参数结构复杂、盲注场景下工具跑得比蜗牛还慢的时候那些只会敲工具的人就彻底懵了。手工注入的价值不在于炫技而在于它逼着你去理解数据库到底在干什么——你输入的每一个字符经过应用层、数据库解析层最终变成了什么样的查询语句这个链条你必须心里有数。DVWA的SQL Injection模块分Low、Medium、High、Impossible四个安全等级每个等级对输入的处理方式不同对应的注入手法也有差异。Low级别几乎不设防是理解注入原理的最佳起点Medium级别加了转义和数字型参数需要你调整策略High级别用了会话变量和LIMIT限制手工注入的难度陡增。我打算按这个难度递进的顺序把每个级别的手工注入过程拆开揉碎讲清楚包括我实际测试中遇到的意外情况和踩过的坑。这篇文章适合谁看如果你已经知道SQL注入的基本概念但手工注入时总是卡在某个环节——比如判断闭合方式、猜列数、找回显位——那这篇内容就是为你准备的。如果你完全零基础也没关系我会从最基础的判断注入点开始讲每一步都解释为什么这么做。工具能帮你省时间但手工能帮你长脑子两者不冲突但后者是前者的基础。2. Low级别从判断注入点到拖库的完整链路2.1 第一步不是急着加单引号而是先摸清参数行为很多人一上来就在输入框里敲一个单引号看到报错就兴奋地喊“有注入”。这个做法没错但不够系统。我的习惯是先输入一个正常值比如1观察页面返回了什么——通常是ID为1的用户信息包括First name和Surname。然后输入1如果页面报出SQL语法错误说明单引号被带入了查询且没有被转义。但这里有个细节DVWA Low级别的报错信息会直接显示在页面上包括完整的SQL查询语句这等于把数据库结构直接摊开给你看。我实测时输入1后页面返回了类似这样的错误You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 1 at line 1注意看错误信息里的1这说明原始查询大概是SELECT ... WHERE user_id 1这种形式参数被单引号包裹。这个信息极其关键它告诉你闭合方式是单引号而且参数是字符型。如果输入1正常、输入1报错、输入1 --正常那基本可以确认字符型注入且注释符可用。注意DVWA的Low级别默认使用MariaDBMySQL的分支注释符用--注意后面有个空格或者#。在URL中传递时#会被浏览器当作锚点截断所以实际测试中更常用--。2.2 用ORDER BY猜列数为什么是二分法而不是逐个试确认注入点后下一步是确定查询返回的列数。方法是用ORDER BY子句因为ORDER BY n在n超过实际列数时会报错。很多人从1开始逐个试1、2、3、4、5……这样效率太低。我的做法是先用一个较大的数比如10如果报错就减半试5再根据结果调整这就是二分法的思路。在DVWA Low级别中输入1 ORDER BY 1 --正常1 ORDER BY 2 --正常1 ORDER BY 3 --报错。这说明查询返回2列。为什么是2列因为原始查询大概是SELECT first_name, last_name FROM users WHERE user_id $id只查了两个字段。这个信息决定了后续UNION SELECT时必须要匹配2列。我见过有人在这一步卡住原因是他们用了ORDER BY 3报错后又去试ORDER BY 2发现正常但不确定到底是2还是3。这里有个技巧报错的那个值就是“超过列数”的最小值所以实际列数 报错值 - 1。如果ORDER BY 3报错而ORDER BY 2正常列数就是2。2.3 UNION SELECT找显位为什么有时候看不到回显确定列数后用UNION SELECT来合并查询目的是找到页面上哪些位置会显示查询结果。在DVWA Low级别中输入1 UNION SELECT 1,2 --页面会显示First name为1、Surname为2说明两个位置都有回显。但有时候你输入1 UNION SELECT 1,2 --后页面没有任何变化可能的原因有几个一是原查询的WHERE条件过滤掉了UNION的结果比如原查询返回了行UNION的结果被追加在后面但页面只显示第一行二是参数类型不匹配三是页面只显示特定字段。DVWA Low级别的情况比较简单因为原查询WHERE user_id 1会返回一行UNION SELECT会追加一行页面通常会显示两行结果。如果你只看到一行试试把1换成-1或一个不存在的ID让原查询返回空这样UNION的结果就会占据显示位。我实测时用-1 UNION SELECT 1,2 --页面直接显示了1和2非常清晰。2.4 从information_schema拖出库名、表名、列名找到显位后就可以开始拖库了。DVWA Low级别的数据库是MariaDB信息架构库是information_schema。第一步是获取当前数据库名-1 UNION SELECT 1,database() --页面Surname位置会显示dvwa。接下来获取表名-1 UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schemadvwa --这里用group_concat把多行结果拼成一行避免页面只显示第一行。我实测返回了guestbook,users两个表。然后获取users表的列名-1 UNION SELECT 1,group_concat(column_name) FROM information_schema.columns WHERE table_nameusers --返回了user_id,first_name,last_name,user,password,avatar等列。最后拖出用户名和密码-1 UNION SELECT user,password FROM users --密码是MD5哈希DVWA的默认密码比如password对应5f4dcc3b5aa765d61d8327deb882cf99可以直接在线解密或者用hashcat跑。实操心得group_concat默认有长度限制1024字符如果表名或列名太多被截断可以用substring分段读取或者用limit逐行取。DVWA的数据量小一般不会触发限制但实战中这是常见坑点。3. Medium级别当单引号被转义后怎么继续玩3.1 转义函数不是铜墙铁壁数字型参数是突破口切换到Medium级别后你会发现输入1不再报错了页面返回正常。查看源码DVWA提供了源码查看功能会发现Medium级别用了mysqli_real_escape_string对单引号进行了转义同时把参数类型改成了数字型——查询变成了WHERE user_id $id没有引号包裹。这意味着单引号注入失效了但数字型注入依然可行。我实测时输入1 AND 11页面正常返回输入1 AND 12页面返回空。这说明AND逻辑被带入了查询且生效。数字型注入不需要闭合引号直接拼接SQL语句即可。这时候UNION SELECT依然可用1 UNION SELECT 1,2 --注意这里不需要前面的单引号因为参数本身就是数字型。但Medium级别还有一个变化它用了LIMIT 1限制返回行数。查看源码会发现查询是SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1。这个LIMIT 1会限制UNION的结果只显示第一行所以如果你用1 UNION SELECT 1,2原查询返回了ID为1的用户UNION的结果被LIMIT截掉了。解决办法是把原查询的ID设为一个不存在的值比如-1或0这样原查询返回空UNION的结果就成为第一行-1 UNION SELECT 1,2 --我实测时用-1 UNION SELECT user,password FROM users --页面直接显示了第一个用户的用户名和密码。如果想看第二个用户可以用LIMIT偏移-1 UNION SELECT user,password FROM users LIMIT 1,1 --这里的LIMIT 1,1表示跳过第一行取第二行。注意这个LIMIT是加在UNION SELECT后面的不是原查询的LIMIT。3.2 当UNION被限制时用报错注入打开局面有些环境可能对UNION做了额外过滤或者页面不回显查询结果。DVWA Medium级别没有这个问题但实战中经常遇到。这时候可以试试报错注入利用extractvalue或updatexml函数让数据库报错并带出数据-1 AND extractvalue(1,concat(0x7e,database())) --0x7e是波浪号~的十六进制用于分隔报错信息。页面会返回类似XPATH syntax error: ~dvwa的错误从而泄露数据库名。这个方法在MySQL 5.1以上版本有效MariaDB同样支持。DVWA Medium级别实测可以触发但要注意extractvalue的参数长度限制是32位超长数据需要分段读取。注意报错注入在实战中越来越难用因为很多生产环境关闭了详细报错只返回通用错误页面。但在DVWA这种练习环境里它是理解注入多样性的好工具。3.3 Medium级别的完整拖库流程复现把Medium级别的手工注入流程完整走一遍判断注入类型输入1 AND 11正常1 AND 12空确认数字型注入。猜列数1 ORDER BY 2正常1 ORDER BY 3报错确认2列。找显位-1 UNION SELECT 1,2 --页面显示1和2。获取库名-1 UNION SELECT 1,database() --返回dvwa。获取表名-1 UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schemadatabase() --。获取列名-1 UNION SELECT 1,group_concat(column_name) FROM information_schema.columns WHERE table_name0x7573657273 --。这里用十六进制0x7573657273代替users因为单引号被转义了十六进制不受影响。拖数据-1 UNION SELECT user,password FROM users --。第6步的十六进制转换是个关键技巧。users的ASCII码是75 73 65 72 73转成十六进制就是0x7573657273。在MySQL中十六进制字面量可以直接当作字符串使用不需要引号。这个技巧在单引号被转义的场景下非常实用。4. High级别会话变量与LIMIT夹击下的手工注入策略4.1 输入框变成了会话变量注入点转移到了CookieHigh级别的DVWA把输入方式从GET/POST参数改成了会话变量。你点击页面上的链接选择ID后ID被存储在$_SESSION[id]中查询时直接从会话读取。这意味着你无法直接在URL或表单里注入但会话变量最终还是会进入SQL查询所以注入点依然存在只是入口变了。查看High级别源码会发现查询是SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1其中$id来自会话变量。而会话变量的值是通过一个下拉菜单选择的下拉菜单的选项是1到5。表面上看你只能选这几个值但如果你用Burp Suite拦截请求会发现选择ID的请求是POST到session-input.php参数是id1。你可以把这个参数改成注入payload它会存入会话然后在sql-injection.php页面触发查询。我实测时用Burp拦截了选择ID的请求把id1改成了1 UNION SELECT user,password FROM users --放行后访问SQL Injection页面页面直接显示了用户名和密码。但这里有个问题LIMIT 1依然存在而且原查询的WHERE user_id $id中如果$id是1 UNION SELECT ...原查询会先执行WHERE user_id 1返回一行然后UNION追加一行LIMIT 1只取第一行——也就是原查询的结果UNION的结果被截掉了。解决办法和Medium级别一样把原查询的ID设为一个不存在的值。但High级别的会话变量是通过下拉菜单设置的下拉菜单只有1到5。你需要拦截请求把id参数改成-1 UNION SELECT user,password FROM users --。这样原查询WHERE user_id -1返回空UNION的结果成为第一行LIMIT 1取到的就是注入结果。4.2 当LIMIT 1卡住UNION时用子查询绕过如果环境不允许你修改会话变量的原始值比如某些场景下会话变量被服务端强制重置还有一个办法用子查询把UNION的结果“塞”进原查询的返回列中。比如1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT user FROM users LIMIT 1),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) --这是一个经典的报错注入变体利用GROUP BY和RAND()的冲突让数据库报错从而带出子查询的结果。这个方法不依赖UNION也不受LIMIT 1影响因为它在原查询的WHERE条件里执行。DVWA High级别实测可以触发但报错信息可能被页面截断需要调整payload长度。实操心得High级别的会话变量注入在实战中对应的是“二次注入”或“存储型注入”场景——你的输入先被存起来然后在另一个地方被取出使用。这类场景下注入点的判断需要你追踪数据的流动路径而不是只盯着一个输入框。4.3 High级别手工注入的完整操作步骤把High级别的手工注入流程整理成可复现的步骤打开DVWA把安全等级设为High进入SQL Injection页面。开启Burp Suite代理拦截浏览器请求。在SQL Injection页面选择ID为1点击SubmitBurp会拦截到POST请求。把请求中的id1改为id-1 UNION SELECT user,password FROM users --。放行请求浏览器会跳转回SQL Injection页面。此时页面显示的First name和Surname就是users表中的用户名和密码。如果想拖其他表把UNION SELECT中的查询替换即可。注意High级别的会话变量在每次选择ID时都会被覆盖所以如果你想连续注入不同的payload每次都需要重新拦截请求并修改。另外--后面的空格在POST请求中需要URL编码为--或--%20否则可能被截断。5. Impossible级别为什么它真的防住了手工注入5.1 预处理语句与Anti-CSRF Token的双重防线Impossible级别的DVWA用了两个核心防护预处理语句Prepared Statements和Anti-CSRF Token。预处理语句的原理是先把SQL查询的结构固定下来参数用占位符?表示然后再把用户输入绑定到占位符上。这样用户输入永远不会被当作SQL代码解析而是作为纯数据传入。比如查询变成SELECT first_name, last_name FROM users WHERE user_id ?你输入1 OR 11数据库只会把它当作一个字符串去匹配user_id不会执行OR逻辑。Anti-CSRF Token则是防止跨站请求伪造每次请求都需要携带一个服务端生成的tokentoken不匹配就拒绝请求。这个机制本身不防注入但它增加了自动化工具的难度因为你需要先获取token再构造请求。我实测在Impossible级别输入1 UNION SELECT user,password FROM users --页面返回“Invalid user ID”或类似提示没有任何数据泄露。查看源码确认参数确实被绑定到了占位符上而且服务端对输入做了严格的数字校验——如果不是纯数字直接拒绝。5.2 从Impossible级别反推防御思路Impossible级别的防护思路值得每个开发者学习。预处理语句是防SQL注入的银弹但前提是正确使用——所有用户输入都必须通过占位符绑定不能拼接字符串。我见过一些代码用了预处理语句但在某些参数上又退回了字符串拼接结果还是被注入了。另外输入校验也很重要如果参数预期是数字就强制转换成整数预期是字符串就限制长度和字符集。从攻击者角度看Impossible级别几乎堵死了手工注入的所有路径。但实战中很多系统并没有做到Impossible级别这么彻底。常见的问题包括预处理语句只用在部分查询上、输入校验不严格、报错信息泄露过多、数据库权限过大等。所以手工注入的技能依然有用武之地只是你需要先找到那个薄弱的环节。注意DVWA的Impossible级别在源码中用了$id $_SESSION[id]然后if(is_numeric($id))判断再绑定到预处理语句。这个is_numeric检查是关键——它把非数字输入直接挡在了数据库之外。如果开发者忘了这一步即使有预处理语句某些边缘情况依然可能出问题。6. 手工注入中那些工具不会告诉你的细节6.1 注释符的坑--、#和/* */在不同环境下的表现注释符是手工注入中最容易被忽视的细节。MySQL支持--注意空格、#和/* */三种注释。但在URL中#会被浏览器当作锚点不会发送到服务端所以实际测试中要用--或%23#的URL编码。--后面的空格在URL中容易被过滤或截断可以用--代替在URL解码后是空格。/* */是多行注释适合在payload中间截断查询比如1 UNION SELECT 1,2 /*。DVWA Low级别实测中--和#都可用但#在URL中需要编码为%23。Medium和High级别中--依然有效但要注意POST请求中的空格编码。我踩过的一个坑是在High级别的POST请求中用了--但没有URL编码结果注释符后面的内容被当作了参数的一部分导致查询报错。后来改成--就正常了。6.2 数据类型不匹配导致的“假失败”手工注入时经常遇到一种情况payload语法没问题但页面就是没反应。这可能是数据类型不匹配导致的。比如原查询是WHERE user_id $id参数是字符型你用了数字型的payload1 UNION SELECT 1,2数据库可能会尝试把字符串1 UNION SELECT 1,2转换成数字转换失败就变成0查询返回空。这时候你需要加上单引号闭合1 UNION SELECT 1,2 --。反过来如果原查询是数字型WHERE user_id $id你用了字符型的payload1 UNION SELECT 1,2 --单引号会导致语法错误。所以判断参数类型是第一步字符型加引号数字型不加。DVWA Low是字符型Medium是数字型High是字符型会话变量被单引号包裹这个差异直接决定了payload的写法。6.3 回显位被占用时的“盲注”思维有时候UNION SELECT的显位被原查询的结果占用了页面只显示原查询的数据UNION的结果看不到。这时候可以试试布尔盲注或时间盲注。布尔盲注是通过构造AND 11和AND 12观察页面返回内容的差异来判断条件真假。时间盲注则是用SLEEP()函数如果页面延迟响应说明条件为真。DVWA Low级别实测中1 AND 11 --页面正常1 AND 12 --页面返回空这就是布尔盲注的基础。如果要猜数据库名的第一个字符可以构造1 AND substring(database(),1,1)d --如果页面正常说明第一个字符是d如果页面空说明不是。逐个字符猜下去虽然慢但在没有回显的环境下是有效手段。时间盲注的payload类似1 AND IF(substring(database(),1,1)d,SLEEP(3),0) --如果页面延迟3秒说明条件为真。这个方法在实战中更隐蔽但速度更慢适合在布尔盲注也被拦截时使用。6.4 从DVWA到实战环境差异与应对策略DVWA是练习环境实战中的数据库类型、版本、权限、WAF规则都不同。我总结了几条从DVWA过渡到实战的经验数据库类型判断DVWA用MariaDB实战中可能是MySQL、PostgreSQL、SQL Server、Oracle等。不同数据库的information_schema结构、函数名、注释符都不同。比如SQL Server用--注释但字符串拼接用而不是concat。权限差异DVWA的数据库用户权限较高可以读information_schema。实战中可能只有特定表的读写权限这时候需要先探测权限再决定注入策略。WAF绕过DVWA没有WAF实战中可能有。常见的绕过手法包括大小写混写、内联注释、编码绕过、等价函数替换等。比如UNION SELECT被拦截可以试试UNION/**/SELECT或UNION%0aSELECT。报错信息DVWA直接显示报错实战中可能只返回通用错误页。这时候需要依赖盲注或带外通道如DNSlog来获取数据。实操心得我建议在DVWA上练熟手工注入后去一些合法的靶场平台如SQLi Labs、PortSwigger Web Security Academy继续练习不同数据库和场景的注入。DVWA的四个级别覆盖了从无防护到强防护的典型场景但真实世界的复杂性远不止这些。手工注入的核心能力是“根据反馈调整策略”这个能力只能在不断变化的环境中磨出来。7. 我对手工注入练习节奏的建议如果你刚开始练手工注入我的建议是不要贪快。DVWA的Low级别可以让你在半小时内走完整个拖库流程但你要花时间理解每一步的原理为什么用ORDER BY猜列数、为什么用UNION SELECT找显位、为什么用group_concat合并结果。这些“为什么”比“怎么做”更重要因为它们决定了你在遇到新环境时能不能举一反三。Medium和High级别的手工注入需要你熟悉Burp Suite的基本操作——拦截请求、修改参数、放行请求。这个工具在实战中几乎是必备的早点上手没坏处。Impossible级别不用花太多时间尝试注入它的价值在于让你理解防御方是怎么思考的这对你写安全代码或者做代码审计都有帮助。最后分享一个我自己的练习方法每完成一个级别的注入我会把payload整理成一份笔记标注每个payload的适用场景和原理。比如-1 UNION SELECT 1,database() --适用于字符型注入且有回显位-1 UNION SELECT 1,database() --适用于数字型注入。这份笔记后来成了我实战中的速查表比任何工具都可靠。手工注入的技能不会过时因为只要还有代码在拼接SQL字符串注入就永远存在。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询