0x00 前言

宽字节注入是mysql的特性。当PHP和数据库使用的编码不一致时,特定情况下会造成SQL注入。

0x01 名词解释

单字节:ASCII编码就是单字节编码方式。如字符 a 编码为 %61

双字节:GBK编码采用的是双字节编码方式。如汉字 蠻 的编码为 %d0%55。(GBK编码的第一个字节要大于128才属于汉字范围)

宽字节注入1.png

三字节: UTF-8就是三字节编码。如汉字 汉 的编码为 %E6%B1%89

宽字节:双字节及以上的编码方式

魔术引号:PHP中,为了防止SQL注入等情况,引入了魔术引号。当输入的数据中有

单引号'  、 双引号" 、 反斜杠\

字符时,会在其前面自动添加一个反斜杠 进行转义,使这个字符成文普通的文本。

在低于PHP 5.4.0的版本中,魔术引号默认是开启的。在PHP5.4.0开始,魔术引号被移除。

常见的魔术引号函数有magic_quotes_sybase()、magic_quotes_gpc()、magic_quotes_runtime、addslashes()、iconv()等。

0x02 宽字节注入条件

1、mysql数据库

2、开启魔术引号(输入指定字符会转义)

3、PHP和数据库的编码不相同。如PHP是UTF-8,数据库是GBK等。

0x03 宽字节注入原理

当PHP使用的是UTF-8三字节编码,而数据库使用的是GBK双字节编码时,若传参内容为 %df',魔术引号会自动对最后的单引号进行转义,输入的单引号 ' 就成了 ' ,反斜杠 的编码为 %5c ,最终传入数据库的就成了 %df%5c' ,而数据库是GBK双字节编码,认为%df%5c是一个字,造成了单引号逃逸。核心思想是编码原因,导致转移作用的反斜杠被前面字符吃掉。

在单引号 ' 前添加一个汉字也可以造成宽字节注入,汉字传参一般是UTF-8三字节编码。

如 汉' -> 汉 '

汉 一共四个字节,当数据库使用双字节编码时会认为这是两个汉字,同样造成单引号逃逸。

0x04 代码审计

宽字节注入2.png

第24行判断如果存在GET传参,若存在进入if语句。
第26行对传参内容传入check_addslashes()函数,并且将返回结果复制给变量id。

而check_addslashes()函数在第17行定义

宽字节注入3.png

这个函数对传递进来的内容传递给addslashes()函数,而addslashes()是PHP自带的转义函数。然后将转义后的结果进行返回。

宽字节注入4.png

在第36行,

mysql_query("SET NAMES gbk");

设定mysql数据库字符集为GBK编码

第37行将变量id的值拼接进SQL语句执行。

中间虽然有用转义函数进行转义,但是数据库编码为GBK方式。因此可以尝试宽字节注入。

0x05 靶场复现

正常页面:

宽字节注入5.png

输入数据包含单引号时会被转义:

宽字节注入6.png

尝试添加%df或汉字:

宽字节注入7.png

返回语法错误,是由于单引号逃逸造成的

获取数据库名:

宽字节注入8.png

0x05 其他

POST传参时,需要抓包修改Hex(%df是URL编码)

mysql支持十六进制,注入时若语句中的单引号被转义,可以使用十六进制。

?id=1%df' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = 'dbs' limit 0,1)))

可以写为

?id=1%df' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = 0x2764627327 limit 0,1)))

其中 0x2764627327 是 'dbs' 的十六进制表示。