浅析字符集编码与宽字节注入
想要深入理解一下宽字节注入,结果找博客看的时候发现要真正的理解宽字节注入的原理以及宽字节注入出现的场景,还要懂得一些字符集的知识。
先简单提一下宽字节注入的原理:
存在宽字节注入的前提是数据库使用了多字节字符集(比如GBK,BG18030)。拿GBK来举例,假设MySQL数据库使用了GBK字符集,一个汉字在GBK中用两个字节来表示。一些cms可能会使用addlashes函数来转义特殊字符,如将’变成\’让单引号失去它原本意义,以抵御SQL注入。这时我构造payload id=1%df’,经过addlashes转义后变成id=1%df\’,在GBK编码中反斜杠的编码是%5c,这样%df%5c就被MySQL认为是一个汉字,从而’实现逃逸。
现在你已经对宽字节注入有了一定的认识,但深入理解可能还存在很多疑问:多字节字符集是什么?MySQL使用同样是多字节字符集的GB2312能造成宽字节注入吗?为什么%df%5c会被MySQL误认为是汉字呢?
只有了解一件事物,才能更好地去理解、发现它的漏洞。那我们就先从字符集开始吧!
字符集的来历
计算机刚被发明出来时只是用来做一些数学计算,比如计算导弹弹道,但后来人们还想用计算机处理文字,可是只有0101的计算机只能处理数字,该如何处理文本呢?这时字符集就被发明了出来,实际上就是将文本和数字一一对应起来,比如A对应1,B对应2,那么(A=>1,B=>2)就是字符集。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。
由于计算机是美国人发明的,因此,最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,是最早的字符集。
但随着计算机逐渐普及,ASCII码对于非英语国家来说就不够用了。所以各个国家纷纷搞出了自己国家的一套编码,比如日本的Shift_JIS,韩国的Euc-kr,中国的G2312。但可以想到的是,全世界有上百种语言,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。
因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。然而为了囊括世界上所有的字符,Unicode最长可达到四到六个字节,最短也要两个字节,所以使用Unicode编码比ASCII编码需要多一倍的存储空间,英文国家自然不乐意。
所以后来又出现了把Unicode编码转化为“可变长编码”的UTF-8编码,英文一个字节表示,中文三个字节表示。
参考文章:
(18条消息) 字符编码详解:ASCII、Unicode、UTF-8_Yancy的博客-CSDN博客
所以多字节字符集顾名思义,就是用多个字节表示字符的字符集。如:GB2312、GBK、UTF-8、GB18030等。
常见字符集编码范围
1、ASCII
ASCII是7位编码(最前面永远是0),编码范围是0x00-0x7F。
0000 0000-0111 1111
2、gb2312
表示汉字用两个字节,英文一个字节
两个字节中第一个成为“高位字节”,第二个称为“低位字节”
高位字节的范围是0xA1-0xF7 低位字节的范围是0xA1-0xFE
Gb2312并未收录繁体中文汉字和一些生僻字
3、GBK
表示中文汉字用两个字节,英文一个字节
GBK编码是GB2312的超集,向下完全兼容GB2312,
GBK的整体编码范围是0x8140-0xFE,不包括低字节是0X7F的组合。
即高位字节的范围是0x81-0xFE,低字节范围是0x40-0x7E和0x80-0xFE,其中低字节是0x40-0x7E的GBK字符有一定特殊性,因为这些字符占用了ASCII码的位置,这样会给一些系统带来麻烦。
GBK和GB2312都是双字节等宽编码,如果算上和ASCII兼容所支持的单字节,也可以理解为是单字节和双字节混合的变长编码。
4、GB18030
是变长编码,有单字节、双字节和四字节三种方式。
GB18030编码向下兼容GBK和GB2312,兼容的含义是不仅字符兼容,而且相同字符的编码也相同。
GB18030的单字节编码范围是0x00-0x7F,完全等同于ASCII;双字节编码的范围和GBK相同,高字节是0x81-0xFE,低字节是0x40-0x7E和0x80-0xFE;四字节编码中第一、三字节的编码范围是0x81-0xFE,二、四字节是0x30-0x39。
5、UTF-8中文三个字节,英文一个字节
UTF-8的编码规则很简单,只有两条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
参考文章:
常用字符集编码详解:ASCII 、GB2312、GBK、GB18030、... - 百度文库 (baidu.com)
对以上有一些了解后,我们就可以知道gb2312是不能造成宽字节注入的,因为gb2312低位字节的范围是0xA1(1010 0001)-0xFE,而ASCII码的范围为0x00-0x7F(0111 1111),gb2312的低位字节根本不包括ASCII。
与gb2312不同的是,GBK编码和gb18030编码的低位字节包含ASCII编码范围。就拿GBK举例来说,低字节范围是0x40-0x7E和0x80-0xFE,\(ASCII编码为%5c)刚好在低字节范围中,所以%df%5c就会被识别成一个GBK编码的中文字符,' 即可成功逃逸。
最后可以看一篇文章,师傅不仅举了实例还提到了宽字节注入如何防范,拥有字符集的基础知识后看它,你一定会发出妙啊的感叹:
浅析白盒审计中的字符编码及SQL注入 | 离别歌 (leavesongs.com)

总结
1、数据库编码必须需是多字节字符编码集,而且低字节字符范围要和ASCII重合(如GBK、gb18030)。注意,只要是多字节字符编码集就存在可能性,具体是否存在宽字节注入还要看字符集的编码范围,不要把目光局限于GBK和gb18030。
2、gbk编码造成的宽字节注入问题,解决方法是设置
character_set_client = binary
3、mysql_real_escape_string函数会考虑到连接的当前字符集,所以也可以用来抵御宽字节注入。但是一定要记得:单独调用set names和mysql_real_escape_string是无法避免宽字节注入问题的。还得调用mysql_set_charset来设置一下字符集。
4、谨慎使用iconv来转换字符串编码,很可能画蛇添足造成不必要的麻烦。只要前端、数据库、程序编码全部统一,就不会出现乱码问题。

浙公网安备 33010602011771号