已经知道了SQL注入的原理,并且能够完成支持union 且有回显的注入,这种情况是最简单的,最容易注入的,那么,如果注入点发生在update、insert等这些不会有返回内容而仅仅只返回true或者false的 SQL语句中,或者仅仅select而没有输出查询的内容时,是不是就不能获取数据了呢?

当然不是,如果是这样,就不会有这个实验了,要想在上面所说的情况下获取数据,通过盲注就可以获取数据。

盲注主要应用在不能通过查询直接显示数据的地方,在盲注时,主要通过页面返回的不同来判断,盲注主要分为以下三种:

  1. 基于布尔的盲注。这种方法主要通过页面的返回内容不同来获取信息。
  2. 基于时间的盲注。这种方法主要通过页面的响应时间不同来获取信息。
  3. 基于报错的盲注。这种方法通过报错的方式把想要的信息爆出来

下面就来学习上面三种盲注所用到的方法,首先看下基于布尔的盲注。

基于布尔的盲注主要通过页面的返回值不同来获取信息。并不是所有的页面都会输出SQL语句执行的错误,更常用的方法一般是用通用的错误信息来替代数据库错误信息,即使是出现通用的错误信息,我们也可以推断SQL注入是否可行。

它的判断原理是:如果我们注入的SQL指令被成功执行了,那么页面应该是正常显示而不是显示通用的错误信息,如果注入了SQL指令后,语句执行出错,那么页面就应该显示通用的错误信息而不是正常显示。

先对着源码来进行注入,源码如下:

可以看到,该页面输出仅有2种情况,sql语句执行成功输出“找到记录!”,失败则输出“没有找到记录!”,没有对查询后的内容进行输出,所以,我们不能像“SQL注入原理与实践”这个实验一样通过联合查询来获取数据,虽然像“SQL注入原理与实践”这个实验一样注入union指令可以执行成功,但是获取的数据,并没有返回给我们,所以,我们需要用其他办法来间接地获取数据。

“SQL注入原理与实践”在我之前的博客有詳細介紹過

基于布尔的盲注主要通过控制sql语句的where部分返回true或者false,以此来获取数据。所以我们需要通过在where 后面再添加一个条件,利用后面这个条件的正确与否来获取信息,比如,我们可以注入一个子查询,因为在数据库中子查询会先执行,它不会显示查询结果而是作为外部查询的条件,然后把这个子查询的结果跟某个字符或者字符串比较,如果返回true,则说明子查询的结果就是拿来比较的字符串,如果返回false,则说明子查询的结果就不是拿来比较的字符串,通过这种方式就可以获取数据。

如果我们要判断当前连接数据库是不是sqli,我们可以构造如下sql语句。

1 and (select database() = 'sqli')

此时数据库最终执行的是:

select * from `new` where id = 1 and (select database() = 'sqli') limit 0, 1

其中红色部分为我们注入的SQL指令。如果当前的数据库是sqli,则我们注入的子查询返回true,则相当于执行以下语句:

select * from `new` where id = 1 and (1) limit 0, 1

所以只要存在id=1的数据则这个查询会返回一条数据,页面输出“找到记录!”。很明显id=1的数据是存在的,所以此时的where 返回的是true。

注:在MySQL中,非空或者非0即为true,所以1就是true。

输出“找到记录!”,然后把sqli改成其他字符。

输出“没有找到记录!”,说明当前连接的数据库就是sqli。

我们在没有输出信息的情况下就获取了当前的数据库。但是,如果数据库名比较长,很明显我们很难猜到,这样的话注入就会显得很鸡肋,因为要猜的次数太多了,即使数据库名长度为4,我们也需要列出所有长度为4的字母+数字的组合,然后一个一个来测试。这样无疑效率极低甚至数据库长度过长几乎数据库名都猜不到,更不用说获取里面的数据了。所以得再改进下。

