The discovered vulnerability allows an unauthenticated attacker to perform an SQL Injection attack on Joomla based servers running versions 3.2 through 3.4.4. Joomla is a very popular open-source Content Management System (CMS) used by no less than 2,800,000 websites (as of September 2015).
Unrestricted administrative access to a website’s database can cause disastrous effects, ranging from complete theft, loss or corruption of all the data, through obtaining complete remote control of the web server and abusing or repurposing it (for instance, as a host for malicious or criminal content), and ending in infiltration into the internal network of the organization, also-known-as lateral movement.
3 CVEs has been assigned to the vulnerability - CVE-2015-7297, CVE-2015-7857 and CVE-2015-7858. It has been tested and found working on a number of large websites, representing different business verticals.
We encourage site administrators to update their Joomla installations immediately, deploy a 3rd-party protection product, or at the very least take their site down until a proper solution is found. According to the Verizon 2015 Database Breach Investigation Report, “99.9% of the exploited vulnerabilities were compromised more than a year after thcve CVE was published” so not patching your system will almost guarantee it will be hacked.
Screenshots: Two examples of a Table Dump command executed on a vulnerable server
In our quest to protect our customers’ web servers and services, we are constantly looking out for new attacks and attack vectors, reviewing different popular platforms. Joomla, being an extremely popular CMS, was definitely on our search.
SQL Injection, while being one of the oldest tricks in the book, is still being exploited in many platforms. When an Internet-facing web service is relying on SQL for its operation, there is always a risk that improper security (or just bad programming) will allow a remote attacker to execute arbitrary SQL commands. Those, in turn, commonly lead to Remote Code Execution on the involved server, and from there the way into a lateral-movement operation into the organization is very short.
Why Is Joomla So Significant?
Joomla is an extremely popular CMS. With over 2.8M installations worldwide, many of which are government and public sector web sites, Joomla-based systems hold not only business-specific Intellectual Property, but also 3rd-party data and Personally identifiable information (PII): email addresses, passwords, and with certain plugins - even credit card information.
- Oct 15, 2015 – Disclosure to the Joomla security team
- Oct 19, 2015 – Vulnerability is acknowledged by Joomla
- Oct 22, 2015 – Patch released by Joomla
- Oct 30, 2015 – Disclosure published by PerimeterX
Joomla is designed with a sophisticated OOP in mind - it features an extensive set of APIs, themes, templates, plugins, components, models, widgets and more. The drawback of such a design is that sometimes the code is too complicated to track, and developers make incorrect assumptions regarding variable sanitization.
With that in mind, let’s examine how Joomla operates:
Joomla is comprised of components, which contain tasks, models and views. All these elements are basically classes, inheriting from a main Joomla class related to their functionality (i.e ‘JModel’, ‘JController’, ‘JView’ and so on).
Components are basically responsible for the different functionalities of the system, such as a registration system, a content system and even a banners system. Tasks are the code representation of different actions that can be performed in a specific component, like logging in, viewing an article or submitting a comment. Views are responsible for displaying the output of a task using the system’s GUI – the chosen template, theme, and so on. The user can partially control the view class loaded in many controllers, as the view name is set using the ‘view’ HTTP parameter. Models are objects responsible for extracting task specific data out of the database. Models and views go hand in hand in Joomla, and by default the Model class is determined by the name of the view class. This means the model class name is also partially controlled by the user.
Now that we have a sense of how the system operates, let’s dig into the code. As mentioned, the elements in the system responsible for extracting view-specific data out of the database are the models. As such, they are sensitive parts in the code and user-input is rarely passed into them as-is, without any validation or processing. Instead, user-input is inserted into the model instance ‘state’ property, by calling the method ‘setState()’ only after it is validated (converted to int, quoted, etc.).
When the model instance uses user-input, it simply calls the method ‘getState()’ with the specific key it needs and it assumes the value was already validated properly.
Or was it?
Inside each model class there’s a method called ‘populateState()’, responsible for populating the ‘state’ array with validated user-input parameters. Most of the time this method is overridden by inheriting models, which retrieve and validate exactly the parameters they need for the model operation. These methods usually use hardcoded keys so an attacker cannot insert or change a sensitive ‘state’ variable.
Let’s have a look at the ‘populateState()’ method inside the ‘JModelList’, which greatly differs from standard ‘populateState()’ methods:
It is easy to note that this implementation of ‘populateState()’ allows the creation of dynamic state keys as long as they begin with either the ‘filter.’ or ‘list.’ Prefixes. The values assigned to those keys are validated only in some cases – when the key is either ‘fullordering’, ‘ordering’ or ‘direction’. The rest of the keys remain unfiltered, specifically the ‘limit’ key which is responsible for the number of results retrieved from the DB (using the ‘Limit’ SQL keyword). Since this implementation is under the ‘JModelList’ class, which is a core class and is not being used as-is, we need to find another model class inheriting from it. We have to make sure this child class either doesn’t implement a new ‘populateState()’ method, or at least calls the parent’s one as well (using ‘parent::populateState()’).
On one hand, there are plenty of those, but on the other hand, they are only located in admin controllers. Obviously, we are not administrators, so we can’t access these controllers.
Or can we?
As mentioned earlier, Joomla implemented an extremely complicated OOP code, and it only makes sense to assume developers weren’t aware of how some of it works. Part of that translates to the fact that some user-based controllers do use admin based models. Now we only need an admin model inheriting from ‘JModelList’, which uses our vulnerable ‘populateState()’ method.
One such model actually exists – the ‘ContentModelArticles’ model which is used in the ‘com_content’ user component. Unfortunately, this controller doesn’t use the ‘list.limit’ state key - the one we can completely control, so we can’t perform any SQL injections.
Or can we?
Joomla’s dynamic design and complicated OOP will again prove itself to be efficient in hiding vulnerable places in the code - with the heavy use of another critical state key in the code. This state key is ‘list.select’, which is responsible for specifying the columns to extract from the DB.
This key was originally designed to be used by component developers for specifying different ‘JOIN’ SQL statements in models SQL queries, but as we already saw, the ‘JModelList’ class allows us to control that key by using it in our regular input, as that code’s developers probably weren’t aware of the fact that this key is even being used.
As stated, once we control ‘list.select’ we could use any SQL statement we wanted in ‘ContentModelArticles’ queries, causing an unauthenticated SQL Injection. Using this SQLI we could extract all users, reset password tokens, sessions, and other configuration data stored in the DB. This will ultimately allow an attacker to obtain admin credentials, and therefore control the system’s PHP code using the ‘edit theme’ interface, effectively compromising the entire server.
This vulnerability is a classic example of how having a too-dynamic code can reflect very severely on security. I expect this disclosure will stir up a hornet’s nest regarding the system’s dynamic nature, and more vulnerabilities exploiting it will be discovered. When you are developing a complex system, keep in mind that although your design is convenient for other developers, it is convenient for vulnerability researchers, too.