原理这里我先解释下什么是宽字节。
先说下ASCII,这个编码为8个比特位,1个字节,所以能映射的范围仅有256个字符。
到了汉字这里,这套编码不够用了,毕竟汉字太多了。
这其中,出现GBK、BIG5、GB2312、gb18030等编码用以适用于汉字,原来的一个字节无法容纳,需要占用更多的字节来编码,这就是所谓的宽字节。
为什么宽字节注入会发生?
一般来说,我们使用进行SQL注入测试时,都会使用'、",开发者为了防止SQL注入,将传入到的符号进行转义,例如php中addslashes函数,会将字符加上转义符号。
由于转义的存在,加上mysql的特性是的结果和正常的相同,甚至都不能判断含有注入点,sqlmap进行测试页无法进行注入。
下面以sqli-labs的第36关为例。
这里我开启日志功能,查看真正执行的语句,你也可以在网页中打印语句。
1
2
3
4
5
6
7
| SHOW VARIABLES LIKE 'general%';
#查询日志功能是否被开启,general_log 默认关闭OFF
#general_log_file:日志文件保存位置
set GLOBAL general_log='ON';
#更改为'ON',开启日志记录。
tail /var/lib/mysql/785e6e87385b.log
#文件my有可能不一样,具体参考查出来的文件名
|
执行的语句为SELECT * FROM users WHERE id='1\'' LIMIT 0,1,不知道有没有小伙伴和我一样疑惑这个语句为什么能执行成功,笔者迷惑了一上午,在某位大大的帮助下终于理解了,感谢大大。笔者进行了一系列测试。
我们都知道”\“是转义符,也就是说最终where的是 id “1‘”(我特意用双引号表示),表中应该没有“1’”这个ID,结果应该为空,但实际上这条查询的结果和 SELECT * FROM users WHERE id='1' LIMIT 0,1相同。
这和mysql中的隐式类型转换有关,官方文档在末尾。
简单来说,mysql会自动推导数据类型,我们看一个列子。
笔者猜测由于类型转换失败,不进行匹配,所以仍然能查出结果。
回到宽字节的主题上,浏览器会将URL中'的编码为%27,经过函数添加的转义符,变成了%5c%27(\‘),如果在 “‘” 前面添加%df,编码后的数据为%df%5c%27。
如果查询的数据库是GBK编码时,会被认为是一个汉字,这里是”運“,也就是说最终语句变成了SELECT * FROM users WHERE id='1運' LIMIT 0,1(上面网页中为编码为UTF-8,无法正常显示)。添加的转义符号被“吃”掉了,转义符失去了原有的作用。
知道了这一点,后续的注入就很简单了。
order by 确定字段列数。
查看回显。
后面的查库、查表、查列、查数据就很顺利了。
能不能sqlmap直接一把梭?可以,不过需要更改下测试语句。
另外,sqlmap也提供了tamper来解决这种情况。
sqlmap -u "http://wuhash.ml/Less-36/?id=1" -b --tamper=unmagicquotes.py --batch --thread=10
如何发现宽字节注入后续的一些问题为什么输入%81就可以进行宽字节注入了?
GBK编码是对GB2312编码的扩展,采用双字节编码方案,其编码范围是 8140-FEFE,上面添加 %81 是为了让编码的结果在GBK编码范围中,将其识别为一个字符,从而“吃掉“转义符。
编码问题是如何发生的?
注入的过程设计到多个编码,包括php源码文件中指定SQL语句的编码,数据库的编码,页面本身的编码。
页面的编码有什么影响?添加的“%df”在URL中不会被再次编码,SQL语句指定编码我GBK,addslashes对单引号进行添加转义符号,添加的%df和转义发被解释为一个字符,同事页面返回的结果未正确显示,笔者的默认编码是Unicode(可声明编码),更换编码后可正确显示。
后续是P牛博客的思路,链接放在末尾。
如何防御?
php文档提供了mysql_real_escape_string函数,需要在声明数据库使用的编码,否则宽字节注入仍然会发生。
指定连接的形式是二进制即可,所有数据以二进制形式传递,就能有效避免宽字节注入。
SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary
只有GBK编码会发生吗?
实际上其他语言的编码也可以,只要能够“吃掉”转义符的编码。
还有其他姿势吗
在大多数的CMS中采用icnva函数,将UTF-8编码转换为GBK编码。
但实际上仍然会发生注入。
P牛提到“錦”的UTF-8编码为e9 8c a6,GBK编码为E55C。
转义符和单引号的编码为5c27,合起来是E55C 5c27。
两个5c被解释为转义符转义转义符本身,仅作为一个字符解释,所以注入仍然会发生。
二次编码注入