易优CMS RCE

易优CMS RCE

漏洞分析

Eyou是由TP衍生出来的一款cms,一般审这种我喜欢先看看TP版本

/core/base.php

<?php

define('THINK_VERSION', '5.0.24');
define('THINK_START_TIME', microtime(true));
define('THINK_START_MEM', memory_get_usage());
define('EXT', '.php');

5.0.24最恶心的版本,先试着找一些可getshell的点,比如文件写入或上传之类的

/application/user/controller/Uploadify.php

 public function delupload()
    {
        if (IS_POST) {
            $action = input('post.action/s','del');                
            $filename= input('post.filename/s');
            $filename= empty($filename) ? input('url') : $filename;
           
            $filename= str_replace('../','',$filename);
            $filename= trim($filename,'.');
            $filename= trim($filename,'/');
            if(eyPreventShell($filename) && $action=='del' && !empty($filename) && file_exists($filename)){
              var_dump($filename);
                $fileArr = explode('/', $filename);
                if ($fileArr[2] != $this->users_id) {
                    return false;
                }
                $filetype = preg_replace('/^(.*)\.(\w+)$/i', '$2', $filename);
                $phpfile = strtolower(strstr($filename,'.php'));  //排除PHP文件
                $size = getimagesize($filename);
                $fileInfo = explode('/',$size['mime']);
                if($fileInfo[0] != 'image' || $phpfile || !in_array($filetype, explode(',', config('global.image_ext')))){
                    exit;
                }
                if(@unlink($filename)){
                    echo 1;
                }else{
                    echo 0;
                }  
                exit;
            }
        }
    }

在找的过程中发现了这么一处有意思的点unlink以及file_exists操作了filename,如filename可控的情况下,我们就可以通过phar来导致rce

            $filename= input('post.filename/s');
            $filename= empty($filename) ? input('url') : $filename;
           
            $filename= str_replace('../','',$filename);
            $filename= trim($filename,'.');
            $filename= trim($filename,'/');

但是这里有一些过滤,简单看看,首先str_replace不用看。这个过滤很简单,直接/....//这样就可以过了,trim用了两次,主要针对.和/。但是这个函数主要是针对两侧,所以也根本影响我们的利用。但是测试的时候发现

image-20200605135214931

直接把phar:给尼玛替换了,我想了一下问题也只能出在input获取层了,因为是tp的框架加上一般都不会去改它的获取函数就忽略了

    'extra_file_list'        => array(APP_PATH . 'helper' . EXT, THINK_PATH . 'helper' . EXT, APP_PATH . 'function' . EXT),
    // 默认输出类型
    'default_return_type'    => 'html',
    // 默认AJAX 数据返回格式,可选json xml ...
    'default_ajax_return'    => 'json',
    // 默认JSONP格式返回的处理方法
    'default_jsonp_handler'  => 'jsonpReturn',
    // 默认JSONP处理方法
    'var_jsonp_handler'      => 'callback',
    // 默认时区
    'default_timezone'       => 'PRC',
    // 是否开启多语言
    'lang_switch_on'         => $lang_switch_on,
    // 默认全局过滤方法 用逗号分隔多个
    'default_filter'         => 'strip_sql,htmlspecialchars', // htmlspecialchars

在获取的时候通过了strip_sql,htmlspecialchars两个函数

function strip_sql($string)
    {
        $pattern_arr = array(
            "/\bunion\b/i",
            "/\bselect\b/i",
            "/\bupdate\b/i",
            "/\bdelete\b/i",
            "/\boutfile\b/i",
            // "/\bor\b/i",
            "/\bchar\b/i",
            "/\bconcat\b/i",
            "/\btruncate\b/i",
            "/\bdrop\b/i",
            "/\binsert\b/i",
            "/\brevoke\b/i",
            "/\bgrant\b/i",
            "/\breplace\b/i",
            // "/\balert\b/i",
            "/\brename\b/i",
            // "/\bmaster\b/i",
            "/\bdeclare\b/i",
            // "/\bsource\b/i",
            // "/\bload\b/i",
            // "/\bcall\b/i",
            "/\bexec\b/i",
            "/\bdelimiter\b/i",
            "/\bphar\b\:/i",
            "/\bphar\b/i",
            "/\@(\s*)\beval\b/i",
            "/\beval\b/i",
        );
        $replace_arr = array(
            'union',
            'select',
            'update',
            'delete',
            'outfile',
            // 'or',
            'char',
            'concat',
            'truncate',
            'drop',
            'insert',
            'revoke',
            'grant',
            'replace',
            // 'alert',
            'rename',
            // 'master',
            'declare',
            // 'source',
            // 'load',
            // 'call',
            'exec',
            'delimiter',
            'phar',
            'phar',
            '@eval',
            'eval',
        );

        return is_array($string) ? array_map('strip_sql', $string) : preg_replace($pattern_arr, $replace_arr, $string);
    }

好了!破案了,这种替换我是没招的但是他写的很死!比如遇到phar:这样的时候才去替换,不会说因为出现了phar就去进行替换操作!那我们文章的str_replace这不就用上了?

payload:p../h../a../r://./p../har.p../har

可以看见完美了但是在进入到file_exists之前还有一个eyPreventShell函数

 function eyPreventShell($data = '')
    {
        $data = true;
        if (is_string($data) && (preg_match('/^phar:\/\//i', $data) || stristr($data, 'phar://'))) {
            $data = false;
        } else if (is_numeric($data)) {
            $data = intval($data);
        }

        return $data;
    }

他自己给传入的data复了一次值(不知道程序员怎么想)导致可以直接忽视这个函数

用一个echo来证明一下

完美

2 个赞

Joseph大佬,新人对于代码审计应该怎么进一步的学习呢,那本法师的书看完了,想进一步学习,有什么推荐的媒介或者方法吗

talk is cheap 干就完事了

求附赠源码一份。

这个应该官网能下的吧

刚开始至少掌握最基本的语言功底,多练,多看。

链接: https://pan.baidu.com/s/1IZT6i6Bzh68oKY-r-zF1HA 提取码: p3m3
版本是1.4.6 我原来也想发这个漏洞,但是内部说内容太少,让我增加内容不知道啥时候才发的出来。

1 个赞

还是想成为像大佬一样的开发牛,但是这辈子应该是学不会了。

1 个赞

嗯,thinkphp直接就存在利用的pop链,找个能触发反序列化的地方就行了。

1 个赞

俺写了个POC,apache环境测试成功写getshell。但是在nginx环境下测试失败,发现根本没有进到Windows.php的__destruct(),不明白是什么原因,请大佬们指教orz

这个系统用户量比较少,爆过非常多的洞,去年11月份就审计过这个cms,当时前台有几处可以直接 post unserialize,eyPreventShell 这个函数应该是新版本出问题了,老版本当时看了半天绕不过去就放弃了,前面绕过过滤替换有点意思

学习了,伪协议

是不是所有文件操作的函数都可以呀

铁子 你图裂了

你好,链接失效了,能再发一个吗,qq1156865535