Before discovering my latest Magento RCE, I’ve found two different vulnerabilities, both resulting in the complete compromise of customer data and/or the server.

As they are far less complicated, I’m presenting both of them in this single blog post for your convenience.

Vulnerable Versions

Magento EE & CE 2.x.x before 2.0.6.

Re-Installation – Technical Description

Some of Magneto’s installation process can be accessed even after the system has been installed. Specifically, the part responsible for creating the new database, adding the admin user, and storing the system’s configuration in the “env.php” file.

This weird vulnerability occurs because of how Magento enables administrators to edit components, update the system, and change the system’s configuration.

In order to provide administrators a convenient way of updating their Magento installations, Magento doesn’t delete its install directory – “/setup”. This directory contains all the PHP code base responsible for installing and updating the system.

In order to prevent guests from updating the system or changing the configuration, Magento has implemented an authentication token that’s been passed between the “admin” module and the mentioned “setup” module.

Magento then checks this token is valid – it makes sure it’s a new token created by an administrator, and that it has been created solely for the setup utility (not for validating forms against CSRF attacks etc.).

These checks seem pretty secure – Magento validates the user that created the token, the time it was created, and the purpose it was created for.

But when you are trying to perform a fresh install of the system, you don’t have users. You don’t even have a database set up yet. So how do you make sure no one is trying to access the installation process and re-install the system?

In Magento’s case, the answer is “you don’t”. Magento simply makes no check whatsoever to make sure unprivileged users is trying to access the installation process.

In fact, the installation controller and the “deny access” controller in the “setup” module are the only ones that can be accesses without a security token.

On top of that, Magento doesn’t check if the system has already been installed, which allows us to install it again, using our own database and our own admin user.

Once the system has been installed again, by us, we can access the admin control panel, and install modules, upload files, or just steal some credit cards.

In any way, admin panel access in Magento is essentially equal to a Remote Code Execution vulnerability, as you can just install or edit parts of the system’s PHP code.

Account Hijacking – Technical Description

Magento allows authenticated customers to change their information using one of the RPCs (XMLRPC, JSONAPI) featured in the system.

Magento does that by featuring the “Save” API method in the “CustomerRepository” module. Let’s have a look at the code:

/** * Save a customer based on the user input. */ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $passwordHash = null) { // Validate the customer $this-&gt;validate($customer); // Check if we're updating an existing customer $prevCustomerData = null; if ($customer-&gt;getId()) { $prevCustomerData = $this-&gt;getById($customer-&gt;getId()); } // Set the customer instance based on our input $customerModel = $this-&gt;customerFactory-&gt;create(['data' =&gt; $customer]); // Set the ID of the customer instance to our ID $customerModel-&gt;setId($customer-&gt;getId()); ... // If customer email was changed, reset RpToken info if ($prevCustomerData &amp;&amp; $prevCustomerData-&gt;getEmail() !== $customerModel-&gt;getEmail() ) { $customerModel-&gt;setRpToken(null); $customerModel-&gt;setRpTokenCreatedAt(null); } // Save the customer instance in the database $customerModel-&gt;save(); ... } // Magento\Customer\Model\ResourceModel\CustomerRepository::save()

The code is pretty straight forward – it uses our user input in order to create a new customer instance.

After validating our input, the code takes the customer ID, its name, its email address, and other parameters from it in order to replace the current customer data with our updated information.

The problem is that the code blindly trusts the user input after the validation method has been called.

The validation method validates the user input for any anomalies, such as null bytes in the email address or a really long first name. The only thing it doesn’t validate, though, is the user ID.

Because the “Customer” instance uses the user ID we supplied it, rather than the one stores in our active session, and because the ID never goes through any kind of validation, we can use any ID we’d like – our customer ID, or other customers’ IDs.

This simple bypass allows us to change the email address of any customer in the system. After that, gaining complete access to that account is as easy as sending a password reset request.

Once we used the password reset token to gain access to the account, we could see the customer’s information – his address, name, previous orders, and even his payment methods.

This vulnerability effectively compromises the entire customer database, exposing almost all customer data.