今天看到云舒在群里贴的漏洞公告,原始的文章在
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
Be careful! Starting in PHP 5.3.7 the behavior of is_a() has changed slightly: when calling is_a() with a first argument that is not an object, __autoload() is triggered!In practice, this means that calling is_a('23', 'User'); will trigger __autoload() on "23". Previously, the above statement simply returned 'false'.
More info can be found here:
https://bugs.php.net/bug.php?id=55475
Whether this change is considered a bug and whether it will be reverted or kept in future versions is yet to be determined, but nevertheless it is how it is, for now...
但是需要注意的是,PHP是否会对这个问题做出修补仍属未知!在昨天发布的PHP 5.4 beta1 中,并未见到修复了此问题。
这是一个类似于PHP的unserialize()函数可以执行__destruct()/__wakeup()中代码的问题。
漏洞的触发是控制 is_a()函数的第一个参数,该参数会被当做输入传入__autoload()函数,并自动执行__autoload()函数中的代码。能够执行什么功能,取决于__autoload()函数的功能。
验证此问题如下:
测试代码:
function __autoload($classname){
include_once $classname;
}
function test($str){
print "this is a test
";
$object = $str;
if (!is_a($object, 'SAFE')){
die("not safe!
");
}
}
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 '
';';echo $classname.'类找不到';
echo '
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。