参考链接:
(1)漏洞银行 [3期]浅析宽字节注入
https://www.bugbank.cn/live/view.html?id=109301
(2)大部分来自于SQL注入:宽字节注入(GBK双字节绕过)这篇文章,仅仅用于个人思路梳理与总结。https://lyiang.wordpress.com/2015/06/09/sql%E6%B3%A8%E5%85%A5%EF%BC%9A%E5%AE%BD%E5%AD%97%E8%8A%82%E6%B3%A8%E5%85%A5%EF%BC%88gbk%E5%8F%8C%E5%AD%97%E8%8A%82%E7%BB%95%E8%BF%87%EF%BC%89/
一、漏洞原因
宽字节注入的方法很简单,就是编码。
GBK编码导致宽字节注入漏洞。GBK编码是数据库编码,跟前台的编码无关。
GBK双字节编码:一个汉字用两个字节表示,首字节对应0x81-0xFE,尾字节对应0x40-0xFE(除0x7F),刚好涵盖了对应的编码0x5C。
构造如下语句进行sql注入。
?id=1%df' union select 1,user(),concat(name,0x7e,pass) from admin %23
等同于
?id=1%df%27 union select 1,user(),concat(name,0x7e,pass) from admin %23
这样的话id的参数传入代码层,就会在’前加一个\,由于采用的URL编码,所以
产生的效果是%df%5c%27。
二、基本解释
因为%df的关系,\的编码%5c被吃掉了,也就失去了转义的效果,直接被带入到mysql中,然后
mysql在解读时无视了%df%5c形成的新字节,那么单引号便重新发挥了效果。
其中%5c表示\,即转义符号。
%27表示’的url编码。
%23为#,代表注释。
三、分析过程
假设一个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
我们在后边这么构造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,形成一个新的字节,举个例子就是%df遇到%5c会把%5c吃掉,形成%df%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中运行的结果
是什么呢?
输出了user()
我们
追踪数据的变化过程
%df%27===>(addslashes)=== >%df%5c%27===>(GBK)====>運’
用户输入==>过滤函数==>代码层的$sql==>mysql处理请求==>mysql中的sql
mysql_query("SET NAMES gbk");
当这行代码在代码层被写入时,三个字符集(客户端、连接层、结果集)都是GBK编码。
那么便会发生如上的情况。
四、安全方案
对于宽字节编码,有一种最好的修补就是:
(1)使用mysql_set_charset(GBK)指定字符集
(2)使用mysql_real_escape_string进行转义
原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?
就是使用mysql_set_charset进行指定。
scape_string进行转义
原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?
就是使用mysql_set_charset进行指定。
上述的
两个条件是“与”运算的关系
少一条都不行。