CodeIgniter任意代码执行漏洞

CodeIgniter任意代码执行漏洞

漏洞描述

CI在加载模板的时候,会调用$this->load->view('template_name', $data);

内核中,查看view函数源码:

/system/core/Loader.php

public function view($view, $vars = array(), $return = FALSE)
    {
        return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
    }

...

    protected function _ci_load($_ci_data)
    {
        // Set the default data variables
        foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
        {
            $$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
        }

        $file_exists = FALSE;

        // Set the path to the requested file
        if (is_string($_ci_path) && $_ci_path !== '')
        {
            $_ci_x = explode('/', $_ci_path);
            $_ci_file = end($_ci_x);
        }
        else
        {
            $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
            $_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;

            foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
            {
                if (file_exists($_ci_view_file.$_ci_file))
                {
                    $_ci_path = $_ci_view_file.$_ci_file;
                    $file_exists = TRUE;
                    break;
                }

                if ( ! $cascade)
                {
                    break;
                }
            }
        }

        if ( ! $file_exists && ! file_exists($_ci_path))
        {
            show_error('Unable to load the requested file: '.$_ci_file);
        }

        // This allows anything loaded using $this->load (views, files, etc.)
        // to become accessible from within the Controller and Model functions.
        $_ci_CI =& get_instance();
        foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
        {
            if ( ! isset($this->$_ci_key))
            {
                $this->$_ci_key =& $_ci_CI->$_ci_key;
            }
        }

       
        if (is_array($_ci_vars))
        {
            $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
        }
        extract($this->_ci_cached_vars);

    
        ob_start();

        if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
        {
            echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
        }
        else
        {
            include($_ci_path); // include() vs include_once() allows for multiple views with the same name
        }

        log_message('info', 'File loaded: '.$_ci_path);
        if ($_ci_return === TRUE)
        {
            $buffer = ob_get_contents();
            [@ob_end_clean](/ob_end_clean)();
            return $buffer;
        }
        if (ob_get_level() > $this->_ci_ob_level + 1)
        {
            ob_end_flush();
        }
        else
        {
            $_ci_CI->output->append_output(ob_get_contents());
            [@ob_end_clean](/ob_end_clean)();
        }

        return $this;
    }

且看这一段:

if (is_array($_ci_vars))
{
        $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
}
extract($this->_ci_cached_vars);

这个extract将导致变量覆盖漏洞。

$this->_ci_cached_vars是来自$_ci_vars,而$_ci_vars是来自用户传给view方法的第二个参数。(正常情况下是开发者传给模板的变量)

而我们看到extract后面:

extract($this->_ci_cached_vars);
ob_start();

// If the PHP installation does not support short tags we'll
// do a little string replacement, changing the short tags
// to standard PHP echo statements.
if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
{
    echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
}
else
{
    include($_ci_path); // include() vs include_once() allows for multiple views with the same name
}

include($_ci_path)$_ci_path是模板地址,因为之前的变量覆盖,将会导致任意文件包含漏洞,进而getshell。

所以,只要我们可以控制view的第二个参数的『键值』,传入 _ci_path=file:///etc/passwd ,在被extract后覆盖原来的模板地址,将可以包含/etc/passwd。

这个漏洞和 相似漏洞 有点类似,就是在assign(CI里叫$this->load->vars或是$this->load->view)的时候传入数组导致的。

漏洞复现

如下Controller将可导致漏洞:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

	public function index()
	{
		$data = $this->input->post();
		$this->load->view('welcome_message', $data);
	}
}

  public function index()
{
$data = $this->input->post('info');
$this->load->vars($data);
$this->load->view('welcome_message');
}

 

当开启了远程文件包含的情况下,也可以直接包含php://input 

 

修复建议

将extract换成

foreach($this->_ci_cached_vars as $key => $value) {
        if(strpos($key, '_ci') !== 0) {
                $$key = $value;
        }
}

猜你喜欢

转载自blog.csdn.net/qq_48985780/article/details/121262371