在闯关之前先来说说什么是宽字节注入。
宽字节注入原理
宽字节顾名思义就是两个及以上的字节表示一个中文汉字。在sql注入中,当用户输入中包含特殊字符,比如(单引号’),(双引号"),(斜杠/)等,为了过滤用户输入的这些特殊字符,会在字符前面加一个\转义字符对特殊字符进行转义。而大多数网站使用utf-8编码,utf-8编码会认为一个中文字符占三个字节;而后端mysql常用GBK编码,GBK编码认为一个中文占两个字节。所以针对这个原理,我们可以构造中文汉字来绕过字符转义。
绕过原理
当输入id=1'的时候,网站的utf-8编码会将1’编码为1\',这里就网站就自动添加了\将后面的'进行了转义;其url编码为1%5c%27。其中%5c是\的url编码;%27是'的url编码。当该语句到后端执行的时候,mysql的编码可是会将%5和%27结合在一起形成两个字节来判断中文汉字。注意,只有当前一个符号的ASCII大于或等于128的时候才能到达汉字范围。
所以为了避免'被转义,我们手动在%5c前面加入一个%df的url编码。(为什么加%df?因为%df的ascii编码刚好是128,所以加上该编码可以达到汉字范围,添加其他的也可以,只要ascii编码大于等于128即可。)这个时候我们的输入变成了id=1%df',同理,url编码会编码为id=1%df%5c%27。这个时候该语句带入到后端执行,GBK编码会认为%df%5c这两个字节构成了一个’運’字,后面的%27仅剩下一个字节,所以不会被解析为汉字。这个时候语句就变成了id=運',就绕过了字符的转义。
接下来进行实战,靶场为sqli-32
进入sqli32.
判断注入点和普通注入类似。首先在url中传参id=1和id=2页面发生了变化,说明前后端发生了交互。
判断字符型还是数字型。url端输入and 1=1和 and 1=2页面没有发生变化,但是url传参id=1'时,页面发生了变化,观察页面提示信息,多出了一个\,判断后端对特殊字符进行了转义。
and 1=2
id=1'
按照上面说的注入原理,在url端输入id=1%df' order by 4 --qwe页面报错。将order by 4改为order by 3页面没有发生报错,判断字段数为3.
接下来的注入方式就和联合注入差不多啦。
判断占位符的回显位,待会儿我们就要利用回显位置查询出数据库里面存储的相关信息。
注意这里输入id值得时候要构造一个数据库中没有的id号,我这里输入的是id=50,一般输入id=0即可。因为这里只能显示出一个结果,所以当union前面的selec语句能够正确查询出结果的时候只能显示前面selec语句的结构,后面的select语句即使正确,也无法显示出结果,就无法显示出回显位。所以只有当union前面的语句报错的时候才可以先输出后面语句的查询结果。
上面结果可以看出回显位在2,3的位置,接下来构造sql语句查询数据库里面的信息。使用database()和version()函数查看当前数据库和数据库版本。
我们可以看到数据库版本在5.0以上。所以我们可以使用information_schema这个数据库来查询我们想要的信息。
爆破出当前数据库下面的所有表名。 构造的查询语句为:
union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()-- qwe
接下来我们就可以根据爆破出的表名,查询相应表中的信息。这里以users为例,查询users表中的信息。
但是输入union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'-- qwe报错。因为table_name='users'中的‘也被转义了。
解决办法:使用十六进制进行查询。
网上搜索一个在线十六进制文本转换器。获取users的十六进制值,利用这个值查询便可以得到我们想要查询的信息。
将后面的十六进制代替url中的'users',别忘了前面加上0x表示十六进制。
构造的查询语句:union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273-- qwe
其他信息以同样的方式查询即可,这里就不在做过多赘述了。
出了以上手动注入方法我们还可以使用sqlmap工具进行自动化sql注入,之后会出一篇文章。今天就写到这里,吃饭去了~~