暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SQL注入之宽字节注入

第59号 2021-05-11
3420

No.1

简   介

SQL注入近几年来连续被OWASP当作十大漏洞中最最危险的漏洞而存在。无论是从数据库中获得敏感信息还是执行一系列的恶意操作甚至是直接获取整个数据库权限,都可能发生在一次小小的提交参数的过程中。为此大多数网站开始对于SQL注入做了一定的防御方法,最早有人提出,将用户提交的所有敏感字符进行过滤和转义,要么将提交参数中的敏感字符过滤掉后再提交给数据库,要么对那些敏感字符使用转义符号进行转义,使其丧失掉注入功能后再进行提交。但我们知道,如果不对源头进行处理,再怎么亡羊补牢也是无济于事。后来这些网站在面对黑客那些令人匪夷所思的Bypass技巧面前根本没有应对方法,最终无奈选择关闭。


本篇文章,美创安全实验室将给大家介绍一种当年绕过转义防御最好用的技巧即宽字节注入攻击。


No.2

编码历史

一听到“宽字节注入”,那不可避免地就要提到有关字节编码方面的知识,所以在讲解注入原理之前,我们简单讲解一下有关编码的历史。


最早美国人决定用8个可以开合的晶体管来组成不同的状态,这些晶体管只有“亮”或“不亮”两种形态,也就是对应了二进制的0和1。而1个字节有8个比特位,可以组合成2^8=256种不同的方案,他们把编号从0开始的32种状态用在规定的特殊用途,这32个字符后来成为“控制码”;他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,而这96个字符也被称为”ASCII码”。


后来随着计算机不断发展,世界各国为了可以让计算机保存各国的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,于是从第128字符到最后一个255字符被称为“扩展字符集”。


但是等到中国人民得到计算机时,已经没有多余的字节状态来存储汉字。于是国人自主研发,将127之后的字符全部去掉,并规定:一个小于127的字符意义与原来相同,若两个大于127的字符连在一起时,就表示一个汉字,这种方案被叫做“GB2312“。但中国的汉字实在太多GB2312已经无法满足,于是人们将规则放宽,规定只要第一个字节是大于127就固定表示这是一个汉字的开始,而不管后面跟的是不是扩展字符集的内容。这种方案也就是现在我们常用的“GBK”编码。


因为当时各国都有一套自己的编码标准,如果没有装其他国家的编码系统,就完全无法使用他国的语言,结果互相之间谁也不懂谁的编码,后来国际标准化组织ISO决定着手解决这个问题,他们废除了所有的地区性编码方案,并将字符的表示由原来的一个字节改成两个字节,也就是用16位来统一表示所有的字符,对于ASCII码这些半角字符,也从8位扩展到16位,也就是说他们的高8位永远是0。这一标准化方案也就是现在世界各国普遍使用的UNICODE编码。


No.3

宽字节注入原理

所谓宽字节指的是类似于GB2312、GBK、BIG5等需要两个字节编码的字节,而宽字节带来的安全问题主要是“吃掉”ASCII字符的现象,即将两个ASCII字符误认为是一个宽字节字符。


以PHP和MYSQL为例,MySQL收到请求时将请求数据从character_set_client转换为character_set_connection;在进行内部操作前,将请求数据从character_set_connection转换为内部操作字符集,使用每个字段的character_set的值将操作结果从内部操作集转换为character_set_results。而宽字节注入发生的位置就是PHP将请求发送到MYSQL时字符集使用character_set_client设置值进行了一次编码。


如果PHP中编码为GBK,函数执行添加的是ASCII编码,MYSQL默认字符集是GBK等宽字节字符集就会发生宽字节注入的现象。


