背景:在sqli-labs第五关时,即使sql语句构造成功页面也没有回显出我们需要的信息,看到了有使用双查询操作造成报错的方式获得数据库信息,于是研究了一下双查询的报错原理,总结了探索的过程,整理出此文希望可以帮到感兴趣的人。
sqli-labs闯关游戏下载地址:https://github.com/Audi-1/sqli-labs
双查询报错注入需用到四个函数和一个group by语句:
group by ... --->分组语句 //将查询的结果分类汇总
rand() --->随机数生成函数
floor() --->取整函数 //用来对生成的随机数取整
concat() --->连接字符串
count() --->统计函数 //结合group by语句统计分组后的数据
用sqli-labs中的数据库为示例,首先先了解一下子查询的概念。
子查询又称为内部查询,子查询允许把一个查询嵌套在另一个查询当中
简单的来说就是一个select中又嵌套了一个select,嵌套的这个select语句就是一个子查询。
连接数据库后用子查询测试一下:
mysql> SELECT concat("test:",(SELECT database()))as a;执行查询操作时,子查询先开始,所以SELECT database()先执行,然后查询到当前数据库名称”security“,并将其传给concat函数,concat函数在对字符进行连接,于是显示出图上的结果。
然后是rand()函数,其作用是生成一个大于0小于1的随机浮点数,如下:
floor()函数的作用是对传入的参数取整,这里将rand()生成的随机数做处理进行取整,由于rand()生成的值取整结果只能为0,所以我们这里做一点处理,使其生成一个大于0小于2的随机值,并对其取整:
mysql> SELECT floor(rand()*2);结果要么为0要么为1
接下来结合子查询,显示出数据库信息:
mysql> SELECT concat((SELECT database()), floor(rand()*2)) from users;由于users表中只有13条数据,所以这里返回了13条数据
在注入中,我们不知道库名表名,往往借助information_schema这个库进行猜解
其中information_schema.schemata中包含了mysql的所有库名,information_schema.tables中包含了所有的表名,information_schema.columns中包含了所有的列名。
示例如下:(我电脑有7个数据,所以返回了7条数据)
现在加上group by语句对返回的数据进行分组处理
mysql> SELECT concat((SELECT database()), floor(rand()*2))as a from information_schema.schemata group by a;from前的as a,是为concat((SELECT database()), floor(rand()*2))这一串取了个别名,后面使用group by分组时就不用打那么长一串了,直接使用别名就像。
到这里都是基础知识的铺垫,而且前面所有的查询操作都是返回库名和“0、1”的拼接结果,然而在sqli-labs第五关这样网页无回显的环境下,我们是看不到任何的信息的,所以接下来才是正题,我们要利用count函数和上面的操作构成mysql内部错误,然后通过报错的提示获得我们想要的信息。
(上面的database()函数在实际注入中也可以换成其他的,如version(),具体看你想要通过报错获得的信息)
这里增加一个聚合函数count,构造的语句如下:
mysql> SELECT count(*),concat((SELECT database()), floor(rand()*2))as a from information_schema.schemata group by a;这里利用count(*)对前面的返回数据进行统计,由于group by 和随机数的原因,有可能会出现重复的键值,当键值重复时就会触发错误,然后报错,由于子查询在错误发生之前就已经完成,所以子查询的内容会随着报错信息一起显示出来: