代码审计之lvyecms后台getshell
2020-01-17 10:35:46 Author: xz.aliyun.com(查看原文) 阅读量:369 收藏

准备审计

码云上下载最新的lvyecms

地址:https://gitee.com/lvyecms/lvyecms?_from=gitee_searchs

审计开始

首先看网站目录结构

找到入口文件 index.php

标准的thinkphp3.2风格,具体介绍见官方文档:http://document.thinkphp.cn/manual_3_2.html

进入lvyecms/Application目录,查看存在哪些模块

一般进行代码审计时,先看前台代码,再看后台代码,但lvyecms都是需要后台权限的模块,所以不用管先后顺序,直接看Template模块

审计Template模块代码,可以发现自定义页面

//增加自定义页面 
    public function add() {
        if (IS_POST) {
            $model = D('Template/Customtemp');
            if ($model->create($_POST)) {
                $tempid = $model->add();
                if ($tempid) {
                    $this->Html->createHtml((int) $tempid);
                    $this->success("添加自定义页面成功!", U('Custompage/index'));
                } else {
                    $this->error("添加自定义页面失败!");
                }
            } else {
                $this->error($model->getError());
            }
        } else {
            $this->display();
        }
    }

先将数据写进数据库里,然后调用createHtml函数,下面几个自定义页面都是这样的逻辑。

跟进createHtml函数,在跟进createHtml函数之前,先要知道$this->Html这个方法是从哪里来的,跟进这个控制器的父类(Common\Controller\AdminBase):

发现父类中也没有定义该函数,跟进父类的父类查看

class LvyeCMS extends \Think\Controller {

    //缓存
    public static $Cache = array();
    //当前对象
    private static $_app;

    public function __get($name) {
        $parent = parent::__get($name);
        if (empty($parent)) {
            return Components::getInstance()->$name;
        }
        return $parent;
    }

    public function __construct() {
        parent::__construct();
        self::$_app = $this;
    }
    ...
}

这里没有定义,但定义了__get()魔术方法,魔术方法中先调用了父类的__get的魔术方法,get方法只是获取模板显示变量的值,继续向下审计,进入下面的代码块

public function get($name='') {
        return $this->view->get($name);      
    }

    public function __get($name) {
        return $this->get($name);
    }

跟进Components类中的getInstance方法,这个方法是实例化自身对象

static public function getInstance($_components = array()) {
        static $systemHandier;
        if (empty($systemHandier)) {
            $systemHandier = new Components($_components);
        }
        return $systemHandier;
    }

上面的类中也定义了__get魔术方法

public function __get($name) {
        if (isset(self::$_components[$name])) {
            $components = self::$_components[$name];
            if (!empty($components['class'])) {
                $class = $components['class'];
                if ($components['path'] && !class_exists($class, false)) {
                    import($components['path'], PROJECT_PATH);
                }
                unset($components['class'], $components['path']);
                $this->$name = \Think\Think::instance($class);
                return $this->$name;
            }
        }
    }

先判断$name是不是类中数组的key,是就返回一个对象,$name是Html,查看数组中对应的类在哪里,跟进:

static private $_components = array(
        'Url' => array(
            'class' => '\\Libs\\System\\Url',
            'path' => 'Libs.System.Url',
        ),
        'Cloud' => array(
            'class' => '\\Libs\\System\\Cloud',
            'path' => 'Libs.System.Cloud',
        ),
        'CloudDownload' => array(
            'class' => '\\Libs\\System\\CloudDownload',
            'path' => 'Libs.System.CloudDownload',
        ),
        'Html' => array(
            'class' => '\\Libs\\System\\Html',
            'path' => 'Libs.System.Html',
        ),
        'UploadFile' => array(
            'class' => '\\UploadFile',
        ),
        'Dir' => array(
            'class' => '\\Dir',
            'path' => 'Libs.Util.Dir',
        ),
        'Content' => array(
            'class' => '\\Libs\\System\\Content',
            'path' => 'Libs.System.Content',
        ),
        'ContentOutput' => array(
            'class' => '\\content_output',
        ),
    );

回到控制器类中,查看调用的方法

public function add() {
        if (IS_POST) {
            $model = D('Template/Customtemp');
            if ($model->create($_POST)) {
                $tempid = $model->add();
                if ($tempid) {
                    $this->Html->createHtml((int) $tempid);
                    $this->success("添加自定义页面成功!", U('Custompage/index'));
                } else {
                    $this->error("添加自定义页面失败!");
                }
            } else {
                $this->error($model->getError());
            }
        } else {
            $this->display();
        }
    }
    ...

得知调用的是createHtml这个方法
到Html.class.php文件中,查看此方法:

public function createHtml($data = '') {
        if (empty($data)) {
            if (!empty($this->data)) {
                $data = $this->data;
                // 重置数据
                $this->data = array();
            } else {
                $this->error = '没有数据';
                return false;
            }
        } else if (is_integer($data)) {
            $data = M('Customtemp')->where(array('tempid' => $data))->find();
            if (empty($data)) {
                $this->error = '没有数据';
                return false;
            }
        }
        //模板内容
        $temptext = $data['temptext'];
        if (empty($temptext)) {
            return true;
        }
        //初始化一些模板分配变量
        $this->assignInitialize();
        //生成文件名,包含后缀
        $filename = $data['tempname'];
        //生成路径
        $htmlpath = SITE_PATH . $data['temppath'] . $filename;
        // 页面缓存
        ob_start();
        ob_implicit_flush(0);
        parent::show($temptext);
        // 获取并清空缓存
        $content = ob_get_clean();
        //检查目录是否存在
        if (!is_dir(dirname($htmlpath))) {
            // 如果静态目录不存在 则创建
            mkdir(dirname($htmlpath), 0777, true);
        }
        //写入文件
        if (false === file_put_contents($htmlpath, $content)) {
            E("自定义页面生成失败:{$htmlpath}");
        }
        return true;
    }
    ....

发现从数据库中读取文件信息,直接生成,没有过滤。

漏洞验证


文章来源: http://xz.aliyun.com/t/7101
如有侵权请联系:admin#unsafe.sh