举例来说,如果在php为了防止SQL注入而使用了一些转义敏感字符的函数,类似于:addslashes()、mysql_real_escape_string()、mysql_escape_string()等。这些函数将敏感字符例如 ‘ ,转义成 \’ ,导致无法进行SQL注入。但如果在提交参数的时候在单引号前加上一个大于127的字符,比如 %df,那么提交的参数就变成了 %df’ ,php自动加上反斜杠将、转义变成 %df\’ ,再进一步变换成 %df%5c%27 ,再提交给MYSQL进行处理的时候问题来了,MYSQL要对接收到的数据进行编码,编码方式是GBK,所以他认为 %df%5c 是一个宽字符,而不是两个字符。也就是说 %df\’ = %df%5c%27 = 運' 。


我们追踪一下数据变化的过程:

可以发现转义符号被%df给“吞掉”了,导致‘ 成功绕过转义限制,接下来就可以进行SQL注入了。


No.4

漏洞复现

1、以php客户端为例,当用户输入数据后,会通过php的默认编码生成sql语句发送给MYSQL服务器,在php没有开启default_charset编码时,php的默认编码为空


此时php则会根据数据库中所使用的编码来自动确定自己使用哪种编码,在测试的时候可以输出strlen()对单个汉字的返回结果。如果输出值是3代表是UTF-8编码,输出是2代表是GBK编码


2、服务器在接收到请求后会把客户端编码的字符串转换成连接层编码字符串,也就是由character_set_client指定的值变成character_set_connection指定的值。也就是宽字节发生注入时的位置


3、假设在php页面中,php使用了addslashes()函数,将用户提交的敏感字符例如单引号,双引号,斜杠等全部转义导致这些符号无法被SQL注入利用。但是通过阅读php代码可以发现,mysql_query(“SET NAMES gbk”);这一句,使三个字符集(客户端,连接层,结果集)都采用GBK编码。经过前面的介绍可以知道GBK是双字节,如果此时再使用了addslashes()函数,那么一定可以触发宽字节注入


4、为了绕过对单引号的转义限制,我们利用宽字节编码的漏洞,即提交一个大于127号字符的十六进制表示加上我们提交的单引号,%cc‘


可以发现%cc与转义符号\经过GBK编码后成为了一个汉字,从而导致单引号绕过转义的限制,注入到SQL语句中。


5、现在拥有了可以利用的单引号,接下来的SQL注入就变得简单了,例如要获得数据库的库名,版本信息,用户信息等敏感信息可以使用union联合查询如下:%cc’ union select database(),version(),user() --+


6、利用sqlmap可以更轻易的进行SQL注入

Python sqlmap.py -u “127.0.0.1/zsxq-sql/sql-WideByte.php?token=1 %df’” --dbs


No.5

防御方法

1、使用Mysql_set_charset(‘gbk’);指定字符集。

防御宽字节注入,除了要进行更严格的过滤之外,还需要一个字符集的设置,SET character_set_connection=gbk,character_set_results=gbk,character_set_client=binary;


2、使用mysql_real_escape_string进行转义

mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集(使用mysql_set_charset指定字符集),不会出现前面的df和5c拼接为一个宽字节的问题


3、使用pdo方式,在Php 5.3.6及以下版本中需要设置setAttribute(PDO:ATTR_EMULATE_PREPARES,false); 来禁用preparcd statements 的仿真效果。



关于美创

杭州美创科技有限公司,敏感数据保护和数据安全领域的拓荒者和领导者,由国内多名数据库资深专家携手于2005年成立,产品及服务覆盖数据安全、数据管理、容灾备份、智能运维等四大领域,广泛应用于医疗、教育、金融、政府、人社、电力能源、物流交通、企业等众多行业。多年来,凭借卓越的技术创新与良好的用户口碑,美创多次入围全国网络安全50强,并参与多项国家及行业标准的编写,引领数据安全领域的规范发展。目前,美创科技已全面推动全国市场化发展战略,相继在北京、广州、武汉、南京、成都、上海等地设立分公司,致力于为更多的客户提供专业的安全解决方案。


文章转载自第59号,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论