MySQL宽字节注入及相关知识点
Info
这些天在利用sqli-labs来研究sql注入,发现了很多有趣的东西,今天就在这里记录一下宽字节注入以及相关知识点.
宽字节注入
人类有多种语言,然而计算机的语言只有0和1,所以编码就此诞生.宽字节注入的发生背景就是GBK编码.
我们的场景很简单:
mysql_query("SET NAMES 'gbk'");
$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;
$sql = "SELECT * FROM news WHERE tid='{$id}'";
服务端设置连接字符集,将通过addslashes等过滤函数将输入提交到数据库,然而问题在于,过滤函数并不是按照连接字符集来过滤的,所以就导致了以下的现象:
当用户提交%df'
时,'
因为在addslashes的黑名单内,所以会被过滤为%df\'
,转成ascii码后变成%df%5c%27
.
当数据提交到数据库后,由于设置了GBK为连接字符集,当两个字符中前一个字符的ascii码值大于128时,该编码会把这两个字符视作一个汉字,所以%df%5c
被当做一个汉字,从而使'
逃逸.
修复
上面提到了,问题的关键在于过滤函数并没有考虑当前连接的字符集.所以要使用mysql_real_escape_string()
,该函数会考虑当前连接的字符集.
但是该函数是从mysql->charset
这个对象属性中获取字符集,我们上面的mysql_query("SET NAMES 'gbk'");
并不会达到这种效果,所以要将其改为mysql_set_charset('gbk'),
该函数除了SET NAME 'gbk'
(下面会提到)之外,还会将mysql->charset
设置为GBK,从而供mysql_real_escape_string()
使用.
MySQL字符集转换过程
既然说到字符集,就有必要了解一下其运作过程:
- MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection
-
进行内部操作前将请求数据从character_set_connection转换为内部操作字符集,其确定方法如下:
- 使用每个数据字段的CHARACTER SET设定值
- 若上述值不存在,则使用对应数据表的DEFAULT CHARACTERSET设定值(MySQL扩展,非SQL标准)
- 若上述值不存在,则使用对应数据库的DEFAULT CHARACTERSET设定值
- 若上述值不存在,则使用character_set_server设定值
- 将操作结果从内部操作字符集转换为character_set_results
我们所执行的SET NAMES
的功能如下:
SET NAMES sets the three session system variables character_set_client, character_set_connection, and character_set_results to the given character set.
几点需要注意的问题
- URL encode的过程就是把部分url做为字符,按照某种编码方式(如:utf-8,gbk等)编码成二进制的字节码,然后每个字节用一个包含3个字符的字符串 “%xy” 表示,其中xy为该字节的两位十六进制表示形式
- GET和POST的URL编码方式
- POST的格式为application/x-www-form-urlencoded,在HTML5中不编码的字符有字母,数字和
*-_.
- chrome中GET只会对空格和单引号进行编码