MYSQL注入之宽字节注入

一、经典sql注入漏洞

当前例子1中并没有对$_GET[‘name’]的值进行过滤,所以是存在sql注入漏洞的。

<?php
  $name=$_GET['name'];
  $conn=mysql_connect('localhost','root','root');
  if($conn==null){exit("connect error !<br>");}
  mysql_select_db("aaa",$conn);
  $sql="select * from a1 where name='".$name."'";
  $result=mysql_query($sql,$conn);
  while($val=mysql_fetch_row($result)){
      print_r($val);
      print("<br>");
  }
?>

对应检测的payload如下:

http://127.0.0.1/test.php?name=a'or 1=1 --+ 页面正常
http://127.0.0.1/test.php?name=a'or 1=2 %23  页面报错

其中%23对应#的URL编码,在mysql中起到注释的作用。

二、安全过滤

首先宽字节注入与HTML编码是没有关系的,如果页面中看到了<meta charset="utf-8">就放弃尝试的话,那是一个误区,因为这是当前页面的编码,而并非数据库的编码。
常见的宽字节有如下:

  1. GB2312
  2. GBK
  3. GB18030
  4. BIG5
  5. Shift_JIS

实际上只有两个字节,宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象。
对于上面的例子1中的PHP程序中的$name变量进行安全过滤,如使用转义函数
addslashes, mysql_real_escape_string mysql_escape_string来进行过滤,则对应的payload全部失效。

还有一种情况是magic_quote_gpc(php5.4后废除),php中的magic_quotes_gpc是配置在php.ini中的,他的作用类似addslashes(),就是对输入的字符创中的字符进行转义处理。他可以对_POST、$__GET以及进行数据库操作的sql进行转义处理,防止sql注入。
转义函数影响的字符包括:

ASCIINULL)字符\x00
换行字符\n,addslashes不转义
回车字符\r,addslashes不转义
反斜杠字符\
单引号字符'
双引号字符"
\x1a,addslashes不转义

对于例子1进行安全增强后,得到例子2,如下所示。
注意:三个转义函数的功能稍有区别,同时,转义只对字符型SQL注入防范有效,对于数值型SQL注入无效。

<?php
	$name=$_GET['name'];
	//$name=addslashes($name);
	//$name=mysql_escape_string($name);
	$conn=mysql_connect('localhost','root','root');
	$name=mysql_real_escape_string($name);
		if($conn==null){exit("connect error !<br>");}
	mysql_select_db("aaa",$conn);
	$sql="select * from a1 where name='".$name."'";
	$result=mysql_query($sql,$conn);
		while($val=mysql_fetch_row($result)){
   			 print_r($val);
   			 print("<br>");
}
?>

三、宽字节注入原理

通常来说,一个GBK编码的的汉字,占2个字节,一个utf-8的汉字,占3个字节。在php中,我们可以通过输出

echo strlen("我")

来进行测试。当页面保存为GBK时,页面输出的是2,当页面保存为utf-8时,页面输出的是3。
宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入。
宽字节是指两个字节宽度的编码技术,如UNICODE,GBK,BIG5等。当mysql数据库数据在处理和存储过程中,涉及到字符集相关信息包括:

1) character_set_client:客户端发送过来的SQL语句编码,也就是PHP发送的SQL查询语句编码字符集。
(2) character_set_connection:MySQL服务器接收客户端SQL查询语句后,在实施真正查询之前SQL查询语句编码字符集。
(3) character_set_database:数据库缺省编码字符集。
(4) character_set_filesystem:文件系统编码字符集。
(5) character_set_results:SQL语句执行结果编码字符集。
(6) character_set_server:服务器缺省编码字符集。
(7) character_set_system:系统缺省编码字符集。
(8) character_sets_dir:字符集存放目录,一般不要修改。

宽字节对转义字符的影响发生在character_set_client=gbk的情况,也就是说,如果客户端发送的数据字符集是gbk,则可能会吃掉转义字符\,从而导致转义失败。

比如:PHP的编码设置为UTF-8而Mysql的编码设置为SET NAMES 'gbk’或是SET character_set_client =gbk,这样配置会引发编码转换从而导致的注入漏洞。

