产生原因:
1、mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个汉字(前一个 ascii 码大于 128 才能到汉字的范围)
2、mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符。
mysqli_real_escape_string(*connection,escapestring*)
connection | 必需。规定要使用的 MySQL 连接 |
---|---|
escapestring | 必需。要转义的字符串。编码的字符是 NUL(ASCII 0)、\n、\r、\、'、" 和 Control-Z。 |
返回已经转义的字符串。
3、addslashes() 函数返回在预定义的字符前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
NULL
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备合适的字符串。
注释:默认情况下,PHP 指令 magic_quotes_gpc 为 on,对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。
addslashes(string)
利用条件:
如果在使用PHP连接MySQL的时候,又设置了“set character_set_client = gbk" 时又会导致一个编码转换的注入问题,也就是我们所熟悉的宽字节注入。当存在宽字节注入漏洞时(存在addslashes等函数转义),注入参数里带入%df%27(解码为�’),即可把程序中过滤的 \ ( %5c)吃掉。
利用方法
PHP 自带一些转义特殊字符的函数,如addslashes()
,mysql_real_escape_string()
,mysql_escape_string()
等,这些函数可用来防止 SQL 注入。
如id=1'or'1'='1
,单引号本用来闭合语句,这些函数会自动转义这些闭合的单引号,在这些单引号前面加上转义符\
,变为1\'or\'1\'=\'1
,如此在 SQL 查询中是一个普通的字符串,不能进行注入。
而网站在过滤'
的时候,通常的思路就是将'
转换为\'
,因此我们在此想办法将'
前面添加的\
去掉,一般有两种思路:
%bb吃掉``\
如果程序的默认字符集是GBK`等宽字节字符集,就有可能产生宽字节注入,绕过上述过滤。 若在 PHP 中使用`mysql_query("set names gbk")`将默认字符集设为`GBK`,而使用`addslashes()`转义用户输入,这时如果用户输入`%bb%27`,则`addslashes()`会在`%27`前面加上一个`%5c`字符,即转义字符`\
。 而 MySQL 在使用GBK
编码时,会认为两个字符为一个汉字,%bb%5c
是一个宽字符(前一个 ASCII 码大于 128 才能到汉字的范围),也就是籠
,也就是说%bb%5c%27
=籠'
,这样单引号就未被转义能闭合语句,从而产生 SQL 注入。%bb
并不是唯一一个可以产生宽字节注入的字符,理论上%81
-%FE
均可。
过滤\'
中的\
构造%bb%5c%5c%27`,`addslashes()`会在两个`%5c`和`%27`前都加上`\
即%5c
,变为%bb %5c%5c %5c%5c %5c%27
,但宽字符集认为%bb%5c
是一个字符即籠
,则变为%bb%5c %5c%5c %5c%5c %27
即籠\\\\'
,四个\
正好转义为两个\
,即'
未被转义。这也是 bypass 的一种方法。
出现这个漏洞的原因是在PHP连接MySQL的时候执行了如下设置:
set character_set_client = gbk
告诉MySQL服务器客户端来源数据编码是GBK,然后MySQL服务器对查询语句进行GBK转码导致反斜杠\被%df吃掉。 而一般都不是直接设置character_set_client=gbk,通常的设置方法是SET NAMES 'gbk',但其实SET NAMES 'gbk'不过是比character_set_client gbk多干了两件事而已,即
mysql_query( "SET NAMES gbk");
SET NAMES 'gbk'等同于如下代码:
character_set_client 客户端使用的编码,如GBK, UTF8 比如你写的sql语句是什么编码的。 character_set_results 查询返回的结果集的编码(从数据库读取的数据是什么编码的)。 character_set_connection 连接使用的编码
这同样也是存在漏洞的。
下面对宽字节注入进行一个简单测试。 测试代码如下:
<?php $conn=mysql_connect('localhost', 'root', '123456'); mysql_select_db("test", $conn); mysql_query("SET NAMES 'gbk'", $conn); $uid=addslashes($_GET['id']); $sq1="SELECT * FROM userinfo where id=' $uid'"; $result=mysql_query($sq1, $conn); print_r('当前SQL语句: '.$sql.'<br />结果: '); print_r (mysql_fetch_row(Sresult)); mysql_close() ;
当提交/1.php?id=%df' union select 1,2,3,4%23时,成功注入的效果如图4-3所示。 在这里插入图片描述 在代码审计中,对宽字节注入的挖掘方法也比较简单,只要搜索如下几个关键字即可:
SET NAMES
character_set_client=gbk
mysql_set_charset('gbk')
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Less-32 **Bypass addslashes()**</title>
</head>
<body bgcolor="#000000">
<div style=" margin-top:70px;color:#FFF; font-size:23px; text-align:center">Welcome <font color="#FF0000"> Dhakkan </font><br>
<font size="5" color="#00FF00">
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
function check_addslashes($string)
{
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash
$string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash
return $string;
}
// take the variables
if(isset($_GET['id']))
{
$id=check_addslashes($_GET['id']);
//echo "The filtered request is :" .$id . "<br>";
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font color= "#00FF00">';
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "</font>";
}
else
{
echo '<font color= "#FFFF00">';
print_r(mysql_error());
echo "</font>";
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
</font> </div></br></br></br><center>
<img src="../images/Less-32.jpg" />
</br>
</br>
</br>
</br>
</br>
<font size='4' color= "#33FFFF">
<?php
function strToHex($string)
{
$hex='';
for ($i=0; $i < strlen($string); $i++)
{
$hex .= dechex(ord($string[$i]));
}
return $hex;
}
echo "Hint: The Query String you input is escaped as : ".$id ."<br>";
echo "The Query String you input in Hex becomes : ".strToHex($id). "<br>";
?>
</center>
</font>
</body>
</html>
看了源码:
转义了",',\
而且输入?id=1' ?id=1" ?id=1\
返回页面都是一样的
输入?id=1%df'
报错
说明存在注入。
?id=1%df%27--%20#
?id=-1%df%27union select 1,database(),3--%20#
接着就是一般union注入了