前言
在mysql用于转义的函数中有addslashes,mysql_real_escape_string,mysql_escape_string等等,还有另一种情况magic_quote_gpc,但是高版本PHP这一特征将被去除。
首先,注入宽字节HTML页面编码无关,作者曾经看到过
<meta charset=utf8>放弃尝试是一种误解,SQL注入不是XSS。虽然编码的原因相似,但发生在不同的地方。
许多在线材料说,程序使用宽字节来处理程序,但没有指出具体的程序。本文介绍了具体漏洞的原理和简单的使用。我们在这里限制的语言是PHP5.4,数据库MYSQL5.6。
一些涉及的概念
字符集和字符序列
字符(character)是字符集的组成(character set)基本单位。给字符一个值(encoding)确定字符集中的位置。
字符序(collation)指同一字符集中字符之间的比较规则。
UTF8
由于ASCII只有128个字符表示,所以网络世界的标准是使用UNICODE编码,但是用ASCII使用表示的字符UNICODE不高效。因此,中间格式字符集出现,称为通用转换格式和UTF(Universal Transformation Format)。
宽字节
GB2312、GBK、GB18030、BIG5、Shift_JIS这些都是常说的宽字节,其实只有两个字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象。
MYSQL字符集转换过程
1. MySQL Server收到请求时,请求数据将从character_set_client转换为character_set_connection;
2. 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集的确定方法如下:
• 使用每个数据字段CHARACTER SET设定值;
• 如果上述值不存在,则使用相应的数据表DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
• 如果上述值不存在,则使用相应的数据库DEFAULT CHARACTER SET设定值;
• 如果上述值不存在,则使用character_set_server设定值。
将操作结果从内部操作字符集转换为内部操作字符集character_set_results。
重点:注入宽字节的位置是PHP发送请求到MYSQL使用时间字符集character_set_client编码设置值。
PHP测试代码:
可以成功执行!
URL解码sql=rootß’ or 1=1#
解析过程:
$_GET[‘sql’] 经过 addslashes编码后带入‘\’1、root?\' or 1=1#
2、带入mysql用于处理gbk字符集
?\ -> 运 成功吃了\
' -> ‘ 单引号成功闭合
执行插入sql语句。
怎么吃的:
GBK编码,其编码范围为0×8140~0xFEFE(不包括xx7F),在遇到?(ascii(223)) >ascii(128)自动拼接\,因此吃掉‘\’,而且, 小于ascii(128)字符保留。
补充:
GB2312是被GBK兼容,高位范围是0xA1~0xF7,低位范围是0xA1~0xFE(0x5C不在这个范围内),不能用编码吃\。
其他宽字符集也有相同的分析过程,要吃\,只需要在低位包含正常内容0x5c就行了。
安全过滤
使用了上面的代码mysql_query(“set names gbk”)设置编码,其实是mysql中是推荐mysql_set_charset(“gbk”);编码设置函数,这两个函数的功能大致相似,***区别在于后者会被修改mysql对象中的mysql->charset属性是设置的字符集。
同时配套的过滤函数为mysql_real_escape_string()。上述代码中列出了几个过滤函数,它们之间的区别是mysql_real_escape_string()会根据mysql对象中的mysql->charset传入的字符串可以根据当前的字符集进行过滤。
具体差异可参考:http://www.laruence.com/2010/04/12/1396.html
同理可得
从上面可以看出,宽字节注入是由转编码形成的,具有转编码功能的函数也成为漏洞的原因。
转码函数
mb_convert_encoding()
iconv()
以下用iconv()演示并修改上述代码:
编码分析的过程也可以成功执行。
总结漏洞的原因:
代码一
1、使用不安全的字符集设置函数和过滤函数。
2、漏洞发生在PHP请求mysql时使用character_set_client一次转码值。
代码二
1、使用推荐的设置函数和过滤函数。
2、解析错误发生在iconv()函数转码时,GBK转向UTF8吃掉了“\”
3、PHP请求mysql转码安全。
另外:
改变编码方向时$user = iconv(‘UTF-8′,’gbk’,$user);
通过访问http://localhost/xl.php?sql=root锦可以带一个\,然后注释掉单引号。
注入需要两个参数。
例如:
http://localhost/xl.php?sql=root锦¶= or 1=1#总结:
注入宽字节后跟HTML页面编码无关。
Mysql建议使用编码和过滤函数mysql_real_escape_string(),mysql_set_charset()。
即使使使用了安全的设置函数,转编码函数也会导致宽字节注入。