在了解宽字节注入以前,咱们先来看一看字符集是什么。字符集也叫字符编码,是一种将符号转换为二进制数的映射关系。
几种常见的字符集:php
ASCII
编码:单字节编码latin1
编码:单字节编码gbk
编码:使用一字节和双字节编码,0x00-0x7F
范围内是一位,和 ASCII 保持一致。双字节的第一字节范围是0x81-0xFE
UTF-8
编码:使用一至四字节编码,0x00–0x7F
范围内是一位,和 ASCII 保持一致。其它字符用二至四个字节变长表示。宽字节就是两个以上的字节,宽字节注入产生的缘由就是各类字符编码的不当操做,使得攻击者能够经过宽字节编码绕过SQL注入防护。mysql
数据提交到MySQL数据库,须要进行字符集的转换,使得MySQL数据库能够对数据进行处理,这一过程通常有如下三个步骤:sql
character_set_client
->character_set_connection
。character_set_connection
-> 表建立的字符集
。表建立的字符集
-> character_set_results
。当执行set names "charset"
,至关于执行
set character_set_client = charset
set character_set_connection = charset
set character_set_results = charset
数据库
client 指的是PHP程序
connection 指的是PHP客户端与MySQL服务器之间链接层
results 指的是MySQL服务器返回给PHP客户端的结果浏览器
MySQL常见的乱码问题就是这三个字符集的设置不当所引发的。bash
这里的演示环境为 sqli-labs\Less-32服务器
<?php //including the Mysql connect parameters. include("../sql-connections/sql-connect.php"); 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; } // take the variables if(isset($_GET['id'])) { $id=check_addslashes($_GET['id']); //echo "The filtered request is :" .$id . "<br>"; //logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a'); fwrite($fp,'ID:'.$id."\n"); fclose($fp); // connectivity mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result); if($row) { echo '<font color= "#00FF00">'; echo 'Your Login name:'. $row['username']; echo "<br>"; echo 'Your Password:' .$row['password']; echo "</font>"; } else { echo '<font color= "#FFFF00">'; print_r(mysql_error()); echo "</font>"; } } else { echo "Please input the ID as parameter with numeric value";} ?>
当用户提交
http://127.0.0.1/Less-32/?id=1'
此时,发生以下转换
第一步:'
被 check_addslashes
函数转义为 \'
函数
第二步:在执行sql查询以前,也即\'
代入MySQL服务器以前,mysql_query("SET NAMES gbk");
将MySQL的三个字符集设置为 gbk 编码post
第三步:character_set_client
告诉MySQL Server
,传入的是gbk编码,也就是\'
被当作了%5C%27
传入测试
第四步:character_set_client
-> character_set_connection
编码彻底一致,数据没有作任何转换,因此输入是%5C%27
,输出的是%5C%27
第五步:character_set_connection
-> table charset
这里咱们须要关注下所使用的表的字符集
能够看到id
参数没有设置编码方式,不会对%5C%27
进行处理。在这里MySQL服务器将查询语句执行,并返回结果。
执行的SQL语句为:
$sql="SELECT * FROM users WHERE id='1\'' LIMIT 0,1";
'
被转义没法进行注入
第六步:table charset
-> character_set_results
因为character_set_results
字符集也设定为 gbk ,保证了输出内容没有乱码。
经过上面的分析,咱们发现用户提交的数据实际上只被处理了两次,一次是check_addslashes
对危险字符的处理,另外一次是gbk
编码。因此,咱们能够将以上步骤简化为:
数据 ==== (check_addslashes)====XXX====(GBK)====代入数据库执行的内容
提交:
http://127.0.0.1/Less-32/?id=1%df'
('
浏览器自动进行url编码%27
)
根据以上分析,发生以下转换:
%df%27
====>(check_addslashes)====>%df%5c%27
====>(GBK)====>運'
MySQL执行的语句为:
$sql="SELECT * FROM users WHERE id='1運'' LIMIT 0,1";
成功将单引号闭合,能够进行SQL注入。
为了不漏洞,网站通常会设置UTF-8编码,而后进行转义过滤。可是因为一些不经意的字符集转换,又会致使漏洞。
使用set names UTF-8
指定了UTF-8字符集,而且也使用转义函数进行转义。有时候,为了不乱码,会将一些用户提交的GBK字符使用iconv
函数先转为UTF-8,而后再拼接入SQL语句。
mysql_query(“set names UTF-8”) ; $bar =iconv(“GBK”,”UTF-8”, addslashes($_GET[‘’bar])) ;
提交:
http://127.0.0.1/Less-32/?id=1%e5%5c%27
转换:(%e5%5c
转为UTF-8为e9%8c%a6
)
%e5%5c%27
====>(addslashes)====>%e5%5c%5c%5c%27
====(iconv)====>%e9%8c%a6%5c%5c%27
能够看到,多出了一个%5c
,将转义符(反斜杠)自己转义,使得后面的%27
单引号发挥了做用
对于宽字节编码,有一种最好的修补就是:
(1)使用mysql_set_charset(GBK)
指定字符集
(2)使用mysql_real_escape_string
进行转义
原理是,mysql_real_escape_string
与addslashes
的不一样之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,可是这个“当前字符集”如何肯定呢?
就是使用mysql_set_charset
进行指定。
上述的两个条件是“与”运算的关系,少一条都不行。
<meta charset="utf-8">
测试;
输出: