MySQL宽字节注入

今天做了一道CTF题目,关于MySQL宽字节注入的问题。这是一个由字符编码引起的漏洞,话不多说,进入分析。

0x00 经典的SQL注入漏洞

这是没有任何SQL注入防护措施的PHP程序,它存在SQL注入漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
<?php  
$name=$_GET['name'];
$conn=mysql_connect('localhost','root','root');
if($conn==null){exit("connect error !<br>");}
mysql_select_db("aaa",$conn);
$sql="select * from a1 where name='".$name."'";
$result=mysql_query($sql,$conn);
while($val=mysql_fetch_row($result)){
print_r($val);
print("<br>");
}
?>

对该PHP程序的SQL注入POC包括:

1
2
3
http://127.0.0.1/test.php?name=a'or 'a'='a
http://127.0.0.1/test.php?name=a'or 1=1 -- %20
http://127.0.0.1/test.php?name=a'or 1=1 -- %23

其中,%20对应空格,%23对应#的URL编码,该POC在PHP5.4.45+Apache测试成功。

0x01 安全过滤

如果对上面例子1中的PHP程序中的$name变量进行安全过滤,如使用下列转义函数,则对应的POC全部失效了。

(1)addslashes
(2)mysql_real_escape_string
(3)mysql_escape_string

转义函数影响的字符包括:

  • (1) ASCII(NULL))字符\x00
  • (2) 换行字符\n,addslashes不转义
  • (3) 回车字符\r,addslashes不转义
  • (4) 反斜杠字符\
  • (5) 单引号字符‘
  • (6) 双引号字符“
  • (7) \x1a,addslashes不转义

对于例子1进行安全增强后,得到例子2,如下所示。
注意:三个转义函数的功能稍有区别,同时,转义只对字符型SQL注入防范有效,对于数值型SQL注入无效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php  
$name=$_GET['name'];
//$name=addslashes($name);
//$name=mysql_escape_string($name);
$conn=mysql_connect('localhost','root','root');
$name=mysql_real_escape_string($name);
if($conn==null){exit("connect error !<br>");}
mysql_select_db("aaa",$conn);
$sql="select * from a1 where name='".$name."'";
$result=mysql_query($sql,$conn);
while($val=mysql_fetch_row($result)){
print_r($val);
print("<br>");
}
?>

0x02 宽字节注入漏洞原理

宽字符是指两个字节宽度的编码技术,如UNICODE、GBK、BIG5等。当MYSQL数据库数据在处理和存储过程中,涉及到的字符集相关信息包括:

(1) character_set_client:客户端发送过来的SQL语句编码,也就是PHP发送的SQL查询语句编码字符集。
(2) character_set_connection:MySQL服务器接收客户端SQL查询语句后,在实施真正查询之前SQL查询语句编码字符集。
(3) character_set_database:数据库缺省编码字符集。
(4) character_set_filesystem:文件系统编码字符集。
(5) character_set_results:SQL语句执行结果编码字符集。
(6) character_set_server:服务器缺省编码字符集。
(7) character_set_system:系统缺省编码字符集。
(8) character_sets_dir:字符集存放目录,一般不要修改。

宽字节对转义字符的影响发生在character_set_client=gbk的情况,也就是说,如果客户端发送的数据字符集是gbk,则可能会吃掉转义字符\,从而导致转义消毒失败。例子3就是一个存在宽字符注入漏洞的PHP程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php  
$name=$_GET['name'];
$name=addslashes($name);
$conn=mysql_connect('localhost','root','root');
if($conn==null){exit("connect error !<br>");}
mysql_query("SET NAMES 'gbk'",$conn);
mysql_select_db("aaa",$conn);
$sql="select * from a1 where name='".$name."'";
$result=mysql_query($sql,$conn);
while($val=mysql_fetch_row($result)){
print_r($val);
print("<br>");
}
?>

这个PHP程序的SQL注入POC为:

1
http://127.0.0.1/test/t3.php?name=a%df' or 1=1; %20%23

其原理是mysql_query(“SETNAMES ‘gbk’”,$conn)语句将编码字符集修改为gbk,此时,%df\’对应的编码就是%df%5c’,即汉字“運’”,这样单引号之前的转义符号“\”就被吃调了,从而转义消毒失败

0x04 宽字节注入漏洞再深入

从宽字节注入漏洞原理可以看出,宽字节注入的关键点有两个:

(1) 设置宽字节字符集;
(2) 设置的宽字符集可能吃掉转义符号“\”(对应的编码为0x5c,即低位中包含正常的0x5c就行了)。

理论上,符合第二条的字符集都可能导致宽字节注入漏洞,这里以gbk字符集为典型,介绍宽字符注入漏洞典型案例。
宽字节注入漏洞的另一个关键是设置了character_set_client为宽字节字符集,这里有很多中设置的方式,主要包括隐式设置和显式设置。
隐含方式设置是指charcter_set_client缺省字符集就是宽字节字符集。
显式设置是指在PHP程序中调用相应的设置函数来实现字符集的设置或直接对字符串进行编码转换,设置的函数包括:

(1) mysql_query,如mysql_query(“SET NAMES ‘gbk’”, $conn)、mysql_query(“setcharacter_set_client = gbk”, $conn)。
(2) mysql_set_charset,如mysql_set_charset(“gbk”,$conn)。
(3) mb_convert_encoding,如mb_convert_encoding($sql,”utf8”,”gbk”),将SQL语句从gbk格式转换为utf8格式时,0x5c被吃掉了。
(4) iconv,如iconv(‘GBK’, ‘UTF-8’,$sql),原理同上。

原文链接

文章目录
  1. 0x00 经典的SQL注入漏洞
  2. 0x01 安全过滤
  3. 0x02 宽字节注入漏洞原理
  4. 0x04 宽字节注入漏洞再深入