今天看到云舒在群里贴的漏洞公告,原始的文章在
http://www.byte.nl/blog/2011/09/23/security-bug-in-is_a-function-in-php-5-3-7-5-3-8/
后来查了下PHP官方的手册,这个问题是在PHP 5.3.7中更新了is_a()函数的功能。is_a()经常被用于条件判断。
在此前版本的is_a() 的第一个参数如果不是object,则会返回false,现在变成了如果不是object ,则会去执行 __autoload()函数。PHP为此还开了一个bug,但对此bug仍然有争议,部分开发人员认为这个功能是正常的。
Aron Budinszky 07-Sep-2011 11:21
但是需要注意的是,PHP是否会对这个问题做出修补仍属未知!在昨天发布的PHP 5.4 beta1 中,并未见到修复了此问题。
这是一个类似于PHP的unserialize()函数可以执行__destruct()/__wakeup()中代码的问题。
漏洞的触发是控制 is_a()函数的第一个参数,该参数会被当做输入传入__autoload()函数,并自动执行__autoload()函数中的代码。能够执行什么功能,取决于__autoload()函数的功能。
验证此问题如下:
测试代码:
<?php
function __autoload($classname){
include_once $classname;
}
function test($str){
print "this is a test<br>";
$object = $str;
if (!is_a($object, 'SAFE')){
die("not safe!<br>");
}
}
test($_GET["a"]);
?>
测试结果:
但是一般来说,__autoload()函数的功能会用于加载一个文件,比如在DEDECMS中的用法:
//自动加载类库处理
function __autoload($classname)
{
global $cfg_soft_lang;
$classname = preg_replace("/[^0-9a-z_]/i", '', $classname);
if( class_exists ( $classname ) )
{
return TRUE;
}
$classfile = $classname.'.php';
$libclassfile = $classname.'.class.php';
if ( is_file ( DEDEINC.'/'.$libclassfile ) )
{
require DEDEINC.'/'.$libclassfile;
}
else if( is_file ( DEDEMODEL.'/'.$classfile ) )
{
require DEDEMODEL.'/'.$classfile;
}
else
{
if (DEBUG_LEVEL === TRUE)
{
echo '<pre>';
echo $classname.'类找不到';
echo '</pre>';
exit ();
}
else
{
header ( "location:/404.html" );
die ();
}
}
}
此处在加载文件前判断了文件名只能为/[^0-9a-z_]/i 中的字符,相对较为安全。
但在另外某知名CMS中,则没有做任何判断:
function __autoload($class) {
include_once $class.'.php';
if(!class_exists($class,false)) exit('系统加载类失败,类'.$class.'不存在!');
}
由于这个漏洞是需要is_a()函数配合 __autoload() 才能利用,且对PHP版本有要求,因此实际中能够找到利用的地方相对较少。但若是PHP官方坚持不认为这是个漏洞,在未来可能会成为一个一直可以利用的弱点。在PHP5.4中is_a()可能会支持string类型参数,同时此漏洞应该已经上报给了CVE。