资讯动态

DVWA靶场SQL盲注实战:从手工布尔盲注到时间盲注通关指南

发布时间:2026/9/11 1:36:08 来源:尧图企业网站定制
DVWA靶场里那么多关我最推荐新手先吃透的就是 SQL InjectionBlind也就是SQL盲注。很多人把这个当成“注入的进阶”其实它只是换了种玩法地址栏照样存在注入点但页面不会把查询结果直接回显你只能靠页面是真响应还是假响应、是快还是慢把数据库内容一点一点“问”出来。练好这一关你才敢说自己真的理解了注入的本质而不只是会抄payload。这篇内容我会从环境搭建讲到原理再从LOW难度手工猜解一路打到HIGH难度中间把所有踩过的坑、排查思路、还有为什么这样构造语句的细节都翻出来说一遍。适合刚学Web安全、准备找实习或者单纯对漏洞原理好奇的朋友也适合那些已经在用sqlmap但一直没搞懂它背后在干什么的人。1. 环境准备先把DVWA跑起来1.1 为什么选DVWA练盲注DVWA全称是Damn Vulnerable Web Application中文叫“脆弱的Web应用”字面意思就是官方故意制造一堆漏洞给你练习。它跟真实业务系统最大的区别在于源码就摆在PHP目录里你每打完一个漏洞都能翻出源码对照看看开发者究竟哪行代码写错了或者哪个函数用歪了。这种“攻击面源码审计”双视角的体验是其他所谓测试环境给不了的。具体到SQL InjectionBlind这道题DVWA把盲注场景做了三档难度。LOW难度几乎不设防教你认识盲注长什么样MEDIUM难度加了个转义函数逼你想办法绕过HIGH难度又搞了个CSRF Token和LIMIT限制差不多就是弱化版的真实防御。三档打下来你对SQL注入的整个生命周期会有很直观的感觉。1.2 用Docker快速启动DVWA如果你用的是Kali Linux或者本机装好了Docker最快的方式是直接拉镜像docker pull vulnerables/web-dvwa docker run -d -p 8080:80 vulnerables/web-dvwa跑起来之后浏览器访问http://127.0.0.1:8080默认账号是admin密码是password。这里要注意这个镜像的数据库初始化偶尔会抽风如果登录之后一直提示“Setup”页面没完成不用慌进http://127.0.0.1:8080/setup.php点一下Create / Reset Database让它重建一次数据库就好。Docker的好处是干净玩坏了直接删容器重建几秒钟又回到初始状态。缺点也很明显容器一旦删除之前的“战绩”和改过的源码都会丢失所以每次练习前最好先确认下当前安全等级是不是你要的那个。1.3 手动搭建适合想折腾底层的人不想用Docker也可以手动搭建。环境要求很传统PHP MySQL ApacheWindows上用phpStudy也能跑。我习惯直接在Kali里用系统自带的Apache和MariaDBsudo apt update sudo apt install apache2 mariadb-server php php-mysqli php-gd libapache2-mod-php sudo git clone https://github.com/digininja/DVWA.git /var/www/html/DVWA sudo chown -R www-data:www-data /var/www/html/DVWA/hackable/uploads/ sudo chmod -R 755 /var/www/html/DVWA/然后进入DVWA的config目录把默认配置拷出来cd /var/www/html/DVWA/config sudo cp config.inc.php.dist config.inc.php接着编辑config.inc.php默认情况下多半不用改因为DVWA默认连接的就是本地MySQL账号root、密码空如果MySQL有密码就填进去$_DVWA[ db_password ] ;最后启动服务访问http://127.0.0.1/DVWA/login.php用admin/password登录。首次进入时会提示先点Setup进入setup.php拉到底部点重置数据库即可。手动搭的最大好处是你随时能打开源码看。盲注这关我强烈建议你打完一次就翻一眼源码比如LOW难度源码就在/var/www/html/DVWA/vulnerabilities/sqli_blind/source/low.php看到那几行拼接SQL的语句你会瞬间理解刚才所有payload为什么有效。1.4 登录后第一件事设置安全等级DVWA右侧有DVWA Security菜单里面可以切换安全等级。打盲注建议按顺序过先LOW再MEDIUM最后HIGH。每换一次等级当前页面的注入逻辑就会变你得重新测注入点这本身就相当于在练“环境变化后快速定位漏洞”的能力。还有个小建议如果你只是练习千万别开Impossible以外的等级去连真实业务。所有练习都必须在自己的靶机、自己的环境里做养成这个边界感比多打十次靶还重要。2. 盲注原理页面不报错怎么把数据“问”出来2.1 普通注入和盲注的差别普通SQL注入最爽的就是页面会把查询结果直接显示出来。比如输入用户ID页面就显示对应的用户名和姓氏那你只需要调整SQL让联合查询闭合直接就能把库里的账号密码打在屏幕上。盲注则不一样。SQL语句照样存在注入漏洞但应用层出于某种原因可能是程序员故意屏蔽、可能配置了统一错误页不会把数据库的报错或者查询结果回显到前端。你看到的只有两种情况页面显示了一条正常记录或者页面什么都没显示。这就好比你要确认一扇门后面有没有人。普通注入相当于门上有玻璃直接看盲注就是门是全封闭的你只能敲门听到里面答应一声就知道有人没人应就是空房。2.2 布尔盲注用“是与否”猜数据布尔盲注的思路是构造一个逻辑条件如果条件为真页面维持原来的样子如果为假页面就不显示内容。用这种方式你可以一个字符一个字符地猜数据库内容。最经典的判断语句1 AND 11 -- -如果页面正常返回说明条件为真再试1 AND 12 -- -如果页面空白说明条件为假也就是注入成立。因为你是在拿用户输入拼SQL这个输入里带了单引号闭合又用AND 11或AND 12去控制整个判断的真假。也许你会觉得这不就是在“猜”吗对本质就是猜但猜得很科学。2.3 时间盲注真和假都分不清时用时间说话有时候页面连真假响应都不区分无论条件真假的返回都一样。这时候布尔盲注就失效了得换时间盲注。思路是用SLEEP()函数让条件为真时数据库故意延迟几秒再返回通过延迟有没有发生来判断条件是否成立。常见用例1 AND IF(ASCII(SUBSTR(database(),1,1))100,SLEEP(3),0) -- -如果页面3秒后才响应说明数据库名的第一个字符ASCII码是100也就是字母d。这种判断方式不需要页面显示什么只要有网络延迟就能判断所以哪怕页面完全静态也能测这也是为什么时间盲注在真实测试里经常被当成“最后的通用选手”。2.4 猜解时常用的函数盲注本身不算难难在怎么高效地“组织语言”去问数据库。以下这几个函数你最好烂熟于心DATABASE()返回当前使用的数据库名。LENGTH()返回字符串长度。SUBSTR(str,pos,len)从字符串中截取子串下标从1开始。ASCII(char)返回字符的ASCII码。IF(cond,true_expr,false_expr)条件成立执行前一个表达式否则执行后一个。SLEEP(seconds)延迟执行配合IF做时间盲注。盲注的基本套路就是先用LENGTH()猜长度再用SUBSTR()结合二分法逐位猜ASCII码最后拼成完整的字符串。手工去一位位猜确实费劲但这个过程对理解SQL的运算逻辑特别有帮助就像学加密之前先手算一遍RSA之后用工具才不慌。3. LOW难度实战手工盲注一次完整通关3.1 第一步确认注入点进入SQL Injection (Blind)页面你会发现只有一个输入框提示输入User ID。先输个正常值看看响应。输入1页面显示存在一条记录内容是First name和Surname。接下来验证它是不是存在字符串类型的注入点。输入1留意页面有没有报错或者错误提示里有SQL语句片段。在LOW难度下多数环境会暴露MySQL报错这就等于确认了单引号注入位置。然后输入1 AND 11 -- -正常返回。输入1 AND 12 -- -页面空白。两步对比注入点确认布尔盲注条件成立。3.2 第二步判断返回页面的差异这一步是整个盲注能打下去的基础。你必须在心里很明确地记住“真”长什么样“假”长什么样。实际测试中建议用Burp Suite抓包看响应长度。比如正常ID1时响应长度在1500字节左右ID1 AND 11也差不多ID1 AND 12却只有几百字节。有这种差异后面所有盲注语句都能用“响应长度是否约等于1500”作为真伪标准。我见过很多新手在页面上肉眼分辨不出来一紧张就开始瞎猜。这个时候不要盯着页面看用Burp或者浏览器开发者工具看Response的大小比肉眼可靠得多。3.3 第三步猜解数据库名现在开始动真格。第一步先查数据库名长度1 AND LENGTH(DATABASE())4 -- -返回正常说明数据库名长度是4。然后逐位猜字符。比如第一位1 AND ASCII(SUBSTR(DATABASE(),1,1))100 -- -返回正常说明第一个字符ASCII码是100也就是字母d。再做第二位1 AND ASCII(SUBSTR(DATABASE(),2,1))118 -- -第二个字符为v。继续下去很快能拼出dvwa。这就是DVWA默认数据库名。这里要啰嗦一句正规打法是用二分法不是从32到126一个个试。比如先猜ASCII码是否大于100是就再猜是否大于110这样每个字符最多7次请求就能确定。手工做二分也很累所以后面我会说怎么用Burp Intruder快速跑。3.4 第四步从表名到数据内容数据库名猜出来之后目标是拿到库里的表和账号数据。MySQL 5.0以上版本有个元数据表叫information_schema.tables里面存着所有库、所有表的信息。盲注里我们没法直接看多行结果所以要借助一个技巧让条件去判断“某个表名的第几位是什么字符”。先猜当前库里有多少张表或者直接猜表名。DVWA至少会有一张users表我可以验证1 AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schemaDATABASE() AND table_nameusers)1 -- -返回正常说明确实存在users表。然后逐位去猜users表里的列名。比如列user1 AND ASCII(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schemaDATABASE() AND table_nameusers LIMIT 0,1),1,1))117 -- -这里LIMIT 0,1是取第一行第一个字符ASCII码117对应字母u。用同样的办法可以拼出user_id、first_name、last_name、user、password等列名。列名拿到后开始爆数据。比如拿用户admin的密码哈希1 AND ASCII(SUBSTR((SELECT password FROM users WHERE useradmin LIMIT 0,1),1,1))100 -- -为真继续二分。最终得到的是一串MD5值去cmd5类似的平台反查一下大多能解出明文。用DVWA默认的passwordMD5是5f4dcc3b5aa765d61d8327deb882cf99反查结果就是英文单词password。整个流程说白了就是库名 - 表名 - 列名 - 行数据一层套一层每一层都在做同一套猜解动作。玩多了你会觉得有点机械但正因为机械你才更能体会到“SQL的查询其实就是一个表达式可以被条件化”。3.5 自动化工具辅助sqlmap只干脏活手工跑完一遍之后再用sqlmap做交叉验证那种感觉特别通透sqlmap -u http://127.0.0.1/DVWA/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiePHPSESSID你的会话ID; securitylow --dbs跑完它会自动识别出布尔盲注和时间盲注。接着sqlmap -u http://127.0.0.1/DVWA/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiePHPSESSID你的会话ID; securitylow -D dvwa --tables sqlmap -u http://127.0.0.1/DVWA/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiePHPSESSID你的会话ID; securitylow -D dvwa -T users --dump三条命令下来数据全出来了。但我建议每个人在跑sqlmap之前先确保自己已经用手工方法把同一份数据完整猜通过。这样你以后用sqlmap时看到它在日志里“ORDER BY”、“AND OR”地试心里才有谱。3.6 LOW难度源码复盘打完关上电脑前建议看下源码?php if( isset( $_GET[ Submit ] ) ) { // Get input $id $_REQUEST[ id ]; // Check database $getid SELECT first_name, last_name FROM users WHERE user_id $id;; $result mysqli_query( $GLOBALS[ __mysqli_conn ], $getid ); // Get results $num mysqli_num_rows( $result ); if( $num 0 ) { echo preUser ID exists in the database./pre; } else { echo preUser ID is MISSING from the database./pre; } } ?发现没有它对你的输入只做了$_REQUEST[id]然后直接拼进SQL字符串里完全没有任何过滤和转义。页面虽然不回显数据但会根据查没查到记录返回“存在”或“缺失”两种提示。这就是标准的布尔盲注场景。看一下这个源码之前所有payload的构造逻辑就彻底通了。4. MEDIUM与HIGH难度防护升级思路升级4.1 MEDIUM难度转义带来的麻烦把安全等级切换到MEDIUM再回盲注页面你会发现请求方式从GET变成了POST需要提交的字段是id和Submit。这里源码变成了$id mysqli_real_escape_string( $GLOBALS[___mysqli_ston], $_POST[ id ] );mysqli_real_escape_string()会对输入中的单引号、双引号、反斜杠等字符做转义所以1这种payload会变成1\单引号被当成字符串内容闭合失败直接注入就失效了。怎么绕核心思路是换个“不需要引号闭合法”的路径。如果查询本身就是SELECT first_name, last_name FROM users WHERE user_id $id;注意这一行是数字型拼接根本没有引号包裹。但这段源码里实际还有一层判断实际是WHERE user_id $id 所以有引号。那就需要找个不走引号的判断入口吗其实更常见的思路是如果没找到闭合方式就尝试利用数字上下文、编码绕过或者堆叠注入这取决于后端实际SQL语句长什么样。以MEDIUM为例最简单的验证方法还是先提交一个数字1看看页面是否正常再提交1 AND 11、1 AND 12看差异。因为数字上下文里不需要引号闭合转义函数对纯数字没影响。如果这关的SQL确实是数字型拼接那你甚至会发现LOW难度的payload大多失效但数字型盲注语句全都能跑通。这就是环境变换后快速重置心智模型的过程。4.2 HIGH难度Token与LIMIT 1HIGH难度又加了两个东西。第一个是CSRF Token页面上多了一个user_token隐藏字段。这个token来自上一次会话每次提交都必须带着否则直接判定无效。它本质是为了防CSRF但对自动化盲注产生了很大的干扰。你如果用sqlmap跑HIGH必须先把token动态提取出来并保持会话更新否则跑一次就被打回登录。另一个是SQL语句末尾强制加上了LIMIT 1$id $_POST[ id ]; $getid SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1;;LIMIT 1会让查询最多只返回一行这样即使你用注入的方式尝试一次取多行也可能被限制住。不过对于盲注而言影响最大的其实是Token因为它让每个请求都变成“动态认证”手工盲注每发一条payload之前都要去页面拿一次token效率一下子下去了。到了HIGH难度我建议你在浏览器里操作时用Burp的宏功能或者写个脚本去自动提取token然后再继续盲注。靶场练习嘛折腾过程本身就是收获。4.3 从防御视角看盲注打穿三档难度之后其实更值得想的是“怎么防”。最根本的解法就是参数化查询。拿PHP里的PDO举例$stmt $pdo-prepare( SELECT first_name, last_name FROM users WHERE user_id :id ); $stmt-execute( [ id $id ] );这样用户输入只会被当成数据处理永远不会被拼接到SQL语法里不管是普通注入还是盲注全部失效。其次是限制数据库账号的权限比如应用账号只给SELECT不给DROP、INSERT、UPDATE就算注入成功了也做不了太多操作。最后是错误信息收敛不管是数据库报错还是应用异常统一返回通用错误页这样盲注条件判断就会失去依据。在靶场里你是进攻方但你练的目标应该是“懂得如何彻底堵住它”。高手从漏洞里看到的不是一条绕过链而是一整条防御链上可以下手的点同时也是防护时该加固的点。5. 常见问题与排查记录5.1 页面没有“TRUE/FALSE”差异怎么办最常见的问题就是你输入1 AND 11和1 AND 12页面长得一模一样完全看不出差别。遇到这种情况先检查两件事一是请求是否真的被后端拼接进了SQL可以看源码确认拼接方式二是是否页面开启了统一错误显示把查询结果改成了同一张静态页。如果页面静态化确实屏蔽了差异可以考虑转用时间盲注验证先确认正常请求的响应时间再发1 AND SLEEP(3) -- -看是否延迟3秒。延迟出现就说明注入点还在布尔盲注失了效但时间盲注还能工作。5.2 单引号提交后直接报错LOW难度下提交1经常看到报错页面。这不算坏消息反而是洞口敞开的信号。但有些时候报错内容不显示任何SQL信息只有一句“You have an error in your SQL syntax”这时你要么直接继续用AND 11/AND 12验证要么把单引号拆成URL编码%27再提交试试防止是前端层过滤导致的问题。还有种情况是环境中开了魔术引号或者某种全局过滤单引号被转义。此时先试宽字节注入的思路不过靶场环境里较少见建议优先翻源码确认过滤逻辑再针对性绕过。5.3 Docker搭建后数据对口令不一致如果你之前用的是老的vulnerables/web-dvwa镜像偶尔登录admin/password会失败。这时直接进容器看MySQL里的用户表手动把admin的密码哈希改成MD5值再刷新登录。或者干脆点Setup里的Reset Database重新初始化数据。练习环境的数据没那么金贵坏了就重来重点在过程。5.4 其他环境坑DVWA页面提示“Access denied”或者连不上数据库多半是config里数据库密码不对。Apache没有开启重写或者PHP扩展没启用保险起见安装时一次装齐php-mysqli、php-gd。有时候页面只有HTML无样式那是静态资源路径被改了别管它不影响注入练习。手动跑盲注之前一定先把Cookie复制好很多环境登录态超时或者一句SQL没跑完就被踢下线容易误判注入失败。我自己的习惯是每打一关就在终端记一次payload和结论方便后面复盘。这比截图管用因为你能看到判断链是怎么一步步走通的。最后再分享一个我自己练盲注的小技巧。手工猜解时别太依赖鼠标直接在Burp的Repeater里用Intruder跑设置好payload position拿响应长度做排序一次就能测完所有ASCII码。熟练之后你甚至可以自己写个二三十行的Python脚本去自动化盲注思路就是上面的二分法加HTTP请求脚本跑通的那一瞬间你会觉得整个SQL注入体系都通透了。靶场存在的意义不在于你能把多少payload背下来而是让你亲手走过一遍漏洞从产生到利用再到修复的全过程这种手感才是以后在真实环境里最值钱的东西。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价