SQL注入之宽字节注入

什么是宽字节

如果一个字符的大小是一个字节的,称为窄字节;如果一个字符的大小是两个字节的,则称为宽字节。

GB2321、GBK、GB18030、BIG5、Shift_JIS等这些编码都是常说的宽字节,也就是只有两个字节。
英文默认占一个字节,中午占两个字节。

什么是宽字节注入?

原理:宽字节注入发送的位置就是PHP发送请求到MYSQL时字符集使用charater_set_client设置一次编码。在使用PHP连接Mysql的时候,当设置"character_set_client=gbk"时会导致一个编码转换的问题,也就是我们熟悉的宽字节注入。

宽字节注入是利用mysql的一个特性,mysql在使用GBK编码(GBK就是常说的宽字节之一,实际上只有两个字节)的时候,会认为两个字符是一个汉字(前一个ASCII码要大于128,才到汉字的范围)

GBK首字节对应0x81-0xFE,尾字节对应0x40-0xFE(除0x7F),例如%df和%5C会结合;GB2312是被GBK兼容的,它的高位范围是0xA1-0xF7,低位范围是0xA1-0xFE(0x5C不在该范围内),因此不能使用编码吃掉%5c

常见转义函数与配置:
addslashesmysql_real_escape_stringmysql_escape_string
php.ini中magic_quote_gpc的配置

宽字节注入的条件

  1. 数据库为GBK编码
  2. 使用了转义函数,将、POGETST、cookie传递的参数进行过滤,将单引号、双引号、null等敏感字符用转义符\进行转义

宽字节注入方式

root %df' or 1=1 #

原理:在GBK编码中,反斜杠的编码是%5c,在输入%df后,使得添加反斜杠后形成%df%5c,而%df%5c是繁体字"連",单引号成功逃逸,爆出mysql数据库的错误。

案例分析

0x01mysql中的宽字节注入

我们先搭建一个实验环境。

源码很简单(注意先关闭自己php环境的magic_quotes_gpc):

<?php
//连接数据库部分,注意使用了gbk编码,把数据库信息填写进去
$conn = mysql_connect('localhost', 'root', 'toor!@#$') or die('bad!');
mysql_query("SET NAMES 'gbk'");
mysql_select_db('test', $conn) OR emMsg("连接数据库失败,未找到您填写的数据库");
//执行sql语句
$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;
$sql = "SELECT * FROM news WHERE tid='{$id}'";
$result = mysql_query($sql, $conn) or die(mysql_error()); //sql出错会报错,方便观察
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="gbk" />
<title>新闻</title>
</head>
<body>
<?php
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "<h2>{$row['title']}</h2><p>{$row['content']}<p>\n";
mysql_free_result($result);
?>
</body>
</html>

SQL语句是SELECT * FROM news WHERE tid='{$id}',就是根据文章的id把文章从news表中取出来。

在这个sql语句前面,我们使用了一个addslashes函数,将$id的值转义。这是通常cms中对sql注入进行的操作,只要我们的输入参数在单引号中,就逃逸不出单引号的限制,无法注入,如下图:
在这里插入图片描述
那么怎么逃过addslashes的限制?众所周知addslashes函数产生的效果就是,让'变成\',让引号变得不再是“单引号”,只是一撇而已。一般绕过方式就是,想办法处理\'前面的\

1.想办法给\前面再加一个\(或单数个即可),变成\\',这样\被转义了,'逃出了限制
2.想办法把\弄没有。

我们这里的宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)。如果我们输入%df'看会怎样:
在这里插入图片描述
我们可以看到,已经报错了。我们看到报错,说明sql语句出错,看到出错说明可以注入了。

为什么从刚才到现在,只是在'也就是%27前面加了一个%df就报错了?而且从图中可以看到,报错的原因就是多了一个单引号,而单引号前面的反斜杠不见了。

这就是mysql的特性,因为gbk是多字节编码,他认为两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“運”,而'逃逸了出来。

因为两个字节代表一个汉字,所以我们可以试试%df%df%27:
在这里插入图片描述
不报错了。因为%df%df是一个汉字,%5c%27不是汉字,仍然是\'

那么mysql怎么判断一个字符是不是汉字,根据gbk编码,第一个字节ascii码大于128,基本上就可以了。比如我们不用%df,用%a1也可以:
在这里插入图片描述
%a1%5c他可能不是汉字,但一定会被mysql认为是一个宽字符,就能够让后面的%27逃逸了出来。

于是我可以构造一个exp出来,查询管理员账号密码:

