MySQL和SQL字段截短漏洞

当前的Web开发者中肯定有不少人没有注意到作者所提到的这两个问题的。
第一个问题是这样的,MySQL默认有一个配置参数 max_packet_size,这个参数是用于限制MySQL客户端和MySQL服务器端数据通信的数据包大小,MySQL的默认配置是1MB。如果客户端发送的数据超过了1MB,则MySQL服务器端会忽略掉这个请求数据。作者接下来举了两个利用这个缺陷的例子,第一个是利用超长数据来使MySQL的日志记录程序失效,第二个是在PHP+MySQL的环境下,PHP的Session清理程序会由于一次发送的清理session数据的请求数据包超过max_packet_size的限制,而导致清理session失败。

而实际上,由于很多PHP+MySQL的程序都会运行用户上传附件之类,而一般的PHP+MySQL的上传附件限制都是大于1MB的,所以PHP的程序开发人员一般是会去修改max_packet_size的值为大于1MB。这就给我们的漏洞利用带来了一定的麻烦,毕竟在当前的网络状况下,构造1MB多的数据去上传还是可以忍受的。但是太大的数据量就比较考验我们的耐心了,呵呵。

第二个问题就比较严重了,MySQL对于超过字段长度的数据插入操作会进行默认的字符串截短。例如一个字段定义的长度为10,如果插入的字符串长度超过10,MySQL会将长度超过10的部分字符串自动舍去后插入到数据表中。默认配置条件下,MySQL会产生一个警告信息,但是这个警告信息不会被Web应用程序捕获到。所以,从表面上来看,超长数据也是可以“成功”插入数据表的。作者在下面举的这个例子就很有代表性了,首先是一个场景假设:

The application is a forum where new users can register
The administrator’s name is known e.g. ‘admin’
MySQL is used in the default mode
There is no application restriction on the length of new user names
The database column username is limited to 16 characters
用户如果尝试注册一个用户名为admin的用户,会由于Web应用程序中的isAlreadyRegistered函数的校验而注册失败。

SELECT * FROM user WHERE username='admin'
但如果用户使用用户名’admin x’来注册(注意admin和x之间有11个空格),则注册流程会是这样的:

isAlreadyRegistered函数会使用上面的SQL语句来检查user表中是否存在相同用户名的用户,查询结果肯定是不存在的。那么用户注册成功!

实际上,真正插入到user表中的用户名是’admin’!也就是说,MySQL不仅会截短超过长度限制部分的字符串,也会对字符串头尾的空白字符进行截短!所以,在user表中,现在存在了两个admin用户!

接下来,用户登陆,他使用的是用户名admin,密码是他刚才设置的’admin x’的密码。假设Web应用程序的登陆认证和授权函数是这样的一段代码:

$userdata = null;
if (isPasswordCorrect($username, $password)) {
$userdata = getUserDataByLogin($username);
...
}
其中isPasswordCorrect函数使用的SQL语句为:

SELECT username FROM users WHERE username = ? AND passhash = ?
getUserDataByLogin函数使用的SQL语句为:

SELECT * FROM users WHERE username = ?
可以看得出,上面的语句使用了预编译的SQL语句,是无法实施SQL注入的。但是由于MySQL的默认字段截短策略,isPasswordCorrect函数会成功执行并返回用户名admin,接下来的getUserDataByLogin也会正确执行,返回的结果虽然是一个数组,但是Web应用程序一般是取返回数组中的第一个结果,也就是真正的管理员用户admin的所有数据!

怎么样,不用SQL注入,一样拿到管理员权限!

后记:经过测试,上面的漏洞利用过程在MySQL 4中是确实存在并且可以利用的。但是在MySQL 5中,本机测试失败。失败的关键就在于MySQL 5对于超过字段长度限制的字符串插入会报错,并停止字符串插入操作。

附:MySQL 4的测试过程

mysql> select * from tb_sqltest where name='jason';
+----+-------+--------+------+------+------+------+
| id | name | remark | time | col1 | col2 | col3 |
+----+-------+--------+------+------+------+------+
| 1 | jason | NULL | NULL | NULL | NULL | NULL |
+----+-------+--------+------+------+------+------+
1 row in set

mysql> select * from tb_sqltest where name='jason x';
Empty set

mysql> insert into tb_sqltest (id,name) values (2,'jason x');
Query OK, 1 row affected

mysql> select * from tb_sqltest where name='jason';
+----+-------+--------+------+------+------+------+
| id | name | remark | time | col1 | col2 | col3 |
+----+-------+--------+------+------+------+------+
| 1 | jason | NULL | NULL | NULL | NULL | NULL |
| 2 | jason | NULL | NULL | NULL | NULL | NULL |
+----+-------+--------+------+------+------+------+
2 rows in set

http://hi.baidu.com/isbx/blog/item/08ef48547ef1ad58574e00bf.html

加支付宝好友偷能量挖...


评论(0)网络
阅读(76)喜欢(0)SQL及数据库