sql宽字节注入
0x00:宽字节概念
GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象。
0x01:mysql字符转换过程
1. MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;
2. 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集,其确定方法如下:
• 使用每个数据字段的CHARACTER SET设定值;
• 若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
• 若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
• 若上述值不存在,则使用character_set_server设定值。
将操作结果从内部操作字符集转换为character_set_results。
重点:宽字节注入发生的位置就是PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码。
0x02:mysql中gbk宽字节构造
1.正常情况下当GPC开启或使用addslashes函数过滤GET或POST提交的参数时,黑客使用的单引号 ‘就会被转义为: \’;
2.但如果存在宽字节注入,我们输入%df%27时首先经过上面提到的单引号转义变成了%df%5c%27(%5c是反斜杠\),之后在数据库查询前由于使用了GBK多字节编码,即在汉字编码范围内两个字节会被编码为一个汉字。然后MySQL服务器会对查询语句进行GBK编码即%df%5c转换成了汉字“運”,而单引号逃逸了出来,从而造成了注入漏洞。
现在基本上都会将mysql的连接配置为“setcharacter_set_client=binary”来解决这个问题,所以这篇文章将介绍出现在php中因为字符编码转换导致的注入问题。
0x03:PHP本地代码测试
php代码如下,要求编码方式是GBK
<?php $conn = mysql_connect('localhost', 'root', 'root') or die('bad!'); mysql_query("SET NAMES 'gbk'",$conn); mysql_select_db('test', $conn) OR emMsg("数据库连接失败"); $id_tmp = isset($_GET['id']) ? urldecode($_GET['id']) : 1; $id = addslashes($_GET['id']); $sql = "SELECT * FROM news WHERE id = '{$id}'"; echo $sql; $result = mysql_query($sql, $conn) or die(mysql_error()); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>gbk</title> </head> <body> <?php $row = mysql_fetch_array($result, MYSQL_ASSOC); echo "<h3>{$row['title']}</h3><p>{$row['content']}<p>\n"; mysql_free_result($result); ?> </body> </html>
在本地数据库中建立test数据库,一个news数据表,表中的内容为:
测试id=1’时发现因为加了addslashes()函数的过滤,英文单引号被转义为\’,这样输入的值是没法完成注入的。
因为PHP编码为GBK为宽字节,可以构造宽字节注入来绕过addslashes()的过滤。
构造id=1%df’得到如下结果
基于此可以测试union查询,得到如下结果
然后构造输出数据库信息
实现原理就是之前说的输入id=%df%27遇到addslashes()函数%27的单引号被转义为\’,也就是%5c%27,然后跟前边的拼接得到
id = 1%df%5c%27
在gbk编码下%df%5c是个汉字–>運,这样就使得最后一个%27英文单引号’得到闭合,从而可以构造闭合单引号实现sql注入
当遇到可以转码的时候,比如第一种情况:
gbk–>utf-8
源代码如下: gbk_utf.php
<?php $conn = mysql_connect('localhost', 'root', 'root') or die('bad!'); mysql_query("SET NAMES binary"); mysql_select_db('test', $conn) OR die("数据库连接失败"); $title_tmp = isset($_GET['id']) ? addslashes($_GET['id']) : 1; $title = iconv("gbk","utf-8",$title_tmp); $sql = "SELECT * FROM news WHERE id = '{$title}'"; echo $sql; echo '<br/>'; $result = mysql_query($sql, $conn) or die(mysql_error()); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>gbk->utf-8</title> </head> <body> <?php $row = mysql_fetch_array($result, MYSQL_ASSOC); echo "<h3>{$row['title']}</h3><p>{$row['content']}<p>\n"; mysql_free_result($result); ?> </body> </html>
此时测试发现输入%df%27时,遇到转义变为%df%5c%27,然后%df%5c合成汉字,使得单引号%27输出
然后看一下另一种情况转码情况
utf–>gbk
utf_gbk.php源代码如下:
<?php $conn = mysql_connect('localhost', 'root', 'root') or die('bad!'); mysql_query("SET NAMES binary"); mysql_select_db('test', $conn) OR die("数据库连接失败"); $title_tmp = isset($_GET['id']) ? addslashes($_GET['id']) : 1; $title = iconv("utf-8","gbk",$title_tmp); $sql = "SELECT * FROM news WHERE id = '{$title}'"; echo $sql; echo '<br/>'; $result = mysql_query($sql, $conn) or die(mysql_error()); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>gbk->utf-8</title> </head> <body> <?php $row = mysql_fetch_array($result, MYSQL_ASSOC); echo "<h3>{$row['title']}</h3><p>{$row['content']}<p>\n"; mysql_free_result($result); ?> </body> </html>
逆向思考,测试发现输入gbk编码后的汉字时会报错
据此构造语句输出,payload如下
id=運’ union select 1,database(),3 from news%23
总结:
宽字节注入通常都是因为gbk等汉字编码造成的,后端通常会用addslashes()等函数进行转义,在构造时可以通过%df来进行测试,如果可以成功输出中文字符闭合英文单引号则通常都是存在宽字节注入的。