0、前言php
最近要为了自动化审计搜集全部PHP漏洞,在整理注入的时候,发现宽字节注入中使用iconv形成的漏洞原理没有真正搞懂,网上的文章也说得不是很清楚,因而看了荣哥(lxsec)之前发的一篇http://www.91ri.org/8611.html,加上咱们两我的的讨论,最终有了这一篇深刻的研究成果。html
一、概述mysql
主要是因为使用了宽字节编码形成的。,典型情景是网站使用GBK编码,数据库使用UTF-8编码sql
什么是字符集?数据库
计算机显示的字符图形与保存该字符时的二进制编码的映射关系。浏览器
如ASCII中,A(图形)对应编码01000001(65)。安全
对于MYSQL数据库来讲,涉及字符集的地方大体分为存储和传输时,即:服务器
(1)存储在服务器端的数据是何种编码函数
(2)客户端和服务器交互的时候数据传输使用的编码。post
二、MYSQL服务器端存储字符集
在MYSQL服务器端进行数据存储时,容许在如下的级别设置字符集:
(1)服务器端字符集(character_set_server)
(2)库字符集
(3)表字符集
(4)字段字符集
优先级为:字段----->表------->库-------->服务器
对应的语法是:
三、客户端与服务器交互数据传输的字符集
存储时的字符集已经肯定了,不会影响交互阶段的字符集。
在MYSQL中,还有一个中间层的结构,负责客户端和服务器之间的链接,因此称为链接层。
交互的过程以下:
(1)客户端以某种字符集生成的SQL语句发送至服务器端,这个“某种字符集”实际上是任意规定的,PHP做为客户端链接MYSQL时,这个字符集就是PHP文件默认的编码。
(2)服务器会将这个SQL语句转为链接层的字符集。问题在于MYSQL是怎么知道咱们传过来的这个SQL语句是什么编码呢?这时主要依靠两个MYSQL的内部变量来表示,一个是character_set_client(客户端的字符集)和character_set_connection(链接层的字符集)。可使用show variables like ‘character_set_%’ ;进行查看。
能够看到,这里的客户端字符集为GBK,链接层字符集也是为GBK。
二者相同,就不会有问题,若是不一致,就会出现乱码问题了。
使用MYSQL中的set命令能够对这些内部变量作设置,如修改客户端编码为UTF-8;
set character_set_client = UTF-8
(1)服务器将转换好的SQL语句,转为服务器内部编码与存储在服务器上的数据进行交互
(2)服务器处理完以后,将结果返回给客户端,仍是转为服务器认为客户端能够认识的编码,如上图的GBK,使用character_set_results来肯定返回客户端的编码。
平时在PHP中写的set names UTF-8至关于下面三条同时执行:
(1)set character_set_client = UTF-8
(2)set character_set_connection = UTF-8
(3)set character_set_results = UTF-8
四、乱码问题原理
设置三个字符集相同,这也就不会出现乱码的真正原理。网页上有时会出现乱码是由于PHP动态文件将数据打印到浏览器的时候,浏览器也会按照必定的字符集进行判断,若是PHP的响应数据编码和浏览器编码一致,就不会出现乱码,不然就出现乱码。能够经过在PHP中使用header()来指定这个响应数据的编码。
五、宽字节注入原理
有三种形式:
(1)情景一:在PHP中使用mysql_query(“set names GBK”);指定三个字符集(客户端、链接层、结果集)都是GBK编码。
情景代码:
提交:http://127.0.0.1/foo.php?bar=admin%df%27
这时,发生以下转换:
%df%27=====(addslashes)======>%df%5c%27======(GBK)======>運’
带入sql为:
Select password from user where bar=‘運’
成功将单引号闭合。为了不漏洞,网站通常会设置UTF-8编码,而后进行转义过滤。可是因为一些不经意的字符集转换,又会致使漏洞。
(2)情景二:
使用set names UTF-8指定了UTF-8字符集,而且也使用转义函数进行转义。有时候,为了不乱码,会将一些用户提交的GBK字符使用iconv函数(或者mb_convert_encoding)先转为UTF-8,而后再拼接入SQL语句。
情景代码:
咱们能够看到,为了使得SQL语句中的字符集保持一致,通常都会使用iconv等字符集转换函数进行字符集转换,问题就是出在了GBK向UTF-8转换的过程当中。
提交:http://127.0.0.1/foo.php?bar=%e5%5c%27
变换过程:(e55c转为UTF-8为e98ca6)
e55c27====(addslashes)====>e55c5c5c27====(iconv)====>e98ca65c5c27
能够看到,多出了一个5c,将转义符(反斜杠)自己转义,使得后面的%27发挥了做用。
测试以下:
(3)情景三:使用iconv进行字符集转换,将UTF-8转为GBK,同时,set names字符集为GBK。提交%e9%8c%a6便可。
这个情景的大前提是先编码后转义:
e98ca6====(iconv)=====>e55c=====(addslashes)====>e55c5c
一样能够多出一个反斜杠进行利用,在此再也不详述,由于漏洞条件比较苛刻。
六、安全方案
对于宽字节编码,有一种最好的修补就是:
(1)使用mysql_set_charset(GBK)指定字符集
(2)使用mysql_real_escape_string进行转义
原理是,mysql_real_escape_string与addslashes的不一样之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,可是这个“当前字符集”如何肯定呢?
就是使用mysql_set_charset进行指定。
上述的两个条件是“与”运算的关系,少一条都不行。
测试;
输出: