防止sql手工注入
在php+mysql中, 可以通过转义特殊字符来防止污染sql语句(防注入),
有两种情况:
- 魔术引号, magic_quote_gpc 开关,不过高版本的PHP将去除这个特性
- 安全函数, addslashes,mysql_real_escape_string,mysql_escape_string等。
宽字节注入
上有计策下有对策, 当程序员设置数据库编码与php编码为不同的两种编码,那么就可能产生宽字节注入, 也称GBK双字节绕过。
也可以采用编码绕过。
涉及的基本概念
- 字符、字符集
字符(character)是组成字符集(character set)的基本单位。对字符赋予一个数值(encoding)来确定这个字符在该字符集中的位置。
- UTF8
由于ASCII表示的字符只有128个,因此网络世界的规范是使用UNICODE编码,但是用ASCII表示的字符使用UNICODE并不高效。因此出现了中间格式字符集,被称为通用转换格式,及UTF(Universal Transformation Format)。
- 宽字节
GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,即将两个ascii字符误认为是一个宽字节字符。
1. 引发原因
php编码为utf-8, 而mysql的编码设置为set names 'gbk'或set character_set_client=gbk,php中编码为gbk,函数执行添加的是
ascii编码,mysql默认字符集是gbk等宽字节字符集。
这样配置会引发编码转换从而导致的注入漏洞(一个gbk编码汉字,占用2个字节,一个utf-8编码的汉字,占用3个字节)
2. 注入原理
注入原理很简单, 就是编码, 一点一点分析:
假设一个url有注入, 但是有安全函数, 我们敲单引号会被过滤,那么怎么办呢?这时候就利用GBK双字节注入
我们在后边这么构造url一个尝试:
http://www.****.com/index.php?id=1%df' and 1=1
其中,
%df ' 经过安全函数之后在 ' 之前会被加上一个转义符号'\', 即: %df \'
由于采用的是url编码, 最后转化为:
%df%5c%27
关键就在这,%df会吃掉%5c,形成一个新的字节, 形象一点就是%df遇到%5c会把%5c吃掉,形成%df%5c,这个编码经过代码
解码后会形成一个汉字“誠”
还不明白? 没关系, 举一个简单的栗子:
xi'an (西安) ==> xian(先)
值得一提的是, 并不是唯一的使用%df, 只要编码超过ascii码(128)之后, 可以自己组合。只要是汉字就都可以使用
总的来解释一下,
因为%df的关系,\的编码%5c被吃掉了,也就失去了转义的效果,即安全函数丧失了作用, 被直接带入到mysql中,然后mysql在解读时无视了%df%5c形成的新字节,那么单引号便重新发挥了效果, 就可以构造注入了。
3. 实战
话不多说, 直接实战webug4.0 宽字符注入:
1) 尝试旧方法, 发现不管输入单引号还是整数型都无效, 于是初步判断有安全函数
2) 尝试宽字符注入:
可以看到, %df与转义符结合形成一个汉字(乱码), 然后构造的单引号重新发挥了作用, 注入漏洞就因此产生。
最后我们再在url最后加一个注释符, 将php代码中的字符串引号注释掉:
最终确认为宽字符注入型
3) 下面就是之前的常规操作了
- 爆字段数
构造url: http://localhost:32768/control/sqlinject/width_byte_injection.php?id=1%df%27%20order%20by%203%23
再输入2正常页面, 得出字段数为2
- 同理可以爆数据库:
恩, 挺不错的, 宽字节注入的题, 关于数据库也是相同的名字
- 爆表
果然, 引号还是会被安全函数转义, 这里采用编码绕过:
webug_width_byte 对应的十六进制编码: 0x77656275675f77696474685f62797465
猜测数据在sqlinjection下
- 爆字段+爆数据
最终发现数据不在webug_width_byte数据库中....哭晕...
还是老老实实爆webug那个数据库吧......应该还是在env_list那个表中..
值得一提的是, 1) 记得要用数据库.表来引用目标表, 因为不确定当前数据库下也有相同的表名,
2) 通过前面的几道题, 发现了第几题的数据在对应题号的id中。
4. 安全应对方案
对于宽字节编码,有一种最好的修补就是:
(1)使用mysql_set_charset(GBK)指定字符集
(2)使用mysql_real_escape_string进行转义
原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面%df和%5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?
就是使用mysql_set_charset进行指定。
上述的两个条件是“与”运算的关系,少一条都不行。
参考: