SQL注入之宽字节注入

About SQL Injection


宽字节注入

0x01 前言

对于SQL注入,我估计搞安全的都玩的滚瓜烂熟了,搞站什么的都是分分钟来的,但是之前做了一道宽字节注入的题目,又打开了我一扇通往新世界的大门(PS:早都碰到过,只不过一直没有时间写)。

0x02 宽字节和mysql

单字节字符集: 所有的字符都使用一个字节来表示,比如 ASCII 编码。

多字节字符集: 在多字节字符集中,一部分字节用多个字节来表示,另一部分(可能没有)用单个字节来表示。

两位的多字节字符有一个前导字节和尾字节。 在某个多字节字符集内,前导字节位于某个特定范围内,尾字节也一样。

UTF-8 编码: 是一种编码的编码方式(多字节编码),它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

常见的宽字节: GB2312、GBK、GB18030、BIG5、Shift_JIS,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象。(GB2312 不存在宽字节注入。)

在mysql中,用于转义(即在字符串中的符号前加上”\”)的函数有addslashes,mysql_real_escape_string,mysql_escape_string等,还有一种情况是magic_quote_gpc,不过高版本的PHP将去除这个特性。

注意:HTML页面设置的编码跟注入是没有任何联系的,它只不过使规定了HTML渲染时候的字符集设置,与服务器端以及数据库没有任何联系。

0x03 简单的宽字节示例

题目链接

出自Bugku的一道题目,非常基本。正常的传递单引号,没有任何效果。

image

查看源代码

image

红线标注了gb2312,这是html编码方式,但是提示了我们可能是这个方向,于是就尝试

http://103.238.227.13:10083/?id=1%df'

image
发现页面报错,也就是’没有被闭合,而是逃逸了出来生效了,SQL语句如下:

select * from key where id='1運'' limit 1 

这时候就明显的看出来了有个多余的单引号逃逸了,也就是你传递进去的那个单引号生效了,至此,这道题目的考察项已经完全get到了。

构造payload:

http://103.238.227.13:10083/?id=1%df' union select 1,string from sql5.key--+
那么问题来了,为什么会出现这种情况呢?

mysql设置的字符集为gbk,并且使用了那些上面所提到的过滤函数,在你传递’的时候,会在前面加上\,导致',使之被转义,从而无法闭合SQL语句,无法达到预期效果。

上面介绍了gbk是一种两个字节的编码方式,当传递%df’的时候,后台接收为%df%5c%27,这时候,数据库由于是gbk编码,所以数据库会认为%df%5c是一个中文字符’運’,转义的\被吃掉了,无法转义’,这时候就导致了’可以成功逃逸出来,可以对SQL语句进行下一步改造。

可以写个php页面验证下gbk编码中文字符的长度:

<?php
header('content-type:text/html;charsetr=gbk');
echo strlen("哇");
?>

这里还要提一点,不管你在单引号前面加什么字符,都要确保这个字符的ascii大于128,否则无法达到汉字的范围,也就无法使宽字节注入生效。

0x04 gbk与gb2312

前文提到了,gb2312不存在宽字节注入,我当时也很不理解,看了离别歌大佬的博客以后,顿有所悟(大佬终究是大佬)。

GBK与GB2312都是宽字节家族的一员,按理来说,GBK都存在宽字节注入,GB2312也存在,但是实际上,当采用字符集编码为GB2312时,宽字节注入这种情况便无法发生了,为什么呢?这要归结于GB2312编码的取值范围,它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE(PS:前一位是高位,后一位是低位),而\是0x5c,是不在低位范围中的。所以,0x5c也就是\根本不在gb2312的编码范围内,也就是说,无论如何,都无法构造出gb2312认识的编码,也就不存在吞掉\,无法构造注入了。

搬运大佬的一句总结:

把这个思路扩展到世界上所有多字节编码,我们可以这样认为:只要低位的范围中含有0x5c的编码,就可以进行宽字符注入。

0x05 进阶示例

  1. 在 PHP 代码中使用 mysql_query(“set names GBK”); 指定三个字符集(客户端、连接层、结果集)都是GBK编码。

demo:

<?php 
$name = addslashes($_GET['name']);
$con=mysqli_connect("localhost","root","fuckroot","sqli"); 
if (mysqli_connect_errno($con)) 
{ 
    echo "连接 MySQL 失败: " . mysqli_connect_error(); 
} 
// 执行查询
$sql = "select * from user where name = '".$name."'";
echo "SQL:".$sql."<br>";
mysqli_query($con,"set names GBK");
$result = mysqli_query($con,$sql);
if(!$result){
    // 打印错误原因
    printf("Error: %s\n", mysqli_error($con));
}
// echo("Res num_rows:".$result->num_rows."<br>");
// 一条条获取
while ($row=mysqli_fetch_row($result))
{
    printf ("%s : %s",$row[0],$row[1]);
    echo "<br>";
}
// 释放结果集合
mysqli_free_result($result);
// $row=mysqli_fetch_row($result);
echo "Closed!";
mysqli_close($con);
?>

利用的poc:

?name=111%de' or 1=1--+

