SQL注入防御绕过——宽字节注入
原因
由于sql注入的盛行,不少网站管理员都意识到了这种攻击方式的厉害,纷纷想出不少办法来避免,例如使用一些 Mysql 中转义的函数 addslashes,mysql_real_escape_string, mysql_escape_string等等。其实这些函数就是为了过滤用户输入的一些数据,对特殊的字符加上反斜杠“\”进行转义。
- Addslashes()函数:对get、post、cookie等传递的参数中的’、“、\、null等进行转义
- mysql_real_escape_string()函数:转义如\x00、\n、\r、\、’、“、\x1a等
- mysql_escape_string:注意,php5.3中已经不使用。
预备知识
空格 %20
单引号 %27
# %23
\ %5C
字符集
在了解宽字节注入之前,我们先来看一看字符集是什么。字符集也叫字符编码,是一种将符号转换为二进制数的映射关系。
几种常见的字符集:
-
ASCII 编码:单字节编码
-
latin1 编码:单字节编码
-
gbk 编码:一个字符占1个字节**,两个字节以上叫宽字节**,设置“set character_set_client=gbk”(gbk编码设置),通常导致编码转换的注入问题,尤其是使用php 连接mysql数据库的时候
一个gbk汉字占两个字节,取值范围是(编码位数):第一个字节是(129-254),第二个字节(64-254),
当设置gbk编码后,遇到连续两个字节,都符合gbk取值范围,会自动解析为一个汉字
-
UTF-8 编码:使用一至四字节编码, 0x00–0x7F 范围内是一位,和 ASCII 保持一致。其它字符用二至四个字节变长表示。 由于ASCII表示的字符只有128个,因此网络世界的规范是使用UNICODE编码,但是用ASCII表示的字符使用UNICODE并不高效。因此出现了中间格式字符集,被称为通用转换格式,及UTF(UniversalTransformation Format)。
每种编码方式的特点:
-
ASCII 每个字符占据1bytes,用二进制表示的话最高位必须为0(扩展的ASCII不在考虑范围内),因此ASCII只能表示128个字
-
GB2312 最早一版的中文编码,每个字占据2bytes。由于要和ASCII兼容,那这2bytes最高位不可以为0了(否则和ASCII会有冲突)。
-
一个gbk汉字占两个字节,取值范围是(编码位数):第一个字节是(129-254),第二个字节(64-254),当设置gbk编码后,遇到连续两个字节,都符合gbk取值范围,会自动解析为一个汉字
Mysql字符转换
数据提交到MySQL数据库,需要进行字符集的转换,使得MySQL数据库可以对数据进行处理,这一过程一般有以下三个步骤:
-
收到请求,将请求数据从 character_set_client -> character_set_connection 。
-
内部操作,将数据从 character_set_connection -> 表创建的字符集 。
-
结果输出,将数据从 表创建的字符集 -> character_set_results
当执行 set names “charset” ,相当于执行:
set character_set_client = charset
set character_set_connection = charset
set character_set_results = charset
-
client 指的是PHP程序
-
connection 指的是PHP客户端与MySQL服务器之间连接层
-
results 指的是MySQL服务器返回给PHP客户端的结果
MySQL常见的乱码问题就是这三个字符集的设置不当所引起的
宽字节注入原理
宽字节注入发生的位置就是PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码。在使用PHP连接MySQL的时候,当设置“character_set_client = gbk”时会导致一个编码转换的问题,也就是我们熟悉的宽字节注入,两个以上的字节就是宽字节
宽字节注入原理即是利用编码转换,将服务器端强制添加的本来用于转义的\符号吃掉,从而能使攻击者输入的引号起到闭合作用,以至于可以进行SQL注入
这里的宽字节注入是利用mysql的一个特性,mysql在使用GBK编码(GBK就是常说的宽字节之一,实际上只有两字节)的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围),而当我们输入有单引号时会自动加入\进行转义而变为\’(在PHP配置文件中magic_quotes_gpc=On的情况下或者使用addslashes函数,icov函数,mysql_real_escape_string函数、mysql_escape_string函数等,提交的参数中如果带有单引号’,就会被自动转义\’,使得多数注入攻击无效),由于宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,将后面的一个字节与前一个大于128的ascii码进行组合成为一个完整的字符(mysql判断一个字符是不是汉字,首先两个字符时一个汉字,另外根据gbk编码,第一个字节ascii码大于128,基本上就可以了),此时’前的\就被吃了,我们就可以使用’了,利用这个特性从而可实施SQL注入的利用
最常使用的宽字节注入是利用%df,其实我们只要第一个ascii码大于128就可以了,比如ascii码为129的就可以,但是我们怎么将他转换为URL编码呢,其实很简单,我们先将129(十进制)转换为十六进制,为0x81,然后在十六进制前面加%即可,即为%81。另外可以直接记住GBK首字节对应0×81-0xFE,尾字节对应0×40-0xFE(除0×7F),则尾字节会被吃掉,如转义符号\对应的编码0×5C!另外简单提一下,GB2312是被GBK兼容的,它的高位范围是0xA1-0xF7,低位范围是0xA1-0xFE(0x5C不在该范围内),因此不能使用编码吃掉%5c
- 字符、字符集与字符序
字符(character)是组成字符集(character set)的基本单位。对字符赋予一个数值(encoding)来确定这个字符在该字符集中的位置
字符序(collation)指同一字符集内字符间的比较规则
- 宽字节
GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象
GBK是一种多字符的编码,通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。当将页面编码保存为gbk时输出2,utf-8时输出3。除了gbk以外,所有ANSI编码都是2个字节
宽字节注入防御
对于宽字节编码,有一种最好的修补就是:
-
使用 mysql_set_charset(GBK) 指定字符集
-
使用 mysql_real_escape_string 进行转义
原理是, mysql_real_escape_string 与 addslashes 的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?就是使用 mysql_set_charset 进行指定。上述的两个条件是“与”运算的关系,少一条都不行
Cookie注入攻击
我们知道,一般的防注入程序都是基于“黑名单”的,根据特征字符串去过滤掉一些危险的字符。一般情况下,黑名单是不安全的,它存在被绕过的风险。比如有的防注入程序只过滤了通过GET、POST方式提交的数据,对通过Cookie方式提交的数据却并没有过滤,我们可以使用Cookie注入攻击。简单说, cookie是服务器给客户端的一种加密凭证,通常由客户端存储在本地,比如客户A 访问 XXXX.com,网页给了客户A一个123的凭证,客户B也去访问那个网址,网站给B一个456的凭证,以后A和B去访问那个网站的时候只要加上了那个凭证网站就可以把两个人分开了
Cookie注入原理
Cookie最先是由Netscape(网景)公司提出的,Netscape官方文档中对Cookie的定义是这样的:Cookie是在HTTP协议下,服务器或脚本可以维护客户工作站上信息的一种方式
Cookie的用途非常广泛,在网络中经常可以见到Cookie的身影。它通常被用来辨别用户身份、进行session跟踪,最典型的应用就是保存用户的账号和密码用来自动登录网站和电子商务网站中的“购物车”
Cookie注入简单来说就是利用Cookie而发起的注入攻击。从本质上来讲,Cookie注入与传统的SQL注入并无不同,两者都是针对数据库的注入,只是表现形式上略有不同罢了
Request对象的使用方法一般是这样的:request.[集合名称](参数名称),比如获取从表单中提交的数据时可以这样写:request.form(“参数名称”),但ASP中规定也可以省略集合名称,直接用这样的方式获取数据:request(“参数名称”),当使用这样的方式获取数据时,ASP规定是按QueryString、Form、Cookies、ServerVariables的顺序来获取数据的。这样,当我们使用request(“参数名称”)方式获取客户端提交的数据,并且没有对使用request.cookies(“参数名称”)方式提交的数据进行过滤时,Cookie注入就产生了
Cookie注入步骤
- 寻找形如“.asp?id=xx”类的带参数的URL
- 去掉“id=xx”查看页面显示是否正常,如果不正常,说明参数在数据传递中是直接起作用的。如果正常,则说明使用cookie作为参数传递
- 使用burp抓包并构造payload
- 使用常规注入语句进行注入即可
base64注入攻击
base64注入原理
base64注入是针对传递的参数被base64编码后的注入点进行注入。这种方式常用来绕过一些WAF的检测。如果有WAF,则WAF会对传输中的参数ID进行检查,但由于传输中的ID经过base64编码,所以此时WAF很有可能检测不到危险代码,进而绕过了WAF检测
判断是否存在注入
对参数进行base64编码,如下所示:id=1,id=1’,id=1 and 1=1,id=1 and 1=2,编码后:id=MQ==,id=MSc=,id=MSBhbmQgMT0x,id=MSBhbmQgMT0y来判断是否存在SQL注入漏洞