Exploiting Relational Mappings in the Content API
Relational mappings are great for storing complex data structures on databases. However, as I pointed out in my
Strapi vulnerability disclosures early this year
they pose a significant threat of being exploited to leak sensitive data.
Looking at Cockpit CMS, I discovered that it had Insecure Direct Object Reference (IDOR) vulnerability by exploiting relational mappings when populating fields. It is a pretty cool finding, so enjoy the details.
The Issue of Populating Relational Mappings
All actions for content besides files are handled by an API called
Content
(surprising name choice) and the source code is located in the
modules/Content
folder
. Of particular interest to me was how the relational mappings were populated in the
populate
function in
bootstrap.php
(snippet of code below).
if (isset($v['_id'], $v['_model'])) {
$model = $v['_model'];
$array[$k] = $this->_resolveContentRef($v['_model'], (string)$v['_id'], $process);
if ($array[$k]) {
$array[$k] = $this->populate($array[$k], $maxlevel, ($level + 1), $process);
$array[$k]['_model'] = $model;
}
}
Explaining in english the above code, if the data that is being populated has an attribute named
_id
and
_model
then Cockpit CMS would resolve the mapping and fetch the corresponding data. The massive problem here is that
there is no validation if the querying user is authorised to access that data
.
Now this would be an interesting finding if I could find a way to inject in the
_model
and
_id
attributes into this populate function. So I decided to start up a test Cockpit CMS server on my local VM and analyse how data is handled on the CMS.
To do this, I created two separate Collections and configured one of the fields to be a "Link" to an entry in the other collection.
Then I simply created a new object using the Admin UI and took a look at the request.
Hmmmmm
That is very interesting that in the user's JSON request the
_model
and
_id
values are set. This lead to my next question:
Does the server actually validate the type of link that the user sends is the same as the one configured on the model?
To test this theory, I simply created another collection named "Secrets", created a test item and just resent the above request again.
Hmmmmmmmmmmm
Now this is getting intriguing. Cockpit CMS does not validate if a relational mapping is even mapping to the configured type for the field! Plus reading the code, I knew that the user's authorisation is not validated for this mapping so I have got a vulnerability here. Saying that, at this point it was only a low severity finding since it depended on a user having write privileges to a collection with a relational field...
Or did it?
Now this is where my initial ramblings about Cockpit CMS becomes important. Cockpit CMS uses a NoSQL database management system that stores entries in a BSON structure (for simplicity just think of JSON documents). This means that if the CMS
does not even validate the type of attributes in a user's JSON request then you could modify the BSON structure that is stored on the database
.
To test this, I created a
Posts
collection that had two fields that were
not
the "Content Link" type and enabled public read and write access to the collection.
Now as an unauthenticated user I tried to create a Post, but manipulate the type of the "Title" field into a relational link by injecting the
_id
and
_model
attribute.
The following screenshot confirms my suspicion that Cockpit CMS was not validating the types of inputs within the JSON request and I was able to modify the "Title" attribute to a JSON structure.
Now let the magic begin when I whack in
?populate=1
into the URL to trigger the vulnerable populate function.
However, I am not done yet.
The only issue is that in order to exploit this IDOR vulnerability, you need the name of the model to dump and the Object ID of the data to dump. Now the model name could be guessable, but that Object ID is not and
appears
to be random.
Or is it...