MySQL条件等号的异常

敬原作者(应该是原作者,如有错误,请指正)
来自Wupco’s Blog

##根源
MySQL WHERE语句中,等号=通常作为判断条件,然而这个等号却是弱类型的判断,如果忽略了这一点,很容易造成安全问题,以下是原作者整理的匪夷所思的相等

##字符型和数字型
MySQL的等号同PHP一样,对等号两端不同的数据类型都有强制类型转换,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> use mysql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

mysql> select user name from user where '1'=1;
+------------------+
| name |
+------------------+
| root |
| root |
| root |
| debian-sys-maint |
| root |
+------------------+
5 rows in set (0.00 sec)

但却没有PHP的弱类型判断(此处本人不明白,为什么以下语句能说明这句话,望大神指点

1
2
mysql> select user name from user where 0.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999=1;
Empty set (0.00 sec)

这个的应用范围很局限,但是又不得忽视,所以开发者在字段的数据类型的选择以及SQL语句的构造方面,要多多注意,防止等号两边类型不相同导致的漏洞。

1
2
3
4
5
6
7
8
9
10
11
mysql> select user name from user where '1d'=1;
+------------------+
| name |
+------------------+
| root |
| root |
| root |
| debian-sys-maint |
| root |
+------------------+
5 rows in set, 1 warning (0.01 sec)

这个我在本地实验没成功

##尾空格
MySQL等号对字符尾部的空格做忽视的处理,所以我们可以构造一个'abc'='abc [space]的效果,绕过某些特定串整体检查的WAF,不过相对来说,这个也比较鸡肋,因为很少有用的字符串直接对比来过滤某些敏感串的,直接用trim函数来避免。

1
2
3
4
5
6
7
8
9
10
mysql> select user from user where user='root       ';
+------+
| user |
+------+
| root |
| root |
| root |
| root |
+------+
4 rows in set (0.00 sec)

##UNICODE字符集相似字符
Unicode字符集所有字符:[点我](http://unicode-table.com/cn/ "点我")
其中有些相似字符,比如下面这个与a相似。
7.png
这会造成什么后果呢?

1
2
3
4
5
6
7
8
9
10
11
mysql> select user from user where 'a'='à';
+------------------+
| user |
+------------------+
| root |
| root |
| root |
| debian-sys-maint |
| root |
+------------------+
5 rows in set (0.00 sec)

看到了吧,MySQL的等号认为这些相似字符也是相等的,这样的话,我们就可以完全绕过对敏感串的过滤,前提是在UTF8编码下。

##新型万能密码
username='admin'=''
这是有2个等号,然后计算顺序从左到右
先计算username='admin',
如果没有这个记录,则返回false,
然后计算false='',
结果就成了true了,
所以这个就相当于where 1
达到了万能密码的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select user from user where user='admin';
Empty set (0.00 sec)

mysql> select user from user where user='admin'='';
+------------------+
| user |
+------------------+
| root |
| root |
| root |
| debian-sys-maint |
| root |
+------------------+
5 rows in set (0.00 sec)