Analyzing CVE-2018-6376 – Joomla!, Second Order SQL Injection

Prefix

While there are lots of security bugs disclosed each week, for us pentesters, some are more special than others. Very recently, a Second Order SQL Injection was reported in Joomla! and a good analysis can be found here: https://blog.ripstech.com/2018/joomla-privilege-escalation-via-sql-injection/

In this blog post Savan Gadhiya and Amish Patadiya will try to help us understand and discover Second Order SQL Injection methodology and exploitation techniques. The blog also shows how SQLmap can be used to exploit a Second Order SQL Injection (i.e. do NOT reinvent the wheel).

What is Second Order SQL Injection

In a Second Order SQL injection, the application stores user provided information before executing them. A user will not get any error (or SQLi indication) in the response of that page (on which the input is being fuzzed). Later, when a user access another page or functionality, where the stored information will be used to construct a new SQL query which will execute the previously injected payload, this is what is called a Second Order SQL injection.

More information: https://portswigger.net/kb/issues/00100210_sql-injection-second-order

This sounds a little bit confusing, right? Let’s make this simple by taking an example of a second order SQL injection in the Joomla! framework [CVE-2018-6376]

Details

Affected Joomla! Version: <= 3.8.3 and >= 3.7.0

Environment for exploitation: A user with lower privilege (user role ‘Manager’) can escalate their privilege to a higher user role (user role ‘Administrator’ or ‘Super Administrator’).

Injection Detection

Now, we set up a Joomla! Framework as required to test above described scenario. Here, we have set up a vulnerable Joomla! instance (version 3.8.3) as shown in figure below:

We have created user ‘amish’ which has ‘Super Users’ privileges and another user ‘savan’ with ‘Manager’ privileges as shown below:

Our goal is to escalate privilege from ‘Manager’ role to ‘Super Administrator’ role. So, lets login with the user ‘savan’. Below figure show the dashboard for user ‘savan’ and it also shows that ‘Super User’ user is also logged in at the same time:

As explained in the referenced blog, the affected instance is in the profile update page. Below figure shows the profile update page for user ‘savan’:

Let’s intercept the profile update request in BURP Suite HTTP proxy tool. As shown in figure below, the POST request with multipart-form data goes on the following resource:

http://<IP/domain>/joomla/administrator/index.php?option=com_admin&view=profile&layout=edit&id=645

The affected parameter is ‘forms[params][admin_style]‘ and that is highlighted in figure below. Let’s insert following payload (highlighted in red font) to the affected parameter as shown in Figure below:

PAYLOAD: ‘ (single quote)

On successfully submission of this request, the profile update page shows informational message ‘Item saved’ as shown in figure below:

This does not disclose any error message as this page does not use the injected payload to structure the SQL query and execute that. Let’s access the following URL on which the SQL query gets constructed with the injected payload and is executed as shown in figure below:

http://<IP/domain>/joomla/administrator/index.php

Let’s confirm from the source code that the insertion point of payload is not vulnerable to SQL injection. Below figure shows snippet code of file ‘~/administrator/components/com_admin/controllers/profile.php’ which highlights route for ‘Edit profile’ feature:

When a user updates profile details, the application retrieves all parameters and returns JForm object as shown in figure below:

Below figure shows that the application stores retrieved user information to the database:

As we have confirmed that user inputs are not being used to construct SQL query and hence the payload insertion instance is not vulnerable, let’s try to exploit it on affected page. As shown in figure below, let’s insert following string as a payload to check how our payload is being used to constructed SQL query:

PAYLOAD: test

Now let’s, check the error message on affected dashboard page. As highlighted in figure below, we got only first character of the payload in error message.

To try further, we injected another payload ‘AND sleep(5);–‘ and refreshed the dashboard page. As shown in figure below, we got only first character ‘A’ reflected in the error message again:

If we check the database to verify that how user supplied input gets stored in the database. As shown in figure below, the database has the full payload which we injected:

