对宽字节的深入了解

01 背景知识

字符集

在了解宽字节注入之前,我们先来看一看字符集是什么。字符集也叫字符编码,是一种将符号转换为二进制数的映射关系。
几种常见的字符集:

  • ASCII编码:单字节编码
  • latin1编码:单字节编码
  • gbk编码:使用一字节和双字节编码,0x00-0x7F范围内是一位,和 ASCII 保持一致。双字节的第一字节范围是0x81-0xFE
  • UTF-8编码:使用一至四字节编码,0x00–0x7F范围内是一位,和 ASCII 保持一致。其它字符用二至四个字节变长表示。

宽字节就是两个以上的字节,宽字节注入产生的原因就是各种字符编码的不当操作,使得攻击者可以通过宽字节编码绕过SQL注入防御。

通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。在php中,我们可以通过输出

echo strlen("和");

来测试。当将页面编码保存为gbk时输出2,utf-8时输出3。 除了gbk以外,所有ANSI编码都是2个字节。ansi只是一个标准,在不同的电脑上它代表的编码可能不相同,比如简体中文系统中ANSI就代表是GBK。

02 概述

首先我们了解下宽字节注入,宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入

数据提交到MySQL数据库,需要进行字符集的转换,使得MySQL数据库可以对数据进行处理,这一过程一般有以下三个步骤:

  1. 收到请求,将请求数据从 character_set_client ->character_set_connection
  2. 内部操作,将数据从character_set_connection-> 表创建的字符集
  3. 结果输出,将数据从表创建的字符集 -> character_set_results

当执行set names "charset",相当于执行
set character_set_client = charset
set character_set_connection = charset
set character_set_results = charset

client 指的是PHP程序
connection 指的是PHP客户端与MySQL服务器之间连接层
results 指的是MySQL服务器返回给PHP客户端的结果

MySQL常见的乱码问题就是这三个字符集的设置不当所引起的。

宽字符是指两个字节宽度的编码技术,如UNICODE、GBK、BIG5等。当MYSQL数据库数据在处理和存储过程中,涉及到的字符集相关信息包括:

  • (1) character_set_client:客户端发送过来的SQL语句编码,也就是PHP发送的SQL查询语句编码字符集。
  • (2) character_set_connection:MySQL服务器接收客户端SQL查询语句后,在实施真正查询之前SQL查询语句编码字符集。
  • (3) character_set_database:数据库缺省编码字符集。
  • (4) character_set_filesystem:文件系统编码字符集。
  • (5) character_set_results:SQL语句执行结果编码字符集。
  • (6) character_set_server:服务器缺省编码字符集。
  • (7) character_set_system:系统缺省编码字符集。
  • (8) character_sets_dir:字符集存放目录,一般不要修改。

宽字节对转义字符的影响发生在character_set_client=gbk的情况,也就是说,如果客户端发送的数据字符集是gbk,则可能会吃掉转义字符\,从而导致转义失败。

例如说PHP的编码为 UTF-8 而 MySql的编码设置为了
SET NAMES 'gbk' 或是 SET character_set_client =gbk,这样配置会引发编码转换从而导致的注入漏洞。


03 addslashesmysql_real_escape_string的区别


 (1)ASCII(NULL)字符\x00,

 (2)换行字符\n,addslashes不转义

 (3)回车字符\r,addslashes不转义

 (4)反斜杠字符\,

 (5)单引号字符‘,

 (6)双引号字符“,

 (7)\x1a,addslashes不转义

1.AddSlashes()

首先来观察一下是如何通过构造吃掉转义字符的

先将less 34的网页编码换成gbk

加上一些输出

echo "Before addslashes(): " . $uname1 . "<br/>";
        $uname = addslashes($uname1);
        $passwd= addslashes($passwd1);
        echo "After addslashes(): " . $uname . "<br/>";
        
        //echo "username after addslashes is :".$uname ."<br>";
        //echo "Input password after addslashes is : ".$passwd;    

    // connectivity 
    mysql_query("SET NAMES gbk");
    @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
    echo "SQL Statement: " . $sql . "<br/>";

POST提交

uname=%df' or 1#&passwd=1

%df即ASCII码十六进制为df的字符  ß

