Discuz!<5.50 preg_match()的变量$onlineipmatches未初始化漏洞

影响版本:
Discuz! 5.50以下版本

程序介绍:

Crossday Discuz! Board 论坛系统(简称 Discuz! 论坛)是一个采用 PHP 和 MySQL 等其他多种数据库构建的高效论坛解决方案。作为商业软件产品, Discuz! 在代码质量,运行效率,负载能力,安全等级,功能可操控性和权限严密性等方面都在广大用户中有良好的口碑。凭借 Discuz! 开发组长期积累的丰富的 web 开发及数据库经验,和强于创新,追求完美的设计理念,使得 Discuz! 在很短时间内以其鲜明的个性特色从国内外同类产品中脱颖而出。经过了效率最优化和负载能力最佳化设计的 Discuz! ,已获得业内越来越多专家和权威企业的认可。

漏洞分析:

 

文件include/common.inc.php里:
 
$magic_quotes_gpc = get_magic_quotes_gpc();
 
@extract(daddslashes($_COOKIE));
@extract(daddslashes($_POST));
@extract(daddslashes($_GET));
//覆盖变量,这里我们可以覆盖$_SERVER
if(!$magic_quotes_gpc) {
$_FILES = daddslashes($_FILES);
}
 
…..
 
if(getenv(‘HTTP_CLIENT_IP’) && strcasecmp(getenv(‘HTTP_CLIENT_IP’), ‘unknown’)) {
$onlineip = getenv(‘HTTP_CLIENT_IP’);
} elseif(getenv(‘HTTP_X_FORWARDED_FOR’) && strcasecmp(getenv(‘HTTP_X_FORWARDED_FOR’), ‘unknown’)) {
$onlineip = getenv(‘HTTP_X_FORWARDED_FOR’);
} elseif(getenv(‘REMOTE_ADDR’) && strcasecmp(getenv(‘REMOTE_ADDR’), ‘unknown’)) {
$onlineip = getenv(‘REMOTE_ADDR’);
} elseif(isset($_SERVER[‘REMOTE_ADDR’]) && $_SERVER[‘REMOTE_ADDR’] && strcasecmp($_SERVER[‘REMOTE_ADDR’], ‘unknown’)) {
$onlineip = $_SERVER[‘REMOTE_ADDR’];
}
//提取ip,首先尝试getenv()取,如果失败就通过$_SERVER[]来取.
preg_match("/[\d\.]{7,15}/", $onlineip, $onlineipmatches); 
//注意这个preg_match()的第3个参数$onlineipmatches并没有初始化,同时程序员没有判断preg_match函数的返回值,这样在某些特定情况下可能导致绕过正则的判断,
//可以任意构造$onlineipmatches.具体详见[PCH-002]里关于preg_match()的详细分析:http://www.80vul.com/pch/
 
$onlineip = $onlineipmatches[0] ? $onlineipmatches[0] : ‘unknown’;
unset($onlineipmatches);
 
利用iis下getenv()失效,然后通过extract()覆盖$_SERVER的变量,导致preg_match("/[\d\.]{7,15}/", $onlineip, $onlineipmatches);匹配失败,导致我们可以任意提交$onlineipmatches[].

漏洞利用:

 

//在iis环境下

index.php?_SERVER[REMOTE_ADDR][]=1&onlineipmatches[]=80vul 

解决方案:
可以看出来上面的漏洞需要几个条件:

1.需要iis环境,导致getenv()失效
2.需要覆盖$_SERVER的变量
3.preg_match()的变量没有初始化

所以我们的补丁围绕2,3来解决.Discuz!在5.50以后的版本中通过修补"2.需要覆盖$_SERVER的变量",从而不受此漏洞影响:

foreach(array(‘_COOKIE’, ‘_POST’, ‘_GET’) as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != ‘_’ && $$_key = daddslashes($_value);
}
}

//$_key{0} != ‘_’ 禁止了_开头的变量覆盖 🙂

发表评论?

0 条评论。

发表评论