As we confirmed that the payload got stored properly, let’s verify the affected code to check how it constructs the SQL query. The affected instance is from the ‘administrator/templates/hathor/postinstall/hathormessage.php’ file. As shown in figure below, code in 40th line number stores user provided value from ‘admin_style’ parameter to ‘adminstyle’ variable. Now, the code in 47th line number consumes the user supplied input directly to construct SQL query. But wait, here it treats that value as an array. So, index 0 of stored value will be consumed to construct a query. Here we got the answer that why only first character of payload was reflecting.

Now, we know that the payload is being treated as an array and index 0 will be consumed in SQL query. So, let’s try to provide an array ‘[“test1″,”test2″,”test3”]’ as a payload. Then, we were expecting the 0th index of the array i.e. (“test1″) to be consumed to structure the SQL query. But as shown in figure below, the application reflected “[“ in the error message which means that the entire payload was again treated as a string.

Oops. So, what next? Is it not an exploitable instance of SQL injection?

Nope. We came up with an idea to change the parameter name and provide the 0th index of an array ‘admin_style’. As shown in figure below, we changed the parameter name with ‘jform[params][admin_style][0]’ and injected the same payload to 0th index of ‘admin_style’:

PAYLOAD: AND sleep(5);–

Now, the application reflected our full payload. Though, the payload did not worked.

Again, We injected following payload to extract database name and the application responded with database name ‘joomla’ as shown in figure below:

Payload: extractvalue(0x0a,concat(0x0a,(select database())))

Hurray!!! We successfully exploited this. Now let’s fulfill our goal to access the application with Super Administrator privileges. The following payload will give us the session id of an Super Administrator user ‘amish’ as highlighted in figure below:

Payload: extractvalue(0x0a,concat(0x0a,(select * from joomla_session where username=’amish’)))

Yeah…. Now we can use this session id to impersonate the Super Administrator user.

Automation of Exploitation

Now, when it comes to a real penetration test we need to extract information as well. If we try to do that manually it will kill so much time. So how to automate that?

There is a solution for that. SQLMap tool provides a switch ‘–second-order’ which requires payload reflection URL as an input and it will work.

Note/Limitation: As this is the second order SQL injection, we can not automate with multiple threads to check output of each query.

If we directly provide that instance to SQLMap tool, that may not work. To work this process we need to build a query in which Sqlmap can inject its payload and can fetch the data smoothly. We constructed following payload to supply as the value of the parameter ‘jform[params][admin_style][0]’ in the request and parsed that request with the SQLMap tool switch ‘-r’ as shown in figure below;

PAYLOAD: extractvalue(0x0a,concat(0x0a,(select @@version where 1=1 *)))

Here, ‘*’ in the payload will work as a marker for SQLMap tool to inject the payloads such as:

extractvalue(0x0a,concat(0x0a,(select @@version where 1=1 AND 5231=5231 )))

))) extractvalue(0x0a,concat(0x0a,(select @@version where 1=1 AND 5231=1623 )))

))) extractvalue(0x0a,concat(0x0a,(select @@version where 1=1 OR 7231=7231 )))

))) extractvalue(0x0a,concat(0x0a,(select @@version where 1=1 order by 1 — )))

))) extractvalue(0x0a,concat(0x0a,(select @@version where 1=1 union all select NULL,NULL,NULL,’21231231232′)))

As shown in figure below, SQLMap now detects the injection and extracts all database names with following command:

sqlmap -r 1.txt –dbms MySQL –second-order “http://<IP/domain>/joomla/administrator/index.php” -D “joomla” –dbs

Using Sqlmap tool we can now extract further data easily.

Remediation

To mitigate this SQL Injection attack, upgrade Joomla! framework to version 3.8.5 (latest version available at the time of writing this blog). We observed that Joomla! patched this instance with snippet code as highlighted in figure below:

If you have any questions please post below. We shall try our best to answer them.

<marketing>

This year at Black Hat USA, we are launching a new class- Web Hacking, Black Belt edition, which is a compilation of more such pretty neat bugs. Sign up before it sells out:

https://www.blackhat.com/us-18/training/schedule/#web-hacking—black-belt-edition-9617

</marketing>