这里利用了addslashes函数进行转义特殊字符,而我们传递进去的是%de’,就会变成0xde5c,而上文设定的字符集是GBK,之后就会产生宽字节注入,道理与上文一样。
2.
php的iconv函数,对数据进行转码,有三种情况:

2.1

$name = iconv("GBK","UTF-8", addslashes($_GET['name'])) ; 

页面设置都是UTF-8编码,但是页面为了接受部分用户以GBK编码方式提交的数据,会对用户的数据先进行转码,把GBK转为UTF-8,然后再拼接SQL语句,进行一系列的操作。但是,假如传递了一个%df进去,在拼接SQL语句之前,也就是转码的时候,这个单引号逃逸就已经发生了。因为GBK会把%df\解码为一个中文字符,然后进行存储。简单理解,也就是说,进行UTF转码之前,GBK就已经操作过了,所以即便进行UTF-8转码,转的也是已经生效的payload “運’”,无法对单引号进行实际操作。至此,单引号溢出,SQL注入形成。

利用的poc:

name=111%B3‘ or 1=1--+

2.2

$name = iconv("UTF-8","GBK", addslashes($_GET['name'])) ; 

这个跟第一个类似,就是把UTF-8的编码转码成GBK。其实按道理来说对字符串进行转义,然后转码,按照UTF-8的编码方式进行操作,应该不会出现单引号溢出的这种问题。但是,74CMS 3.4版存在这样的漏洞,这就很gay。

现在,直接传递一个GBK编码的中文字符“錦’”参数值,这个参数先被过滤函数转义:

graph LR
錦'-->錦\'

那么GBK编码的16进制结果就变成了0xE55C5C27,好,现在把数据传入数据库,用UIF-8对其进行解码,UTF-8并不认识0xE5,于是0xE5就变成了乱码,而0x5C5C27会被正常解码为“\‘”,就是:

graph LR
0xE55C5C2-->�\\'

现在,出现了两个转义用的反斜线,这时候,单引号就可以成功溢出了,因为第一个反斜线转义了第二个反斜线,使第二个反斜线失去了转义功能,也就导致了单引号的逃逸。至此,成功引发了SQL注入。

利用的POC:

name=錦' or 1=1--+
name=%E9%8C%A6' or 1=1--+

2.3

这种情况,我认为,基本不会出现,不过网络世界这么大,啥鸟都有,也说不定。

这种是对UTF-8的编码进行GBK转码,然后使用过滤函数进行转义:

$name = iconv("UTF-8","GBK", $_GET['name']) ; 
$name = addslashes($name);

看起来就很奇葩,但是有可能存在,利用的POC与2.2一样,只不过解析过程不一样。传递参数:

name=錦'

先进行GBK转码(为了方便理解,我都以16进制表示):

graph LR
0xE98CA627-->0xE55C27

然后进行过滤函数转义:

graph LR
0xE55C27-->0xE55C5C5C27

惊奇的发现,5C多了两个。这因为,进行完GBK转码以后,在进行过滤函数转义的时候,会把0x5C认为是一个\特殊字符,所以会对这个反斜线进行转义,也就是多加一个反斜线变成\\,也就是5C5C,之后数据进入数据库,GBK对其进行解码,0xE55C会被解码为“錦”,0x5C5C27会被解码为“\\‘”,也就是:

graph LR
0xE55C5C5C27-->錦\\'

至此,又跟2.2介绍的一样了,殊途同归,单引号成功逃逸,SQL注入产生。

0x06 小结

关于宽字节,我目前也就在CTF里面接触到了,不过既然碰到了我就写一写,这方面确实菜的不行,所以避免不了去看一些大佬的分析,也学到了很多的东西。

防范:

1.gbk编码造成的宽字符注入问题,解决方法是设置character_set_client=binary。

2.单独调用set names gbk和mysql_real_escape_string是无法避免宽字符注入问题的。还得调用mysql_set_charset来设置一下字符集。

3.谨慎使用iconv来转换字符串编码,很容易出现问题。只要我们把前端html/js/css所有编码设置成gbk,mysql/php编码设置成gbk,就不会出现乱码问题。不用画蛇添足地去调用iconv转换编码,造成不必要的麻烦。

4.在代码审计或网站开发中需要留意一下,类似 GBK 和 BIG5 这种的双字节编码,同时存在低位可以是 %5C 的字符。

PS:最近老是写到凌晨,有不足或者错误的地方,看到了希望可以及时联系我进行纠正。

特别鸣谢

浅析白盒审计中的字符编码以及SQL注入|离别歌

宽字节注入深入研究|Anka9080’s|不忘初心


Reprint please specify: wh1te SQL注入之宽字节注入

Previous
文件上传(一) 文件上传(一)
About File Upload 0x00 前言文件上传什么的,都属于WEB漏洞的基础,虽说是基础,但是也是能get到很多知识点的,再加上各种变化,也就可以实现任意文件的上传。 关于文件上传,在github中也有一个文件上传的靶场,u
2018-08-10
Next
一句话木马 一句话木马
About WebShell WebShell关于webshell在这里做一个简单的讲解,就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。通过这个后门,可以使用特定工具(比如:
2018-07-23
TOC