(注:%df为URL编码,URL编码即是255个ASCII字符的十六进制数值加上一个前置的%,可参考:http://www.w3school.com.cn/tags/html_ref_urlencode.html)

 POST提交

uname=ß' or 1#&passwd=1

可以的到和提交%df一样的效果

现在来观察一下页面的输出

Before addslashes(): �' or 1#
After addslashes(): 運' or 1#

(注意在网页编码为UTF-8的时候  運 字是无法被显示的)

After addslashes(): �\' or 1#

addslashes()函数只是对单引号进行了转义处理,df字符没有做任何处理,在网页编码为GBK时,df 和 \ 字符合并成了一个gbk字符  運 

在注入操作的时候,合并的过程是在MYSQL执行查询操作的时候

SELECT username, password FROM users WHERE username='�\' or 1#' and password='1' LIMIT 0,1#注意此时输入的引号并没有引发闭合
mysql_query("SET NAMES gbk");#由于客户端指定了编码gbk
SELECT username, password FROM users WHERE username='運' or 1#' and password='1' LIMIT 0,1#查询语句由utf-8转向gbk编码,df 和 5c (即\)合并成为了一个gbk字符

理解了这个过程,再来看看GBK编码,编码的范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,字符 \ 的ASCII码为5C,在其低位范围内,就可能被吃掉,并且能吃掉它的字符不仅只有0xdf

取一个实验一下(参考: http://www.bo56.com/gbk汉字内码扩展规范编码表)

比如 乗 字符,GBK编码 815C

那么构造

uname=%81' or 1#&passwd=1

另外,GB2312编码范围是A1A1-FEFE,其中汉字编码范围:B0A1-F7FE,即高位A1~FE,低位A1~FE,字符 \ 的ASCII码为5C,不在其低位范围内,自然不会被吃掉。

 2.mysql_real_escape_string()

Less 36和Less 34用一样的payload注入,因此看不出来addslashes()和mysql_real_escape_string()的区别

 参考:http://www.laruence.com/2010/04/12/1396.html

解释一下这个输出,

addslashes函数读取每个字节,915c按照两个字节读取,发现5c后进行转义: 915c --> 915c5c

控制台为gbk编码所以显示 慭\

第二个第三个输出都是一个原理

对于mysql_real_escape_string()函数,需要注意在使用mysql_set_charset("gbk")后,没有进行转义操作(注意看上面的代码),

也就是说mysql_real_escape_string()函数正确读取了gbk字符(而不是两个ASCII字符,需要注意UTF-8对于ASCII字符的编码是和ASCII码相同的),

前面三种都属于一种 “误转义” ,因此,对于宽字节注入,可以使用第四种方式避免

来实验一下

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

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=gbk" />
    <title>mysql_real_escape_string</title>
</head>
<body>
<?php
    $conn = mysql_connect("localhost", "root", "toor") or die("Error!");
    //mysql_query("SET NAMES 'gbk'"); //mysql_set_charset包含了这个操作
    mysql_select_db("security", $conn);
    mysql_set_charset("gbk", $conn);
    $id = isset($_GET["id"]) ? mysql_real_escape_string($_GET["id"]) :1;
    $sql = "SELECT * FROM users WHERE id='{$id}'";
    echo $sql. "<br/>";
    $result = mysql_query($sql, $conn) or die(mysql_error());
    $row = mysql_fetch_array($result, MYSQL_ASSOC);
    echo "<h2>{$row['username']}</h2>";
    mysql_free_result($result);
?>
</body>
</html>

【1】正常提交

http://localhost/1.php?id=1

【2】单引号提交(正确转义)

【3】尝试注入宽字节

如果引号闭合成功,union select 会返回结果,页面会有输出

http://localhost/1.php?id=0%df' union select 1,2,3%23

并没有得到输出(因为mysql_set_charset("gbk", $conn);先进行了编码

先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string函数来过滤用户输入。

上文中代码使用了mysql_set_charset(“gbk”)来设置编码,他会修改mysql对象中的mysql->charset属性为设置的字符集。
同时配套的过滤函数为mysql_real_escape_string()mysql_real_escape_string()会根据mysql对象中的mysql->charset属性来对待传入的字符串,因此可以根据当前字符集来进行过滤。

来使用sqlmap测试一下

python sqlmap.py -u http://localhost/mysql_real_escape_string.php?id=1 --tamper unmagicquotes.py --dbms mysql

3.character_set_client=binary 和 iconv()

参考此处:https://www.leavesongs.com/PENETRATION/mutibyte-sql-inject.html#005-iconv

使用character_set_client=binary来避免宽字节注入,但误用iconv()函数导致的注入问题,可能发生在搜索框中

先看一下测试代码

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>iconv_utf2gbk</title>
</head>
<body>
<?php

    function strToHex($string)
    {
        $hex='';
        for ($i=0; $i < strlen($string); $i++)
        {
            $hex .= dechex(ord($string[$i]));
        }
        return $hex;
    }

    $conn = mysql_connect("localhost", "root", "toor") or die("Error!");
    mysql_query("SET NAMES 'gbk'");
    mysql_select_db("security", $conn);
    mysql_query("SET character_set_connection=gbk,character_set_results=gbk,character_set_client=binary", $conn);
    
    echo "id: " . $_GET["id"] ." ==> ". strToHex($_GET["id"]) . "<br/>";
    $id = isset($_GET["id"]) ? addslashes($_GET["id"]) :1;
    echo "addslashes(): " . $id ." ==> ". strToHex($id) . "<br/>";

    //  iconv(string $in_charset , string $out_charset , string $str )
    @$id = iconv('utf-8', 'gbk', $id);
    echo "iconv(): " . $id ." ==> ". strToHex($id) . "<br/>";

    $sql = "SELECT * FROM users WHERE id='{$id}'";
    echo $sql. "<br/>";
    $result = mysql_query($sql, $conn) or die(mysql_error());
    $row = mysql_fetch_array($result, MYSQL_ASSOC);
    echo "<h2>{$row['username']}</h2>";
    mysql_free_result($result);
?>
</body>
</html>

一些正常的请求

(注意语句执行是成功的,只是没有查询结果而已)

一个可能造成注入的请求

http://localhost/iconv_utf82gbk.php?id=錦

出现了MySQL的报错,注意SQL语句,\' 是 ' 的转义,用来闭合的单引号被转义了,SQL语句没有闭合,所以出现错误

来看一下产生的原因,錦 的UTF8编码为e98ca6(三个字节),GBK编码为e55c,character_set_client=binary使得 ' 被 5c 转义,语句没有闭合

同样,GBK编码低位为5c的都可以造成这个错误,運 GBK编码 df5c

那么利用这个5c,就可以造成注入了,

构造这个请求

http://localhost/iconv_utf82gbk.php?id=運'%23

没有了错误,addslashes()函数转义了' 转义的\ 和5c 形成了一个新的转义(\的转义为\\),我们输入的 ' 成功闭合了语句,原来用来闭合的 ' 被 # 注释

http://localhost/iconv_utf82gbk.php?id=運' union select 1,database(),3%23

转换:(UTF-8: e9%81%8b 转GBK为 %df%5c
%e9%81%8b%27====>(addslashes)====>%e9%81%8b%5c%27====(iconv)====>%df%5c%5c%27
可以看到,多出了一个%5c,将转义符(反斜杠)本身转义,使得后面的%27单引号发挥了作用  (注:反斜杠的优先级高,所以%df%5c%5c优先变为%df\\

sqlmap并不能发现这个注入点

>python sqlmap.py -u http://localhost/iconv_utf82gbk.php?id=1 -tamper unmagicquotes.py --dbms mysql

 同样的,gbk转向utf-8时也会出现类似的漏洞

测试代码如下

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>iconv_gbk2utf</title>
</head>
<body>
<?php

    function strToHex($string)
    {
        $hex='';
        for ($i=0; $i < strlen($string); $i++)
        {
            $hex .= dechex(ord($string[$i]));
        }
        return $hex;
    }

    $conn = mysql_connect("localhost", "root", "toor") or die("Error!");
    mysql_query("SET NAMES 'gbk'");
    mysql_select_db("security", $conn);
    mysql_query("SET character_set_connection=gbk,character_set_results=gbk,character_set_client=binary", $conn);
    
    echo "id: " . $_GET["id"] ." ==> ". strToHex($_GET["id"]) . "<br/>";
    $id = isset($_GET["id"]) ? addslashes($_GET["id"]) :1;
    echo "addslashes(): " . $id ." ==> ". strToHex($id) . "<br/>";

    //  iconv(string $in_charset , string $out_charset , string $str )
    @$id = iconv('gbk', 'utf-8', $id);
    echo "iconv(): " . $id ." ==> ". strToHex($id) . "<br/>";

    $sql = "SELECT * FROM users WHERE id='{$id}'";
    echo $sql. "<br/>";
    $result = mysql_query($sql, $conn) or die(mysql_error());
    $row = mysql_fetch_array($result, MYSQL_ASSOC);
    echo "<h2>{$row['username']}</h2>";
    mysql_free_result($result);
?>
</body>
</html>

构造请求

http://localhost/iconv_gbk2utf8.php?id=1%e5%27%23

变回了一个普通的宽字节注入,e55c(GBK) 通过iconv函数转向了 e98ca6(UTF-8)

转义的 \ 在编码转换中消失了

http://localhost/iconv_gbk2utf8.php?id=0%e5' union select 1,database(),3%23

4.总结一下

1.宽字节注入跟HTML页面编码无关。

2 Mysql编码与过滤函数推荐使用mysql_real_escape_string()mysql_set_charset()

3 addslashes()函数和mysql_real_escape_string()函数并不等价,addslashes()函数无法抵御宽字节注入,配置正确的mysql_real_escape_string()函数可以抵御宽字节注入,但对于整型注入,这两个函数都无能为力。

错误的使用iconv()函数可能使得转义字符被吃掉,造成注入点。

附一个255位的ASCII码表

Bin           Dec         Hex           缩写/字符                                       解释
0000     0          00             NUL (null)                                      空字符    
0001     1          01             SOH (start of handing)                          标题开始  
0010     2          02             STX (start of text)                             正文开始  
0011     3          03             ETX (end of text)                               正文结束 
0100     4          04             EOT (end of transmission)                       传输结束  
0101     5          05             ENQ (enquiry)                                   请求  
0110     6          06             ACK (acknowledge)                               收到通知  
0111     7          07             BEL (bell)                                      响铃 
1000     8          08             BS (backspace)                                  退格  
1001     9          09             HT (horizontal tab)                             水平制表符  
1010     10         0A             LF (NL line feed, new line)                     换行键  
1011     11         0B             VT (vertical tab)                               垂直制表符  
1100     12         0C             FF (NP form feed, new page)                     换页键  
1101     13         0D             CR (carriage return)                            回车键 
1110     14         0E             SO (shift out)                                  不用切换  
1111     15         0F             SI (shift in)                                   启用切换  
0000     16         10             DLE (data link escape)                          数据链路转义  
0001     17         11             DC1 (device control 1)                          设备控制1  
0010     18         12             DC2 (device control 2)                          设备控制2  
0011     19         13             DC3 (device control 3)                          设备控制3  
0100     20         14             DC4 (device control 4)                          设备控制4  
0101     21         15             NAK (negative acknowledge)                      拒绝接收  
0110     22         16             SYN (synchronous idle)                          同步空闲  
0111     23         17             ETB (end of trans. block)                       传输块结束  
1000     24         18             CAN (cancel)                                    取消 
1001     25         19             EM (end of medium)                              介质中断  
1010     26         1A             SUB (substitute)                                替补  
1011     27         1B             ESC (escape)                                    溢出  
1100     28         1C             FS (file separator)                             文件分割符  
1101     29         1D             GS (group separator)                            分组符  
1110     30         1E             RS (record separator)                           记录分离符  
1111     31         1F             US (unit separator)                             单元分隔符 
0000     32         20                                                             空格 
0001     33         21             !
0010     34         22             " 
0011     35         23             # 
0100     36         24             $ 
0101     37         25             % 
0110     38         26             &
0111     39         27             ' 
1000     40         28             ( 
1001     41         29             ) 
1010     42         2A             * 
1011     43         2B             + 
1100     44         2C             , 
1101     45         2D             - 
1110     46         2E             . 
1111     47         2F             / 
0000     48         30             0 
0001     49         31             1 
0010     50         32             2 
0011     51         33             3 
0100     52         34             4 
0101     53         35             5 
0110     54         36             6 
0111     55         37             7 
1000     56         38             8 
1001     57         39             9 
1010     58         3A             : 
1011     59         3B             ; 
1100     60         3C             <
1101     61         3D             = 
1110     62         3E             > 
1111     63         3F             ? 
0000     64         40             @
0001     65         41             A 
0010     66         42             B 
0011     67         43             C 
0100     68         44             D 
0101     69         45             E 
0110     70         46             F 
0111     71         47             G 
1000     72         48             H 
1001     73         49             I 
1010     74         4A             J 
1011     75         4B             K 
1100     76         4C             L 
1101     77         4D             M 
1110     78         4E             N 
1111     79         4F             O 
0000     80         50             P 
0001     81         51             Q 
0010     82         52             R 
0011     83         53             S 
0100     84         54             T 
0101     85         55             U 
0110     86         56             V 
0111     87         57             W 
1000     88         58             X 
1001     89         59             Y 
1010     90         5A             Z 
1011     91         5B             [ 
1100     92         5C             / 
1101     93         5D             ] 
1110     94         5E             ^ 
1111     95         5F             _ 
0000     96         60             `
0001     97         61             a 
0010     98         62             b 
0011     99         63             c 
0100     100        64             d 
0101     101        65             e 
0110     102        66             f 
0111     103        67             g 
1000     104        68             h 
1001     105        69             i 
1010     106        6A             j 
1011     107        6B             k 
1100     108        6C             l 
1101     109        6D             m 
1110     110        6E             n 
1111     111        6F             o 
0000     112        70             p 
0001     113        71             q 
0010     114        72             r 
0011     115        73             s 
0100     116        74             t 
0101     117        75             u 
0110     118        76             v 
0111     119        77             w 
1000     120        78             x 
1001     121        79             y 
1010     122        7A             z 
1011     123        7B             { 
1100     124        7C             | 
1101     125        7D             } 
1110     126        7E             ~ 
1111     127        7F             DEL (delete)                                        删除  

Extended ASCII
       80             ?
       81             �
       82             ‚
       83             ƒ
       84             „
       85             …
       86             †
       87             ‡
       88             ˆ
       89             ‰
       8A             Š 
       8B             ‹
       8C             Œ
       8D             �
       8E             Ž 
       8F             �
       90             �
       91             ' 
       92             ' 
       93             " 
       94             " 
       95             o 
       96             - 
       97             -
       98             ˜
       99             ™
       9A             š 
       9B             ›
       9C             œ
       9D             �
       9E             ž 
       9F             Ÿ
       A0               
       A1             ¡ 
       A2             ¢
       A3             £
       A4             ¤ 
       A5             ¥ 
       A6             ¦ 
       A7             §
       A8             ¨ 
       A9             ©
       AA             ª 
       AB             « 
       AC             
       AD             
       AE             ® 
       AF             ¯ 
       B0             ° 
       B1             ± 
       B2             ² 
       B3             ³ 
       B4             ´ 
       B5             µ 
       B6             
       B7             · 
       B8              
       B9             ¹ 
       BA             º 
       BB             » 
       BC             ¼ 
       BD             ½ 
       BE             ¾ 
       BF             ¿ 
       C0             À 
       C1             Á 
       C2             Â 
       C3             Ã 
       C4             Ä 
       C5             Å 
       C6             Æ 
       C7             Ç 
       C8             È 
       C9             É 
       CA             Ê 
       CB             Ë 
       CC             Ì 
       CD             Í 
       CE             Î 
       CF             Ï 
       D0             Ð 
       D1             Ñ 
       D2             Ò 
       D3             Ó 
       D4             Ô 
       D5             Õ 
       D6             Ö 
       D7             × 
       D8             Ø 
       D9             Ù 
       DA             Ú 
       DB             Û 
       DC             Ü 
       DD             Ý 
       DE             Þ 
       DF             ß
       E0             à
       E1             á
       E2             â
       E3             ã
       E4             ä
       E5             å
       E6             æ
       E7             ç
       E8             è
       E9             é
       EA             ê
       EB             ë
       EC             ì
       ED             í
       EE             î
       EF             ï
       F0             ð
       F1             ñ
       F2             ò
       F3             ó
       F4             ô
       F5             õ
       F6             ö
       F7             ÷
       F8             ø
       F9             ù
       FA             ú  
       FB             û
       FC             ü
       FD             ý
       FE             þ 
       FF             ÿ

以wiki上的图为准

本文参考:

【sqli-labs】 对于less34 less36的宽字节注入的一点深入icon-default.png?t=M5H6https://www.cnblogs.com/omnis/p/8413779.html

SQL注入防御绕过——宽字节注入icon-default.png?t=M5H6https://www.cnblogs.com/fengshui/p/9266830.html

PHP宽字节注入icon-default.png?t=M5H6https://www.jianshu.com/p/e3c5233a3c42

魔术引号、addslashes和mysql_real_escape_string的防御以及绕过icon-default.png?t=M5H6https://blog.51cto.com/wt7315/1931667

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值