编码问题引发的文件操作错误和SQL宽字节注入

人过留名,雁过留声
人生天地间,凡有大动静者
必有猪头

前言

有时候由于文件的编码,代码的编码设置或浏览器网页的编码设置不一致往往就会造成一些问题困扰,常见的有文件或者网页上出现中文乱码,文件的导入失败等等。如果放置在网站的开发中,有时候就会因为编码的设置问题导致网页中存在 SQL 宽字节注入漏洞,危害极大。

1. 文件操作

① 中文乱码

  1. 用 sublime 打开一个 PHP 文件时中文出现乱码,但是用记事本打开却是正常的。
    在这里插入图片描述
    在这里插入图片描述
  2. 仔细一看发现记事本右下角的字符集编码形式为 ANSI,而 sublime 默认的编码形式为 UTF-8,将 PHP 文件的编码形式改为 UTF-8 即可,使用文件另存为。
    在这里插入图片描述
  3. 再用 sublime 打开时,中文正常显示。
    在这里插入图片描述

② 文件导入

  1. 在 SQL-Front 中创建数据库时(字符集编码为 utf8),导入 SQL 文件失败。
    在这里插入图片描述
    在这里插入图片描述
  2. 用记事本打开 SQL 文件发现其字符集编码形式为 ANSI。
    在这里插入图片描述
  3. 同样将 SQL 文件的字符集编码改为 UTF-8 即可导入成功。
    在这里插入图片描述

2. SQL宽字节注入

实验前的准备:
将源码导入 phpstudy 的网站根目录下,同时关闭 php.ini 设置中的 magic_quetos_gpc。
默认的,如果开启 magic_quotes_gpc 配置的话,PHP 对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。

2.1 测试代码1

① 源码分析

使用 mysql_query() 在连接数据库部分使用 gbk 编码形式。
使用 addslashes() 函数过滤参数。
单引号引用参数。

addslashes() 函数返回在预定义字符前添加反斜杠的字符串。
预定义字符是:
单引号(’)
双引号(")
反斜杠(\)
NULL
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。

在这里插入图片描述

② 漏洞利用

  1. 对于字符串型的 SQL 注入中,由于源码中使用 addslashes() 函数对参数中的单引号进行转义,是的单引号注入被限制,导致常规注入失败。
    在这里插入图片描述
  2. 为了使单引号逃逸出去不被限制,这里可以注意到在源码中连接数据库部分使用了 gbk 编码。由此也就造成了常见的宽字节注入。

宽字节注入是利用 MySQL 的一个特性,MySQL 在使用 gbk 编码时会将两个字符认为是一个汉字,其中前一个字符的 ascii 码要大于128,才到汉字的范围。
这里的宽字节注入形成的原因是源码中使用 addslashes() 函数将参数中的 进行转义,变成 \ ',即 %5c%27(%5c是\,%27是单引号)。如果在传入数据时可以在 %5c 前面再加上一个 %df ,这样就会变成了 %df%5c%27,而恰好编码方式为 GBK 的话,就会把 %df%5c 当成一个字(汉字"運"),从而让单引号得以从限制从逃逸出来,形成注入。

在这里插入图片描述
在这里插入图片描述

2.2 测试代码2

① 源码分析

这里在连接数据库部分使用 gb2312 编码
其余部分和源码1差不多

在这里插入图片描述

② 注入

同样使用宽字节的形式注入,发现注入失败。
在这里插入图片描述
使用宽字节方法注入失败原因主要是 gb2312 的取值范围,它的高位范围是 0xA1 ~ 0xF7,低位范围是 0xA1~0xFE,而 ====是 0x5c,是不在低位范围中的。所以,0x5c 根本不是 gb2312 中的编码,所以自然也是不会被吃掉的,单引号就自然无法逃逸出来。

2.3 测试代码3

① 源码分析

源码中使用 mysql_real_escape_string() 对参数进行过滤

在这里插入图片描述

mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
\x00
\n
\r
\

"
\x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。

在这里插入图片描述

② 漏洞利用

  1. 使用宽字节注入,成功。
    在这里插入图片描述
  2. 宽字节注入成功的原因是没有指定 php 连接的 MySQL 的字符集同样为 gbk,在执行 SQL 语句之前调用 mysql_set_charset 函数设置当前连接的字符集为 gbk。
    在这里插入图片描述

2.4 测试代码4

① 源码分析

 在 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 将参数转换为二进制,所以就不存在宽字节的问题,所有的数据以二进制的形式传输,能有效避免宽字节的注入。
在这里插入图片描述

2.5 测试代码5

① 源码分析

本来 character_set_client=binary 和 addslashes() 函数结合得好好,半路杀出个 转换编码函数iconv(),导致前功尽弃,漏洞复现。

在这里插入图片描述

② 漏洞利用

输入了一个 錦’,“錦” 这个字,它的 utf-8 编码是 0xe98ca6,它的gbk 编码是 0xe55c。当錦 被 iconv() 函数从 utf-8 转换为 gbk 后,变成了 %e5%5c,而后面的单引号被 addslashes() 变成了 %5c%27,这样结果就是 %e5%5c%5c%27。
在这里插入图片描述
为什么 utf-8 转换成 gbk 的时候,没有使用宽字节注入的姿势还是能注入成功?
这跟utf-8的规则有关,UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

从2我们可以看到,对于多字节的符号,其第 2、3、4 字节的前两位都是 10,也就是说,\(0x0000005c)不会出现在 utf-8 编码中,所以 utf-8 转换成 gbk 时,如果有 \ 则 php 会报错。

2.6 测试代码6

① 源码分析

相比源码5 ,源码6使用 iconv() 函数时将 gbk 转换为 utf-8

在这里插入图片描述

② 漏洞利用

还是宽字节注入
在这里插入图片描述
本次漏洞出现的原因出现在 php 而不是 MySQL 上,gbk 汉字两个字节,utf8 汉字三个字节,当 gbk 转换为 utf8 的时候会两个字符一转换,所以当 ==\ '==前面的字符时奇数的话 ==\ ==就会被吞噬,单引号也就可以逃逸出来。

总结

  1. gbk 编码容易引起宽字节注入,比较有效的解决方法可以设置 character_set_client=binary ,使用二进制去传输数据可以避免宽字节,多字节带来的麻烦。

  2. 单独调用 set name gbk 和 mysql_real_escape_string 是无法避免宽字节注入的,还得结合 mysql_set_charset 来设置数据库的字符集编码形式。

  3. 使用 iconv() 函数转换编码时需谨慎,实际上只要我们把前端 HTML/CSS/JS ,MySQL 和 PHP 的编码形式都设置一致就不会出现乱码问题。iconv() 函数使用不当反而会出现注入漏洞。

                                                                                                            猪头
                                                                                                         2020.2.10
    
发布了21 篇原创文章 · 获赞 3 · 访问量 637

猜你喜欢

转载自blog.csdn.net/LZHPIG/article/details/104245335