sqli-宽字节注入

0x0 背景

  1. 当某字符的大小为一个字节时,称其字符为窄字节.
  2. 当某字符的大小为两个字节时,称其字符为宽字节.
  3. 所有英文默认占一个字节,汉字占两个字节
  4. 常见的宽字节编码:GB2312,GBK,GB18030,BIG5,Shift_JIS等等

0x1 宽字节注入原理

 

 

程序员为了防止sql注入,对用户输入中的单引号(’)进行处理,在单引号前加上斜杠(\)进行转义,这样被处理后的sql语句中,单引号不再具有‘作用’,仅仅是‘内容’而已,换句话说,这个单引号无法发挥和前后单引号闭合的作用,仅仅成为‘内容‘

【再举个例子,要找某位名字里带单引号的用户,搜索的时候,就要让单引号成为内容去搜索,而不能起到其他作用】

 

而安全测试人员要绕过这个转义处理,使单引号发挥作用,有两个思路:

  1. 让斜杠(\)失去作用
  2. 让斜杠(\)消失

 

第一个思路就是借鉴程序员的防范思路,对斜杠(\)转义,使其失去转义单引号的作用,成为‘内容’

第二个思路就是宽字节注入

 

 

 

当使用宽字节编码,如:GBK时,两个连在一起的字符会被认为是汉字,我们可以在单引号前加一个字符,使其和斜杠(\)组合被认为成汉字,从未达到让斜杠消失的目的,进而使单引号发挥作用

注意:前一个字符的Ascii要大于128,两个字符才能组合成汉字

 

0x2 注入方法

0x21 黑盒

 

 

 

可以看到,在发现单引号被转义后,当我们加了%df后,sql语句报错,说明单引号发挥了作用,斜杠与%df组合

 

 

0x22白盒

 

 

1.gbk编码

 

 

2.单引号被替换成\’

 

 

 

3.php内置函数addslashes进行转义(lesson33)

 

4.mysql_real_escape_string

这个mysql内置的函数可以把sql语句中字符串的特殊字符进行转义,同时考虑连接的字符集,可以用来解决宽字节注入,但某些场景仍不行,因为程序没有指定php连接mysql的字符集。

如果要使用这个函数防御,要在执行sql语句之前调用一下mysql_set_charset函数,设置当前连接的字符集为gbk,再调用mysql_real_escape_string来过滤用户输入

0x23 sqlmap

需要注意的是,对于宽字节注入场景,直接

python sqlmap.py -u "http://localhost/sqli-labs-master/Less-32/?id=1"

是找不到注入的

 

 

必须得

python sqlmap.py -u "http://localhost/sqli-labs-master/Less-32/?id=1%df%27"

还可以加些参数:

--threads 10 //如果你玩过 msfconsole的话会对这个很熟悉 sqlmap线程最高设置为10

--level 3 //sqlmap默认测试所有的GET和POST参数,当--level的值大于等于2的时候也会测试HTTP Cookie头的值,当大于等于3的时候也会测试User-Agent和HTTP Referer头的值。最高可到5

--risk 3 // 执行测试的风险(0-3,默认为1)risk越高,越慢但是越安全

----search //后面跟参数 -D -T -C 搜索列(S),表(S)和或数据库名称(S) 如果你脑子够聪明,应该知道库列表名中可能会有ctf,flag等字样,结果有时候题目就是这么耿直对吧?

 

 

//我最后还是没成功用sqlmap注入

0x3防御

 

借鉴内容:https://www.leavesongs.com/PENETRATION/mutibyte-sql-inject.html

 

在0x22.3中我们说到了一种修复方法,就是先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string来过滤用户输入。

 

这个方式是可行的,但有部分老的cms,在多处使用addslashes来过滤字符串,我们不可能去一个一个把addslashes都修改成mysql_real_escape_string。我们第二个解决方案就是,将character_set_client设置为binary(二进制)。

 

只需在所有sql语句前指定一下连接的形式是二进制:

 

SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary

这几个变量是什么意思?

 

当我们的mysql接受到客户端的数据后,会认为他的编码是character_set_client,然后会将之将换成character_set_connection的编码,然后进入具体表和字段后,再转换成字段对应的编码。

 

然后,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。

 

所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。

 

比如,我们的phithon内容管理系统v2.0版本更新如下:

 

 

 

已经不能够注入了:

 

 

 

在我审计过的代码中,大部分cms是以这样的方式来避免宽字符注入的。这个方法可以说是有效的,但如果开发者画蛇添足地增加一些东西,会让之前的努力前功尽弃。

 

0x4总结

在逐渐国际化的今天,推行utf-8编码是大趋势。如果就安全性来说的话,我也觉得使用utf-8编码能够避免很多多字节造成的问题。

不光是gbk,我只是习惯性地把gbk作为一个典型的例子在文中与大家说明。世界上的多字节编码有很多,特别是韩国、日本及一些非英语国家的cms,都可能存在由字符编码造成的安全问题,大家应该有扩展性的思维。

总结一下全文中提到的由字符编码引发的安全问题及其解决方案:

  1. gbk编码造成的宽字符注入问题,解决方法是设置character_set_client=binary。
  2. 矫正人们对于mysql_real_escape_string的误解,单独调用set names gbk和mysql_real_escape_string是无法避免宽字符注入问题的。还得调用mysql_set_charset来设置一下字符集。
  3. 谨慎使用iconv来转换字符串编码,很容易出现问题。只要我们把前端html/js/css所有编码设置成gbk,mysql/php编码设置成gbk,就不会出现乱码问题。不用画蛇添足地去调用iconv转换编码,造成不必要的麻烦。

参考文档:https://www.leavesongs.com/PENETRATION/mutibyte-sql-inject.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值