这里要说明一小点的是:
SET NAMES 'x’语句与这三个语句等价:
mysql>SET character_set_client =x;
mysql>SET character_set_results =x;
mysql>SET character_set_connection =x;
也就是说你设置了 SET NAMES ‘x’ 时就等于同时执行了上面的3条语句。

在我们正常情况下使用 addslashes函数或是开启PHP的GPC(注:在php5.4已上已给删除,并且需要说明特别说明一点,GPC无法过滤$_SERVER提交的参数)时过滤 GET、POST、COOKIE、REQUSET 提交的参数时,黑客们使用的预定义字符会给转义成添加反斜杠的字符串如下面的例子:

单引号(')=(\')
双引号(")=(\")
反斜杠(\)=(\\)

假如这个网站有宽字节注入那么我们提交:http://127.0.0.1/test.php?name=%df%27
这时,假如我们现在使用的是addslashes来过滤,那么就会发生如下的转换过程.

%df%27=====(addslashes)======>%df%5c%27========>(数据库GBK)====>運'

这里可能有一些人没看懂,我可以粗略的解释一下。
前端输入%df%27时首先经过上面addslashes函数转义变成了%df%5c%27%5c是反斜杠\),之后在数据库查询前因为设置了GBK编码,即是在汉字编码范围内两个字节都会给重新编码为一个汉字。然后MySQL服务器就会对查询语句进行GBK编码即是%df%5c转换成了汉字"",而单引号就逃逸了出来,从而造成了注入漏洞

比如:

http://127.0.0.1/test.php?name=%df’ or 1=1 limit 1,1%23&pass=

其对应的sql语句就是:

select * from table_name where name=‘運’ or 1=1 limit 1,1#’ and password=”

说明:GBK编码,它的编码范围是0×8140~0xFEFE(不包括xx7F),在遇到%df(ascii(223)) >ascii(128)时自动拼接%5c,因此吃掉‘\’,而%27、%20小于ascii(128)的字符就保留了。

例子3:

<?php
	header("Content-Type:text/html;charset=gbk"); //为了显示,将页面默认为gbk
	$name=$_GET['name'];
	$name=addslashes($name);
	$conn = mysqli_connect('127.0.0.1','root','');  
	mysqli_select_db($conn,'mysql');
	mysqli_query($conn,"SET NAMES 'gbk'");
	if($conn==null){exit("connect error !<br>");}
	$sql="select * from user where user='".$name."'";
	$result=mysqli_query($conn,$sql);
	echo $sql;
		while($val=mysqli_fetch_row($result)){
   			 var_dump($val);
   			 print("<br>");
}
?>

这是一个带有宽字节注入漏洞的PHP代码程序,对应的payload如下:

http://127.0.0.1/test/t3.php?name=%df' or 1=1 %20%23

对应的sql为:

select * from a1 where name='運' or 1=1 #

在这里插入图片描述原理就是:mysql_query("SETNAMES 'gbk'",$conn)语句将编码字符集修改为gbk,此时%df\'对应的编码就是%df%5c’,即汉字“運’”,这样单引号之前的转义符号“\”就被吃调了,从而造成注入。

例子4:
访问:
http://www.2cto.com /test.php?username=test%d5′%20or%201=1%23&pwd=test

经过浏览器编码的URL为:
username=test%df%27%20or%201=1%23

经过php的URL编码:
username=test 0xd5 0×27 0×20 or 0×20 1=1 0×23(为了便于阅读,在字符串与16进制编码之间加了空格)。

经过php的GPC自动转义:
username=test 0xd5 0×5c 0×27 0×20 or 0×20 1=1 0×23

因为在数据库初始化连接的时候SET NAMES ‘gbk’0xd5 0×5c解码后为诚0×27解码为’0×20为空格0×23为mysql的注释符#
最终的sql语句为:

SELECT * FROM user WHERE username=’test诚’ or 1=1#’ and password=’test’;

最终闭合了单引号,达到了SQL注入的目的。

四、总结:

1:宽字节注入跟HTML页面编码无关。
2:Mysql编码与过滤函数推荐使用mysql_real_escape_string(),mysql_set_charset()。
3:转编码函数同样会引起宽字节注入,即使使用了安全的设置函数
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值