Skip to content

Latest commit

 

History

History
106 lines (72 loc) · 4.84 KB

宽字节注入及数据库编码分析.md

File metadata and controls

106 lines (72 loc) · 4.84 KB

mysql的编码

在mysql中有几种常见编码:

mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

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

mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

在默认情况下, mysql字符集为latin1, 而执行了set names utf8; 以后, character_set_client, character_set_connection, character_set_results 等与客户端相关的配置字符集都变成utf8, 但character_set_database, character_set_server 等服务端相关的字符集还是latin1, 因为这一条语句, 导致客户端和服务端的字符集出现了差别, 既然有差别, mysql在执行查询的时候, 就设计到字符集的转换。

我们用php连接mysql服务器的时候设置的编码set names gbk, 就等同于:

set character_set_client='gbk'  //客户端编码
set charseter_set_connection='gbk' //连接器编码
set charsetter_set_results='gbk'//返回值编码

连接顺序为: 客户端 <= 连接器 <= 服务端 , 这中间如果有哪一个编码不一致就很容易导致乱码的问题,

2008年鸟哥曾在博客中讲解了Mysql字符集:(http://www.laruence.com/2008/01/05/12.html)

MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection; 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集

宽字节注入

在使用php连接mysql服务器的时候, 当设置 "set character_set_client=gbk"(设置其他两个不会触发sql注入) 这时就会导致一个编码转换的注入问题, 也就是我们熟悉的宽字节注入

我们来分析下, 当我们提交id=1%df’ 的时候,假设后端用了addslashes函数处理sql语句变成了select * from articles where id=1%df\'

如果php 连接mysql的时候设置了client客户端的字符集为gbk, 那么mysql服务器对查询语句就会进行GBK转码, mysql服务器数据库一般都是latin1编码,而gbk是两个字符表示一个汉字,因此,`%df 和转义字符%5c就会组成成一个%df%5c的汉字, 吃掉了一个转义符,导致单引号逃逸

解决方法:

  1. 一般官方推荐把charackter_set_client 设置为binary 或utf-8
  2. 使用mysql_real_escape_string()函数, 该函数会自动识别编码问题

另外在高版本的mysql中好像修复了该问题

范围: 据gbk编码,第一个字节ascii码大于128, 即0x81~0xff

mysql的其他编码问题

mysql的编码问题还会导致如下问题

<?php
if ($username === 'admin') {
    if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1') {
        die('Permission denied!');
    }
}
$result = $mysqli->query("SELECT * FROM z_users where username = '{$username}' and password = '{$password}'");

核心代码如上

那么,为什么执行SELECT * FROM user WHERE username='admin\xC2' and password='admin'却可以查出用户名是admin的记录?

当设置客户端的字符集位utf8的时候,这时mysql的编码转换为:

utf8-utf8-latin1

最后执行将数据库里的admin 和传入的admin\xC2 进行比较的时候, admin是一个latin1字符串

大佬猜测原因是Mysql在转换字符集的时候, 将不完整的字符给忽略了

参考: https://www.leavesongs.com/PENETRATION/mysql-charset-trick.html