实际上,在基于布尔型的盲注中,主要用到一些字符串函数,如:substring、ascii。利用这些函数来与指定的数字进行判断,以此来获取数据。

substring函数用来字符串截取,它的原型如下:

substring(str, pos, length),该函数最少需要2个参数,str为被截取字段,pos表示从第几位开始截取,length表示截取的长度,length可以不传参数,则表示从pos开始截取到字符串结束。例:

select substring(“abcdef”, 2) ,结果为:bcdef

select substring(“abcdef”, 2, 1),结果为 b

ascii函数用来求字符串的ascii码。例:

select ascii(‘a’),结果为97

利用这2个函数,我们对子查询的返回结果进行按位截取,然后获取对应的ascii码,最后跟我们指定的数字进行比较,如果这个ascii码跟我们指定的数字相等,则页面会输出“找到记录!”,否则输出“没有找到记录!”

所以,如果我们想要获取所有的数据库,可以先让子查询返回第一个数据库名,然后通过字符串截取分别截取每位后获取它的ascii码,最后来跟某个数字比较,我们只要依次增大该数字即可。

思路是这样的:首先子查询返回某个数据库名,然后通过substring函数来获取这个数据库名的第n个字符,然后计算它的ascii码,最后来跟某个数字比较,依次增加这个数字,使这2个数字相等。说明我们想要获取的字符的ascii码就是我们拿来比较的数字,然后根据ascii码求字符就行了。

为了减少一些不必要的工作,可以先判断一下数据库名、字段名以及列名的长度,比如,我们要查询第一个数据库库名的长度,可以构造如下语句:

sqli.com/sqli-bool.php?id=1 and length((select schema_name FROM information_schema.schemata LIMIT 0,1)) >10

如果第一个数据库名的长度大于10,则会输出“找到记录!”,如果小于10,则会输出“没有找到记录!”。

它的执行顺序为:

  1. 执行select schema_name from information_schema.schemata limit 0,1,这条SQL语句返回一条记录,也就是说,返回数据库中的一个数据库名
  2. 然后length判断这个数据库名的长度,比如步骤一返回的结果为information_schema,那么就会执行length('information_schema'),这个函数执行以后的返回值为18。可能会觉得在上面构造的注入语句中,length没用单引号,但是在这里写了个单引号觉得疑惑,因为这里子查询返回结果为字符串,也就是说,子查询的返回结果传给length的时候,length会把传过去的返回值当作字符串,但是如果你自己在用length求某个字符串的时候,不用单引号,如:length(information_schema),就会提示语法错误,因为length只接受字符型参数。
  3. length返回数据库名长度后, 会判断18 > 10的返回值,两个值比较的返回结果是布尔值,如果18 > 10成立,则返回true,否则返回false。而如果返回true,就会在数据库中查找id为1的记录,然后返回这条记录。如果返回false,则会没有记录返回。因为and 要求两边都返回true。
  4. 测试显示“找到记录!”说明第一个数据库名的长度大于10,然后把10逐渐增大,直接显示“没有找到记录! ,这里的大于号可以换成小于号或者等号,一般是先用大于号或者小于号通过二分法确定值在哪个区间,然后再用等号来确认具体的值。

在测试的时候,可以一次增加5或者10,没必要每次只增加1。如下圖加到20:

测试到 = 18的时候,页面返回正常,说明第一个数据库名的长度为18。

然后就利用substring和ascii来获取这个数据库名。

判断它的第一个字符的ascii码是否大于64

http://sqli.com/sqli-bool.php?id=1 and ascii(substring((select schema_name FROM information_schema.schemata LIMIT 0,1),1,1)) >64

这里的执行顺序为:

  1. 首先执行里面的子查询
  2. substring截取子查询的返回值的一个字符
  3. 利用ascii函数求substring截取的字符的ascii码
  4. 把64和ascii函数的返回值进行比较
  5. 根据比较后返回的布尔值来进行查询,如果步骤4返回false。则整个SQL语句返回结果为空。

