PHP 弱类型带来的安全问题
前一段看到的一个 TIP,实践了下,有所收获。
强制转换规则
字符串转换为数值
由上图可以看出,当对两个字符串进行比较时,会『进行数字或词汇比较』。
当一个字符串被当作一个数值来取值,其结果和类型如下:
如果该字符串没有包含 ‘.’,’e’ 或 ‘E’ 并且其数字值在整型的范围之内(由 PHP_INT_MAX 所定义),该字符串将被当成 integer 来取值。其它所有情况下都被作为 float 来取值。
该字符串的开始部分决定了它的值。如果该字符串以合法的数值开始,则使用该数值。否则其值为 0(零)。合法数值由可选的正负号,后面跟着一个或多个数字(可能有小数点),再跟着可选的指数部分。指数部分由 ‘e’ 或 ‘E’ 后面跟着一个或多个数字构成。
再结合文档,可以得出:
<?php
var_dump('1024' == '1024.00'); //bool(true)
?>
实践
微博上看到安全宝发起的 yuebaomei,最终关中代码审计运用到了以上 TIP。漏洞代码如下:
<?php
$flag = "THIS IS FLAG";
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3)
break;
if ("42" == $password)
echo $flag;
else
echo 'Wrong password';
exit;
}
}
?>
上面的部分是一些对密码强度的验证,读懂代码后可以简单通过。最后的验证在于:
if ("42" == $password) echo $flag;
依照上述思路,令 $password 为 420000000.0e-7 便可通过验证,得到 flag。