这篇文章很有意思,以前注意过,但没仔细考虑过用来注入,或许就象王猛说的:
文章来自寂寞hacker,http://hi.baidu.com/isbx/blog/item/08ef48547ef1ad58574e00bf.html:
当前的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