提示“找到记录!”,说明第一个字符大于64。

这里为什么选择64呢?因为ascii码只有128个,64刚好是一半,我们用二分法,一次就可以排除掉一半。所以我们第二次应该是判断它是否大于 64+ 64/2。

依然正确。再次增加该值。

提示没有找到,所以可以确定,第一个字符的ascii码它位于96-115之间。继续修改该值,

我们还可以把大于号改成小于号或者等于号。

根据测试,数字等于 105的时候,输出“找到记录!”。

然后我们可以根据ascii码表来查或者通过程序来输出该ascii码对应的字母。查表可知字母i的ascii码为105,所以我们可以知道第一个数据库的第一个字符为“i”。

然后猜第二个字符。我们只需要把substring的第二个参数改成2,然后再依次把拿来比较的值改成64,重复上面的步骤。

该值为110的时候,输出找到记录!”。

可以确定第二个字符的ascii码为“110”,对应的字母为“n”。

如此重复,修改substring的第二个参数和比较的值,可以得知第一个数据库的库名。为:

information_schema。

如果要获取第二个数据库的库名,则修改limit的第一个参数为1,表示返回第二个数据库的库名。注:第一个数据库的库名偏移为0。

然后来获取第二个数据库库名,为了减少不必要的步骤,同样先来确定第二个数据库库名的长度。

第二个数据库库名的长度为5。然后重复上面步骤,获取数据库名的每一位,最后组合。

可以看到,第二个数据库名的第一个字符的ascii码为109。

重复以上步骤,可以获取所有的数据库名。

如果limit的第一个参数大于当前数据库数量的话,后面的数字就算小于31,也会提示“没有找到记录!“。

当前是limit 5,1 但是测试是否大于31的时候还是返回了false,说明没有偏移为5的数据库,也就是说,只有5个数据库,因为是从0开始算的。所以最多只能limit 4,1。32以下的ascii码都是控制字符,不能显示。

最终获得的所有的数据库名:

  • information_schema
  • mysql
  • performance_schema
  • sqli
  • test

我们依然选择sqli为目标,先获取里面所有的表名。同样可以先判断第一个表名的长度。

长度为7。

第二个表名长度为3。

然后依次判断其他表名的长度。直到limit 3的时候,大于0都出错,说明这个数据库中只有3个表。

然后判断第一个表名的第一个字符的ascii码是否大于64。

http://sqli.com/sqli-bool.php?id=1 and ascii(substring((select table_name FROM information_schema.tables where table_schema='sqli' LIMIT 0,1),1,1)) > 64

提示“找到记录!“。继续增加该值。等于109的时候正常。

然后获取第二个字符的ascii码,经过测试为101。

重复上述步骤可以得知第一个表名为:message。

然后获取第二个表名。首先获取第二个表名的第一个字符的ascii码。为110的时候正常。

重复上面的步骤,最终可以获取当前数据库中的3个表名,分别为:

  • message
  • new
  • user

同样选择获取user表中的数据。在这之前我们需要知道该表中有哪些列名。

猜测第一个列名的第一个字符的ascii码,为105

http://sqli.com/sqli-bool.php?id=1 and ascii(substring((select column_name FROM information_schema.columns where table_name='user' and table_schema='sqli' LIMIT 0,1),1,1)) =105

当substring的第二个参数为3的时候,大于31也是返回错误,说明第一列只有2个字符,即:id。

然后获取第二列的列名。

第一个字符为u.

重复以上步骤,最终可以获得当前表中的所有列名:

  • id
  • username
  • password
  • lase_login
  • ip

知道了列名,就可以获取数据了。

比如我们要获取用户名,构造语句如下:

得知第一个用户名的第一个字符为“a”。

然后修改substring的第二个参数,获取第二个字符。

重复上面的步骤,可以得知第一个用户名为“admin”。