-1%aa%27union%20select%201,2,concat(name,0x23,pass)%20from%20admin%23

在这里插入图片描述

0×02 GB2312与GBK的不同

gb2312和gbk应该都是宽字节家族的一员。但为什么gb2312不能注入
我们来做个小实验。把内容管理系统中set names修改成gb2312:
在这里插入图片描述

结果就是不能注入了:
在这里插入图片描述

有些同学不信的话,也可以把数据库编码也改成gb2312,也是不成功的。

为什么,这归结于gb2312编码的取值范围。它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE,而\是0x5c,是不在低位范围中的。所以,0x5c根本不是gb2312中的编码,所以自然也是不会被吃掉的。

所以,把这个思路扩展到世界上所有多字节编码,我们可以这样认为:只要低位的范围中含有0x5c的编码,就可以进行宽字符注入。

0×03 mysql_real_escape_string解决问题?

部分cms对宽字节注入有所了解,于是寻求解决方案。在php文档中,大家会发现一个函数,mysql_real_escape_string,文档里说了,考虑到连接的当前字符集。

009.jpg

于是,有的cms就把addslashes替换成mysql_real_escape_string,来抵御宽字符注入。我们继续做试验,内容管理系统v1.2:,就用mysql_real_escape_string来过滤输入:

在这里插入图片描述

我们来试试能不能注入:

在这里插入图片描述

一样没压力注入。为什么,明明我用了mysql_real_escape_string,但却仍然不能抵御宽字符注入。

原因就是,你没有指定php连接mysql的字符集。我们需要在执行sql语句之前调用一下mysql_set_charset函数,设置当前连接的字符集为gbk。

在这里插入图片描述

就可以避免这个问题了:

在这里插入图片描述

0×04 宽字符注入的修复

在上面我们说到了一种修复方法,就是先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string来过滤用户输入。

这个方式是可行的,但有部分老的cms,在多处使用addslashes来过滤字符串,我们不可能去一个一个把addslashes都修改成mysql_real_escape_string。我们第二个解决方案就是,将character_set_client设置为binary(二进制)。

只需在所有sql语句前指定一下连接的形式是二进制:

SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary

这几个变量是什么意思?

当我们的mysql接受到客户端的数据后,会认为他的编码是character_set_client,然后会将之将换成character_set_connection的编码,然后进入具体表和字段后,再转换成字段对应的编码。

然后,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。

所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。

比如,我们的内容管理系统v2.0版本更新如下:

在这里插入图片描述

已经不能够注入了:

在这里插入图片描述

总结

总结一下全文中提到的由字符编码引发的安全问题及其解决方案:

  1. gbk编码造成的宽字符注入问题,解决方法是设置character_set_client=binary。
  2. 矫正人们对于mysql_real_escape_string的误解,单独调用set names gbk和mysql_real_escape_string是无法避免宽字符注入问题的。还得调用mysql_set_charset来设置一下字符集。
    参考文章: 字符编码及SQL注入
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQL注入宽字节注入都是常见的安全漏洞类型。下面我将简要介绍这两种注入方式。 1. SQL注入SQL注入是一种在应用程序中利用不正确处理用户输入的漏洞,攻击者可以通过在用户输入中插入恶意的SQL代码来执行非授权的数据库操作。这可能导致数据泄露、数据篡改、绕过认证、获取系统权限等问题。 预防SQL注入的措施包括: - 使用参数化查询或预编译语句来处理数据库查询,而不是直接拼接用户输入的数据。 - 对用户输入进行严格的验证和过滤,避免将未经处理的输入直接传递给数据库查询。 - 最小化数据库用户的权限,并且仅分配最低必要的权限。 2. 宽字节注入宽字节注入是一种特定于某些数据库和编码方式的注入攻击方法。它利用了某些编码方式对特殊字符的处理不当,从而绕过了应用程序对输入进行过滤和验证的机制。攻击者可以通过插入宽字节字符来篡改SQL语句或绕过认证。 预防宽字节注入的措施包括: - 使用合适的编码方式,例如UTF-8,以避免特殊字符被误解释。 - 进行输入验证和过滤,确保特殊字符被正确处理,不会导致SQL语句的非预期解析。 - 定期更新数据库和应用程序框架,以修复已知的宽字节注入漏洞。 总的来说,要防止SQL注入宽字节注入等安全漏洞,应该采取综合性的安全措施,包括输入验证、参数化查询、最小权限原则、使用最新版本的软件以及定期进行安全审计和漏洞扫描。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值