深入探究宽字节注入漏洞与修补原理

通俗

在GBK编码时,mysql会认为两个字符是一个汉字(在前一个字节的ascii码大于128的情况下)。而经过转义之后的单引号会变为\’,即%5c%27。构造id=1%df%27%23 ,在经过转义传递给mysql时,就是id=1%df%5c%27%23,mysql在解析时,会认为%df%5c是一个汉字,而%27就会闭合掉原本sql语句中的(左)单引号,即select xxx from xxx where id='%df%5c'#',%23用于注释掉原本sql语句中的(右)单引号。这就是宽字节注入的原理。

 

专业

 

0、前言

最近要为了自动化审计搜集所有PHP漏洞,在整理注入的时候,发现宽字节注入中使用iconv造成的漏洞原理没有真正搞懂,网上的文章也说得不是很清楚,于是看了荣哥(lxsec)以前发的一篇http://www.91ri.org/8611.html,加上我们两个人的讨论,最终有了这一篇深入的研究成果。


1、概述

主要是由于使用了宽字节编码造成的。

什么是字符集?

计算机显示的字符图形与保存该字符时的二进制编码的映射关系。

如ASCII中,A(图形)对应编码01000001(65)。

对于MYSQL数据库来说,涉及字符集的地方大致分为存储和传输时,即:

(1)存储在服务器端的数据是何种编码

(2)客户端和服务器交互的时候数据传输使用的编码。

 

2、MYSQL服务器端存储字符集

在MYSQL服务器端进行数据存储时,允许在以下的级别设置字符集:

(1)服务器端字符集(character_set_server)

(2)库字符集

(3)表字符集

(4)字段字符集

优先级为:字段----->表------->库-------->服务器

对应的语法是:

    Create table test(
    name varchar(20) charset gbk,
    number varchar(10),
    age int
    )engine=innodb charset=utf-8 ;

 


3、客户端与服务器交互数据传输的字符集

存储时的字符集已经确定了,不会影响交互阶段的字符集。

在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

 

4、乱码问题原理

设置三个字符集相同,这也就不会出现乱码的真正原理。网页上有时会出现乱码是因为PHP动态文件将数据打印到浏览器的时候,浏览器也会按照一定的字符集进行判断,如果PHP的响应数据编码和浏览器编码一致,就不会出现乱码,否则就出现乱码。可以通过在PHP中使用header()来指定这个响应数据的编码。

 

5、宽字节注入原理

有三种形式:

(1)情景一:在PHP中使用mysql_query(“set names GBK”);指定三个字符集(客户端、连接层、结果集)都是GBK编码。

情景代码: 

    .....
    mysql_query(“set names GBK”);
    $bar = addslashes($_GET[‘bar’]) ;
    $sql = “select password from user where bar=’{$bar}’”;
    $res = mysql_query($sql) ;
    ......


提交: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语句。

情景代码:

    ....
    mysql_query(“set names UTF-8”) ;
    $bar =iconv(“GBK”,”UTF-8”, addslashes($_GET[‘’bar])) ;
    $sql = “select password from user where bar=’{$bar}’” ;
    $res = mysql_query($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

同样可以多出一个反斜杠进行利用,在此不再详述,因为漏洞条件比较苛刻。

 

 

6、安全方案

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

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

(2)使用mysql_real_escape_string进行转义

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

就是使用mysql_set_charset进行指定。

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

测试;


输出:


效果很明显。

 

原文链接:http://write.blog.csdn.net/postedit/42874517

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值