重复上面步骤最终可以获取当前列中所有的用户名:

  • admin
  • zhangsan
  • lisi
  • wangwu
  • zhaoliu

可以用同样的方法获取其他表或者其他数据库中的数据。

至此,我们就在没有回显的情况下通过页面的返回值的不同获取了想要的数据


基于布尔型的盲注并不是所有的注入点都适用,因为有时候只是执行了sql语句,不会有任何输出。这时候就需要用到基于时间的盲注技术了。它主要利用页面的返回时间来判断是否正确,如果正确,则用数据库的sleep函数,使数据库延迟指定的秒数后再返回数据。

它模仿的是一个bug反馈的页面,功能很简单,只能反馈问题,而且你不能查看你反馈的内容。

首先正常输入一些内容,看下如果正常提交网页会显示什么内容。

提示感谢您的反馈。然后根据 SQL注入原理与实践 这个实验中判断存在注入的方法,在输入框输入一个单引号测试,如:

同样提示感谢您的反馈,如上图。这样就能说明没有sql注入了吗?当然不是,我们猜测他可能是在执行了sql语句后,不管有没有正确执行,都会弹出这个提示,那么我们如何来进行SQL注入测试呢?这次我们先不直接查看它的源码,因为在实际测试中,一般都是在没有源码的情况下进行注入,所以首先我们需要的就是猜测它的sql语句的写法,

根据页面的三个输入框,我们猜测它的sql语句是这么写的:

insert into table (`nikename`, `email`, `info`) values (‘username’, ‘emai’, ‘info’)

但是由于不管sql语句有没有正确执行,都会返回同一个提示信息,所以,对于上面学习到的基于布尔型的盲注就没用了,但是,我们可以利用if 和sleep的组合来判断。如果if成立,则让数据库sleep 指定秒数。可以选择任意一个输入框进行测试,我这里选择反馈内容的输入框,因为一般来说,其他2个字段都会有长度的限制,而对于用户留言之类的不会有长度限制,即使有也会比较长。

根据上面的猜测,构造如下sql语句,来猜测当前的数据库名是不是”sqli”,其他2个输入框随便写,不要出现特殊字符即可。

1' and if((select database()) = 'sqli', sleep(5), sleep(0)))-- +

If 的语法为:

IF(Condition,A,B),当Condition为TRUE时,返回A;当Condition为FALSE时,返回B。所以如果当前的数据库为sqli的话,数据库会执行sleep(5),所以页面的返回时间应该大于5秒。

先不点提交,在网页的空白处鼠标右击,选择查看元素,也可以按快捷键F12呼唤出开发人员工具面板,在弹出来的开发人员工具面板,选择网络用来观看页面的返回时间。

这里为什么要用-- +呢?

在mysql中,-- 是注释符,需要注意的是,--后面必须跟上一个空格,mysql服务器才认为它是注释,否则不认为是注释,那么为什么后面还会有一个+呢? 因为在写程序的时候,接受参数的同时一般会调用trim这样的函数去掉两边的空格,所以为了避免--后面的空格被删掉,加一个+。

然后点击提交。

可以看到,页面的返回时间大于5秒,然后我们可以用同样的语句,然后改变sleep的值,看是不是网页的返回时间也会改变。在提交前先清空记录。

那么如何获取数据呢?

步骤跟前面一样,只是把where语句变成了if语句,注入语句还是一样的,大致原理一样。

1' and if(ascii(substring((select schema_name from information_schema.schemata LIMIT 0,1),1,1)) = 105, sleep(5), sleep(0)))-- +

这条语句判断第一个数据库库名的第一个字符的ASCII码是否为105,是则sleep(5),否则sleep(0),即什么都不干。

提交后,页面的返回时间大于5秒。

这里的SQL语句执行顺序为:

  1. 首先执行子查询,返回一个数据库名。
  2. 然后substring截取返回的数据库名的一个字符,这里substring的第二个参数为2,所以截取第二个字符。
  3. 利用ascii函数来查询截取的字符的ascii码。
  4. 然后if 判断表达式是否成立,如果表达式成立,则执行sleep(5),如果不成立,则执行sleep(0)。这里的表达式就是 ascii返回的数值跟后面的等于100 比较,比如ascii返回了110,然后就会根据110 =110 的返回值来确定是执行sleep(5)还是sleep(0)。

然后就可以像前面一样,修改子查询的返回值以及substring的第二个参数来获取数据。


如果页面上显示数据的报错信息,那么可以直接使用报错的方式把想要的信息爆出来。而不用像上面2种方法一样那么麻烦。

在mysql中我们可以使用如下的语句报错:

select 1,2 union select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x;

在phpmyadmin中执行结果如下:

可以看到,虽然SQL语句执行出错了,但是依然输出了我们想要的值,之所以会这样,是因为mysql的bug导致的,主要是rand和group+by的冲突。

对于基于报错的盲注,就是“套公式“一样的,只要把我们想要执行的sql指令”套到公式中“,即可获取数据。这个公式就是and (select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

我们只要把这个语句插到存在sql注入的地方,闭合括号等,使sql语句没有语法错误,就会通过报错信息返回当前数据库的版本。

所以可以构造如下注入语句:

1' and (select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a))-- +

输出当前数据库版本。前面已经说过,这种方法获取数据就是套公式,我们只把想要执行的sql语句替换select concat(0x7e,version(),0x7e) 即可。

concat函数用来连接字符串,其中0x7e即 “~ “。concat会把十六进制的值转换成字符。select concat(0x7e,version(),0x7e)的结果为:

之所以在version前后都添加一个“~“,是为了跟页面的其他信息区别,方便查找我们想要的信息,0x7e可以改成其他字符的ascii码的十六进制数值,一般不用字母和数字。为了避免和返回值混淆。

如果我们想要获取当前第一个数据库的库名,可以把语句改成如下:

1' and (select 1 from(select count(*),concat((select (select (select concat(0x7e,schema_name,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit0,1),floor(rand(0)*2))x from information_schema.tables group by x)a))-- -

返回第一个数据库名,然后依次修改刚才我们修改的子查询的limit偏移,获取第二个数据库名。

返回结果如下:

其他步骤与实验步骤一、二相同,只是通过错误信息一次可以获取库名、表名、列名、字段以及字段值而不用像步骤一、二一样需要按位获取。

SQL注入漏洞-MySQL盲注相关推荐

  1. sql注入学习——布尔盲注

    前言:之前通过前九关学习到了回显注入.报错注入等一些方法,这次就来详细的学习布尔盲注. 首先来了解一下盲注的概念 盲注是注入的一种,指的是在不知道数据库返回值的情况下对数据中的内容进行猜测,实施SQL ...

  2. SQL注入学习——Bool盲注详解 sqli-labs(Less 8)

    文章目录 前言: 一.Bool盲注常用的函数: 二.Less8 布尔型单引号GET盲注 1.查数据库版本 2.猜解数据库的长度 3.猜数据库名字 4.猜解表名 5.猜解字段名 6.猜解数据 三.脚本注 ...

  3. SQL注入之时间盲注 和 报错注入(sql-lab第一关为例)

    什么是时间盲注 时间盲注指通过页面执行的时间来判断数据内容的注入方式,通常用于数据(包含逻辑型)不能返回到页面中的场景,无法利用页面回显判断数据内容,只能通过执行的时间来获取数据. 时间盲注的过程 1 ...

  4. sql注入学习——时间盲注

    前言:之前通过前九关学习到了回显注入.报错注入.布尔盲注等一些方法,这次就来详细的学习时间盲注. 在上一篇博客中,了解了布尔盲注,其实布尔盲注和时间盲注大致相同,注入原理是一致的,区别就是一个还是有回 ...

  5. SQL注入之布尔盲注——sql-lab第八关

    布尔盲注简介 什么是盲注 盲注其实是sql注入的一种,之所以称为盲注是因为他不会根据你sql注入的攻击语句返回你想要知道的错误信息. [之前在做联合注入第一关的时候,用union select语句注入 ...

  6. SQL注入原理-时间盲注

    小伙伴们大家好!本期为大家带来的是SQL注入原理之时间盲注. 目录 使用环境 常见函数与语句 sleep()函数 if语句 substr()函数 ord()函数 length()函数 实战演示 1.判 ...

  7. SQL注入学习——时间盲注详解 sqli-labs(Less 9)

    文章目录 前言: 一.基础知识 1.时间盲注简介: 2.时间盲注常用的函数: 二.Less9 基于时间的单引号盲注 1.判断数据库名的长度: 2.猜测数据库: 3.判断表名的长度 4.猜测 secur ...

  8. web安全入门之SQL注入-时间型盲注

    SQL注入之时间型盲注 1.时间型盲注 时间型盲注条件极为苛刻,不管输入什么,WEB页面回显相同的结果,此时我们无法通过报错型注入以及布尔型盲注来爆数据,此时数据在交互完成以后目标网站没有正确和错误的 ...

  9. SQL注入原理-布尔盲注

    小伙伴们大家好,今天为大家带来的使SQL注入原理之布尔盲注. 目录 布尔盲注使用的环境 常用函数与语句 substr()函数 ord()函数 length()函数 实战演示 1.判断是否存在注入点 2 ...

  10. 墨者学院-SQL注入漏洞测试(报错盲注)

    继续攻克SQL注入类题目啦!!!今天的题目环境提示比较明显,也是比较常见的一种SQL注入漏洞类型,继续储备知识,撸起袖子加油干!!! 附上题目链接:https://www.mozhe.cn/bug/d ...

最新文章

  1. SparkStreaming整合Kafka(Offset保存在zookeeper上,Spark2.X + kafka0.10.X)
  2. LSM树(Log-Structured Merge Tree)存储引擎
  3. UVa1339 - Ancient Cipher
  4. 成功解决mxnet.base.MXNetError: C:\Jenkins\workspace\mxnet-tag\mxnet\3rdparty\dmlc-core\src\io\local_file
  5. [leetcode] 144.二叉树的前序遍历
  6. win10自带虚拟机安装CentOS7系统(转)
  7. netbean的安装及jdk安装和环境变量设置
  8. 具体案例 快速原型模型_快速原型模型
  9. python化学公式配平_最简单易懂的化学方程式的配平方法
  10. css过度效果及动画效果
  11. python文件比较,判断两个文件是否相同
  12. hdu 3853 LOOPS
  13. PDF如何修改文件字体大小
  14. 网络知识入门,什么是以太网包,TCP/IP包,MAC头部,IP头部以及TCP头部(六)
  15. FPGA问题记录之:Warning (13024): Output pins are stuck at VCC or GND
  16. qsnctf nice cream wp
  17. 检测到目标站点存在javascript框架库漏洞 (随手记录)
  18. 前端:jQuery使用eq的作用
  19. 2014H-Star初赛题解
  20. python如何做辅助线_作辅助线的方法和技巧

热门文章

  1. 强烈推荐10本程序员必读的书
  2. SGM—代价聚合学习笔记
  3. 【智能无线小车系列二】车体的组装
  4. 计算机的排版方法,计算机编辑排版系统及其方法
  5. 全国各地电信DNS服务器地址:
  6. IDEACPU占用100%_卡顿 解决办法
  7. DeviceOrientation ---实现指南针
  8. 跨浏览器书签导入实例演示,更好的管理书签!
  9. 我的世界服务器哪个有自动铺路,我的世界自动铺路指令是什么
  10. 再说setlocale——关于区域名称