admin

Phpcms v9漏洞分析

admin 安全防护 2022-12-17 370浏览 0

最近研究源码审计相关知识,会抓起以前开源的CMS漏洞进行研究,昨天偶然看见了这个PHPCMS的漏洞,就准备分析研究一番,最开始本来想直接从源头对代码进行静态分析,但是发现本身对PHPCMS架构不是很熟悉,导致很难定位代码的位置,***就采用动态调试&静态分析的方式对漏洞的触发进行分析,下面进入正题。

1. 漏洞触发代码定位

通过漏洞的POC(/phpcms/index.php?m=member&c=index&a=register&siteid=1 )判断,漏洞触发点的入口位于/phpcms/modules/member/index.php文件中的register()方法中,在代码中插入一些echo函数,观察输出(见下)的变化。从下面的结果变化可知,img标签的src属性是在执行完下面的get()函数:

$user_model_info=$member_input->get($_POST['info'])

后发生变化,因此基本可以确定,漏洞的触发点就是位于这个函数中。

Phpcms v9漏洞分析

Phpcms v9漏洞分析

2. 定位member_input->get()跟进分析

跟进该函数,该函数位于/phpcms/modules/member/fields/member_input.class.php文件中,此处本来还想故技重施,在该方法中对代码进行插桩,但是发现插桩后的居然无法打印到页面上,没辙(原因望各位大神指点一二),只能对代码进行一行行推敲,先把代码贴上,方便分析:

functionget($data){
$this->data=$data=trim_script($data);
$model_cache=getcache('member_model','commons');
$this->db->table_name=$this->db_pre.$model_cache[$this->modelid]['tablename'];
$info=array();
$debar_filed=array('catid','title','style','thumb','status','islink','description');
if(is_array($data)){
foreach($dataas$field=>$value){
if($data['islink']==1&&!in_array($field,$debar_filed))continue;
$field=safe_replace($field);
$name=$this->fields[$field]['name'];
$minlength=$this->fields[$field]['minlength'];
$maxlength=$this->fields[$field]['maxlength'];
$pattern=$this->fields[$field]['pattern'];
$errortips=$this->fields[$field]['errortips'];
if(empty($errortips))$errortips="$name不符合要求!";
$length=empty($value)?0:strlen($value);
if($minlength&&$length<$minlength&&!$isimport)showmessage("$name不得少于$minlength个字符!");
if(!array_key_exists($field,$this->fields))showmessage('模型中不存在'.$field.'字段');
if($maxlength&&$length>$maxlength&&!$isimport){
showmessage("$name不得超过$maxlength个字符!");
}else{
str_cut($value,$maxlength);
}
if($pattern&&$length&&!preg_match($pattern,$value)&&!$isimport)showmessage($errortips);
if($this->fields[$field]['isunique']&&$this->db->get_one(array($field=>$value),$field)&&ROUTE_A!='edit')showmessage("$name的值不得重复!");
$func=$this->fields[$field]['formtype'];
if(method_exists($this,$func))$value=$this->$func($field,$value);

$info[$field]=$value;
}
}
return$info;
}

代码整体比较容易,可能比较难理解的就是$this->fields这个参数,这个参数是初始化类member_input是插入的,这个参数分析起来比较繁琐,主要是对PHPCMS架构不熟,那就在此走点捷径吧,在1中,直接将初始化完成后的member_input类dump出来,效果还不错,所有的参数都dump到页面上了,下面主要摘取比较重要的$this->fields[$field],即:【$this->fields["content"]】这个参数,如下所示⤵:

["content"]=>
array(35){
["fieldid"]=>
string(2)"90"
["modelid"]=>
string(2)"11"
["siteid"]=>
string(1)"1"
["field"]=>
string(7)"content"
["name"]=>
string(6)"内容"
["tips"]=>
string(407)"<divclass="content_attr"><label><inputname="add_introduce"type="checkbox"value="1"checked>是否截取内容</label><inputtype="text"name="introcude_length"value="200"size="3">字符至内容摘要
<label><inputtype='checkbox'name='auto_thumb'value="1"checked>是否获取内容第</label><inputtype="text"name="auto_thumb_no"value="1"size="2"class="">张图片作为标题图片
</div>"
["css"]=>
string(0)""
["minlength"]=>
string(1)"0"
["maxlength"]=>
string(6)"999999"
["pattern"]=>
string(0)""
["errortips"]=>
string(18)"内容不能为空"
["formtype"]=>
string(6)"editor"
["setting"]=>
string(199)"array(
'toolbar'=>'full',
'defaultvalue'=>'',
'enablekeylink'=>'1',
'replacenum'=>'2',
'link_mode'=>'0',
'enablesaveimage'=>'1',
'height'=>'',
'disabled_page'=>'0',
)"
["formattribute"]=>
string(0)""
["unsetgroupids"]=>
string(0)""
["unsetroleids"]=>
string(0)""
["iscore"]=>
string(1)"0"
["issystem"]=>
string(1)"0"
["isunique"]=>
string(1)"0"
["isbase"]=>
string(1)"1"
["issearch"]=>
string(1)"0"
["isadd"]=>
string(1)"1"
["isfulltext"]=>
string(1)"1"
["isposition"]=>
string(1)"0"
["listorder"]=>
string(2)"13"
["disabled"]=>
string(1)"0"
["isomnipotent"]=>
string(1)"0"
["toolbar"]=>
string(4)"full"
["defaultvalue"]=>
string(0)""
["enablekeylink"]=>
string(1)"1"
["replacenum"]=>
string(1)"2"
["link_mode"]=>
string(1)"0"
["enablesaveimage"]=>
string(1)"1"
["height"]=>
string(0)""
["disabled_page"]=>
string(1)"0"
}

有了上面的参数列表后,理解get()函数的代码就要轻松许多了,分析过程略。结论就是,漏洞的触发函数在倒数6、7两行,单独截个图,如下⤵:

Phpcms v9漏洞分析

这里比较重要的是要找出$func这个函数,查查上面的表,找到["formtype"]=>string(6) “editor”,可知$func就是editor()函数,editor函数传入的参数就是上面列出的一长串字符串,和img标签的内容,下面将跟进editor函数,真相好像马上就要大白于天下了。

3. 跟进editor函数及后续函数

editor()函数位于/phpcms/modules/member/fields/editor/imput.inc.php文件中,老规矩,先贴出代码:

functioneditor($field,$value){
$setting=string2array($this->fields[$field]['setting']);
$enablesaveimage=$setting['enablesaveimage'];
if(isset($_POST['spider_img']))$enablesaveimage=0;
if($enablesaveimage){
$site_setting=string2array($this->site_config['setting']);
$watermark_enable=intval($site_setting['watermark_enable']);
$value=$this->attachment->download('content',$value,$watermark_enable);
}
return$value;
}

简单阅读代码,发现实际的触发流程发生在$this->attachment->download()函数中,直接跟进这个函数,这个函数位于/phpcms/libs/classes/attachment.class.php中download()函数,源代码有点长,就贴一些关键代码,⤵

$string=new_stripslashes($value);
if(!preg_match_all("/(href|src)=([\"|']?)([^\"'>]+\.($ext))\\2/i",$string,$matches))return$value;
$remotefileurls=array();
foreach($matches[3]as$matche)
{
if(strpos($matche,'://')===false)continue;
dir_create($uploaddir);
$remotefileurls[$matche]=$this->fillurl($matche,$absurl,$basehref);
}
unset($matches,$string);
$remotefileurls=array_unique($remotefileurls);
$oldpath=$newpath=array();
foreach($remotefileurlsas$k=>$file){
if(strpos($file,'://')===false||strpos($file,$upload_url)!==false)continue;
$filename=fileext($file);
$file_name=basename($file);
$filename=$this->getname($filename);

$newfile=$uploaddir.$filename;
$upload_func=$this->upload_func;
if($upload_func($file,$newfile)){

代码主要是进行一些正则过滤等等操作,这里真正关键的是代码的***一行的操作$upload_func($file, $newfile),其中$this->upload_func = ‘copy’;,写的在明白点就是copy($file, $newfile),漏洞就是一个copy操作造成的。

继续浏览有关 安全 的文章
发表评论