SQL注入:宽字节注入(GBK双字节绕过)

宽字节注入的作用是非常大的,实际上在代码层的防御一般不外乎两种,一是反斜杠转义,而就是replace替换成空字节,之前的替换空字节也就是各种bypass,想办法绕过代码层定制的黑名单,那么,转义的话,就不像替换那么好绕了

要么不用被转义的字符,要们就只能想办法构造了,这时候,便有了一个很牛逼的构造方法,GBK双字节绕过,实际也算是宽字节注入,这个可以看看一本书

  • 《双字节编码 php的隐形杀手》

宽字节注入的方法也很简单,就是编码,我们一点点分析

假设一个URL存在注入但是有addslashes,mysql_real_escape_string,mysql_escape_string等等函数实现转义就比如如下代码

function check_addslashes($string)

{

 $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash

 $string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash

 $string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash

 

 

 return $string;

}

定义了个一个过滤函数,然后使用它

if(isset($_GET['id']))

{

$id=check_addslashes($_GET['id']);


mysql_query("SET NAMES gbk");

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

$result=mysql_query($sql);

$row = mysql_fetch_array($result);

这儿形成的URL应该是

http://www.xxx.com/index.php?id=1

我们敲单引号会被过滤,但又必须要敲,那么怎么办呢?这时候就利用GBK双字节注入

我们在后边这么构造URL

http://www.xxx.com/index.php?id=1%df'and 1=2 union select 1,2,3%23

这样的话id的参数传入代码层,就会在’前加一个\,由于采用的URL编码,所以产生的效果是

%df%5c%27

关键就在这,%df会吃掉%5c,形成一个新的字节,举个例子就是%d5遇到%5c会把%5c吃掉,形成%d5%5c,这个编码经过代码解码后会形成一个汉字“誠”

说的再形象点,我来画个画解决

[O0OI0NEL40WHF38G(_3B[N

基本解释:

因为%df的关系,\的编码%5c被吃掉了,也就失去了转义的效果,直接被带入到mysql中,然后mysql在解读时无视了%a0%5c形成的新字节,那么单引号便重新发挥了效果

那么真正的原因是什么呢?

GBK双字节注入到底是怎么来的呢?

宽字节注入发生的位置就是PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码。

http://www.xxx.com/index.php?id=1%df'and 1=2 union select 1,user(),3%23

按照这个参数,我们在页面输出$sql,看看最终传入到mysql中的语句构造

SELECT * FROM users WHERE id='1運' and 1=2 union select 1,user(),3#' LIMIT 0,1

我们可以看到,单引号前并没有\,而是多了一个汉字

那么这句传入到mysql中运行的结果是什么呢?

6R72BAU$1WYLG_L$SVXRY]Y

输出了user()

这是为什么呢?为什么在传入到mysql时,%df%5c%27会变成運’

我们之前强调过了,宽字节注入的发生位置在PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码。

就是这一次编码,发生了这一切

当一个Mysql连接请求从客户端传来的时候,服务器认为它的编码是character_set_client,
然后会根据character_set_connection把请求进行转码,从character_set_client转成character_set_connection,
然后更新到数据库的时候,再转化成字段所对应的编码
如果使用了set names指令,那么可以修改character_set_connection的值,
也同时会修改character_set_client和character_set_results的值
当从数据库查询数据返回结果的时候,将字段从默认的编码转成character_set_results

这儿会产生什么呢?

我们追踪下数据的变化过程

%df%27===>(addslashes)====>%df%5c%27====>(GBK)====>運’

用户输入==>过滤函数==>代码层的$sql==>mysql处理请求==>mysql中的sql

mysql_query("SET NAMES gbk");

当这行代码在代码层被写入时,三个字符集(客户端、连接层、结果集)都是GBK编码。

那么便会发生如上的情况

有人会说,那直接试用UTF-8编码呢,很多网站就是这么做的,但是为了避免用户输入的GBK字符形成乱码,网站真正的做法是会将一些用户提交的GBK字符使用iconv函数(或者mb_convert_encoding)先转为UTF-8,然后再拼接入SQL语句。

%e5%5c%27====(addslashes)====>e55c5c5c27====(iconv)====>e98ca6\\’

上面的UTF-8的SQL代码,但是如果转成GBK时,e98ca6\\’实际是錦’

那么,mysql中又是如何处理sql语句中的编码的呢?

我们传统意义上说的编码其实是指字符集,它包括两个方面,一个是存储的字符,另外一个是映射关系,也就是真正的编码。各种字符集的存储的字符都是差不多的,就那么几个字符,而编码却是各不相同,是真正发挥威力的地方。
原来的系统数据存储采用gbk字符集,因为版本原因,升级后系统必须采用latin1字符集来存储,所以新的数据库中存储的是gbk的字符,而使用的是latin1的编码。所以这种数据只能在需要显示gbk的页面上正确显示,在显示其他字符集的地方就会是乱码。

这儿mysql对于那个新形成的字符的处理,问了下phithon大牛,很快便给了答案,在他写的一篇文章中有类似的解释—-遇到一个有趣的逻辑漏洞》

到这儿,基本就说的差不多了,还剩最后的一部分了,哪些能形成宽字节呢?

GBK双字节编码:一个汉字用两个字节表示,首字节对应0x81-0xFE,尾字节对应0x40-0xFE(除0x7F),刚好涵盖了对应的编码0x5C。

这儿我给个GBK表,便于大家查询

GBK编码表

6、安全方案

对于宽字节编码,有一种最好的修补就是:

(1)使用mysql_set_charset(GBK)指定字符集

(2)使用mysql_real_escape_string进行转义

原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?

就是使用mysql_set_charset进行指定。

上述的两个条件是“与”运算的关系,少一条都不行。

参考:

Advertisements
SQL注入:宽字节注入(GBK双字节绕过)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

w

Connecting to %s