首页 未命名正文

Phpcms v9漏洞分析

最近研究源码审计相关知识,会抓起以前开源的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'])
  • 变化发生后,基本上可以确定漏洞的触发点位于此函数中。

    屏幕快照 2017-04-12 下午2.59.37.png

    屏幕快照 2017-04-12 下午3.02.04.png

    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"
  • ["i *** ase"]=>
  • 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行,单独截图如下⤵:

    屏幕快照 2017-04-12 下午3.28.38.png

    这里更重要的是找出来$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操作造成的。

       
    版权声明

    本文仅代表作者观点,不代表本站立场。
    本文系作者授权发表,未经许可,不得转载。