OXID eShop is an e-commerce shop software originating from Germany and its enterprise edition is used by industry leaders such as Mercedes, BitBurger and Edeka. In this technical blog post we will show you how an unauthenticated attacker gains Remote Code Execution in OXID eShop running the latest version 6.3.4 on default configurations.

The vulnerability was detected with RIPS code analysis in 170.000 lines of code within 3 minutes.

The truncated analysis results are available in our RIPS demo application. Please note that we limited the results to the issues described in this post in order to ensure a fix is available.

SQL Injection in Product Details

The eShop software is prone to a SQL Injection which is fully exploitable from an unauthenticated remote session. The exploit requires no specific shop configuration.

Each time when a user is viewing a product a specific SQL query is constructed by the _getVendorSelect() method and sent to the database.

source/Application/Model/ArticleList.php

1083
1084
1085
1086
1087
1088
1089
1090
  protected function _getVendorSelect($sVendorId)
  {
    
    if ($this->_sCustomSorting) {
      $sSelect .= " ORDER BY {$this->_sCustomSorting} ";
    }
    return $sSelect;
  }

A preceding call to the setCustomSorting() method will specify the _sCustomSorting property of the object on line 1087, which determines the ORDER BY clause of the SQL query. Later, this will be the injection point of the attacker.

source/Application/Component/Locator.php

131
132
  $oIdList->setCustomSorting($oLocatorTarget->getSortingSql(
    $oLocatorTarget->getSortIdent()));

The custom sorting property is set to the return value of the method getSortingSql() on line 131 of the above code snippet. This call is delegated via the getSorting() method to the getSavedSorting() method of the FrontendController class on line 1424:

source/Application/Controller/FrontendController.php

1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
  public function getSorting($sortIdent)
  {
    
    if ($sorting = $this->getUserSelectedSorting()) {
    /*...*/
    } elseif (!$sorting = $this->getSavedSorting($sortIdent)) {
      $sorting = $this->getDefaultSorting();
    }
  /*...*/
  public function getSavedSorting($sortIdent)
  {
      $sorting = \OxidEsales\Eshop\Core\Registry::getSession()
                                                ->getVariable('aSorting');
      /*...*/
      return $sorting[$sortIdent];
  }

It can be observed that the getSavedSorting() method accesses OXID’s internal session object on line 1430 and retrieves the aSorting variable - this line is equivalent of reading PHP’s session variable $_SESSION['aSorting'] directly. This variable can be controlled by an attacker, which is a keypoint in understanding the vulnerability. Finally, the variable is written to the $sorting placeholder on line 1430, returned through the call stack and used as an argument to the previously described setCustomSorting() method.

source/Application/Component/Widget/ArticleDetails.php

899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
    protected function _setSortingParameters()
    {
        $sSortingParameters = $this->getViewParameter('sorting');
        /*...*/
        list($sSortBy, $sSortDir) = explode('|', $sSortingParameters);
        $this->setItemSorting($this->getSortIdent(), $sSortBy, $sSortDir);
    }
    /*...*/
    public function setItemSorting($sortIdent, $sortBy, $sortDir = null)
    { 
        /*...*/
        $sorting[$sortIdent]['sortby'] = $sortBy;
        $sorting[$sortIdent]['sortdir'] = $sortDir ? $sortDir : null;
        \OxidEsales\Eshop\Core\Registry::getSession()
                                       ->setVariable('aSorting', $sorting);

In the following paragraph we will see how an attacker can control this variable: Just before the SQL query is constructed and sent to the database the attacker overrides the $_SESSION['aSorting'] variable with user input. This is done by a preceding invocation of the method _setSortingParameters() which retrieves the user-controlled sorting parameter on line 901 of the source code listing. The method then calls the setItemSorting() function on line 904 to store the potentially malicious user input into the $_SESSION['aSorting'] variable, by making use of the getSession()->setVariable() construct on line 912.

SQL injected query

899
SELECT ... ORDER BY oxtitle ;INSERT INTO oxuser (...) VALUES (...);

This means an attacker can pivot via the session variable to inject straight into ORDER BY statement of the SQL query. Since the underlying database driver is per default set to PDO, an attacker can make use of stacked queries to insert a brand new admin user with a password of his choice. He can then log into the backend and continue the exploitation process which is described in the following section.

Exploiting an Admin RCE

As soon as the adversary has access to the backend, he can escalate his access into a Remote Code Execution by exploiting a PHP Object Injection vulnerability in the import section. The administrator has the possibility to import articles by uploading a CSV file which is loaded into the $data array of the following code snippet.

source/Core/GenericImport/ImportObject/OrderArticle.php

28
29
30
  protected function preAssignObject($shopObject, $data, $allowCustomShopId){
    /*...*/
    $persParamValues = @unserialize($data['OXPERSPARAM']);

On line 30, values of the column OXPERSPARAM are thrown unsanitized into the unserialize() function leading a PHP Object Injection. To learn more about PHP Object Injections and how to turn them into a Remote Code Execution you can check out our PHP Object Injection blogpost. The following video demonstrates a fully automated exploit PoC that uses the vulnerabilities described in this post.

Timetable

DateEvent
11/Dec/2017Reported a SQL Injection in OXID 4.10.6
18/June/2019First contact with vendor
19/June/2019Agreed on communication encryption
21/June/2019Sent vulnerability details
27/June/2019Vendor informs about releasing fix on 30th July
30/July/2019Vendor fixed issue

Summary

The herein described vulnerabilities affecting OXID eShop illustrate how the combination of two critical vulnerabilities can lead to an exploit that hands the total control of a shop to a remote attacker. It stresses the importance of continuously integrated security testing to minimize risk factors in sensitive source code. We would like to thank the OXID security team for the professional and timely response and we highly recommend to update all OXID eShops to the latest version.