Our team searched for bugs in the source code of Cockpit, an open-source content management system. Here is the description of Cockpit from its official site:
Cockpit is a headless CMS with an API-first approach that puts content first. It is designed to simplify the process of publication by separating content management from content consumption on the client side.
Cockpit is focusing just on the back-end work to manage content. Rather than worry about delivery of content through pages, its goal is to provide structured content across different channels via a simple API.
While investigating the Cockpit source code, we discovered numerous vulnerabilities. Attackers could exploit them to take control of any user account and perform remote code execution.
In this article, I will talk about the technical details and demonstrate how these vulnerabilities can be exploited.
In the source code, we found two methods vulnerable to NoSQL injection, which can be used to extract application usernames. Neither of these methods requires authentication.
Let’s consider the
check method of the Auth controller responsible for authenticating app users:
authenticate function of the cockpit module:
As you can see, the code does not check the type of the user parameter, which allows embedding an object with arbitrary MongoDB operators in the query.
This is blind injection, so for successful exploitation you need to find a way to return the result of the condition.
Having analyzed the method source code, we developed a technique. In essence, we pass an array (instead of a string) in the password parameter. This results in a warning, displayed by the password_verify function, about an invalid value type:
Now I will demonstrate a few more ways to exploit blind NoSQL injection:
$eqoperator matches documents where the value of a field equals the specified value.
For example, you can use it to bruteforce names with a dictionary.
Provides regular expression capabilities for pattern matching strings in queries
You can use it to bruteforce the names of all application users.
We can speed up bruteforcing by adding the
$nin operator to the query, which will exclude any users that have already been found:
$ninselects the documents where the field value is not in the specified array
We can tweak this by adding a fixed quantifier to the regular expression for finding or limiting the length of the string:
This non-standard operator allows calling the criterion function
$b (any PHP function with a single parameter), which takes a single argument equal to field
$a (in this case, the user field):
By passing the PHP function
var_export as the argument, we will turn blind injection into classic in-band injection. With a single query, we can get the names of all app users:
requestreset method of the Auth controller responsible for creating the password reset token:
As in the previous case, there is no type check for the user parameter. Exploitation is similar, but without any difficulties such as password or CSRF token verification:
Cockpit, like many other web applications, allows resetting account passwords.
We discovered two methods that are vulnerable to NoSQL injection and allow obtaining the password reset token for any user.
resetpassword method of the Auth controller, which is responsible for changing the user password using the reset token:
There is no type checking for the token parameter, so you can extract existing tokens with the following query:
newpassword method of the Auth controller, which is responsible for displaying the user password reset form:
And, again, there is no type checking for the token parameter. The query is similar to the previous one:
Now, being able to get password reset tokens, we can compromise any user account we are interested in. This takes just a few steps:
/auth/requestreset to generate a token for resetting the password of the selected user:
2. Extract tokens by using one of the methods just described (
3. Extract user account data (username, password hash, API key, password reset token) using the
/auth/newpassword method and the password reset tokens obtained in the previous step:
With this data in hand, we can then:
Having compromised the administrator account, we can upload a web shell using Cockpit’s standard Finder component in order to achieve remote code execution:
UtilArrayQuery::buildConditionmethod of the MongoLite library
Let’s consider the method
registerCriteriaFunction of the
Database class, which creates a condition function for the specified criteria (filters) of the document:
and the associated function
buildCondition of the
Make note of the
$key variable, which contains the field name. Its content is plugged into the future string literal as-is, without being escaped.
So by controlling the content of the
$key variable, we can escape from the string literal (break it) with a single quote in order to inject arbitrary PHP code.
To demonstrate the vulnerability, we will use the
/accounts/find method (authentication required). This method supports custom criteria (filters), which means it will allow us to place arbitrary content in
In this article, I have demonstrated several ways to exploit blind NoSQL injection, a way for an unauthenticated user to take over any account, and remote code execution in the MongoLite library.
Everyone should update to the latest version (>= 0.12.0) right away.
The disclosure timeline: