Command Injection in parse-community/parse-server

Valid

Reported on

Dec 20th 2021


Description

This is a Remote Code Execution vulnerability in the Parse Server. This vulnerability affects the Parse Server in the default configuration with MongoDB, probably a similar attack can affect the PostgreSQL storage as well. The main weakness that leads to RCE is the Prototype Pollution vulnerable code in the file DatabaseController.js. So it affects any DB backend. The current 4.10.4 version in NPM registry is vulnerable as well as the latest alpha version from the GitHub repository. I tested this vulnerability on Linux (Ubuntu) and Windows.

Proof of Concept

I attached a bash script to exploit this vulnerability by the link https://drive.google.com/file/d/1th-k9vuck02UQHqBS2T1vpLMi9H0d156/view?usp=sharing. I also recorded a video to demonstrate the vulnerability on Ubuntu https://drive.google.com/file/d/1yeLc0ssYUZbCIz7cOdd-flQ9J0U-i2vh/view?usp=sharing Below I will describe the main steps of the exploit:

HOST_URL="http://localhost:1337"
APP_ID="app7"
COLLECTION_PP="PP"
COLLECTION_RCE="RCE"
#PAYLOAD=calc    # for Windows
PAYLOAD=gnome-calculator    # for Linux
  1. Set up variables for your environment, where HOST_URL is a URL to the target Parse Server; APP_ID is an application id; COLLECTION_PP and COLLECTION_RCE are two unique MongoDB collections, they should not exist in the server; PAYLOAD is a process name which the exploit runs for demonstration.
echo Uploading Prototype Pollution payload to DB...
  1. The exploit sends a POST request to the Parse Server to store a Prototype Pollution payload in MongoDB. The payload contains an access path to a property that should be polluted in Nodejs later (constructor.prototype.evalFunctions).
echo Uploading RCE payload to DB...
  1. The exploit stores a command that will be executed during an attack. It is a running of require('child_process').exec('gnome-calculator'), but as you can see an attacker may execute any JS code that is stored here. We use a certain type of this value ("_bsontype": "Code") to evaluate this code during deserialization from DB. The evaluation feature is disabled by default and we use the Prototype Pollution vulnerability of our chain to enable this feature.
echo Populating 1K entities to DB...
  1. We need to populate data to DB to emulate one slow request to DB. We will use this data to get some kind of Race Condition and execute the vulnerability chain in the requered order: (a) serialize GET request into the Parse Server and transfer it to MongoDB; (b) serialize PUT request into the Parse Server and transfer it to MongoDB; (c) deserialize PUT(!) request in the Parse Server and trigger Prototype Pollution; (d) deserialize GET request which returns RCE payload from DB and trigger Command Injection. See details of each request below.
echo Running the exploit...
curl -X GET \
  -H "X-Parse-Application-Id: $APP_ID" \
  -G --data-urlencode 'where={"test":{"$regex": "^(A+)+$", "$options":"i"}}' \
  -o /dev/null \
  -s -w 'GET: %{time_total}s\n' \
  $HOST_URL/parse/classes/$COLLECTION_RCE &
sleep .3 && 
curl -X PUT \
  -H "X-Parse-Application-Id: $APP_ID" \
  -H "Content-Type: application/json" \
  -d '{"obj.constructor.prototype.evalFunctions":{"__op":"Increment", "amount": 1}}' \
  -w '\nPUT: %{time_total}s\n' \
  $HOST_URL/parse/classes/$COLLECTION_PP/$OBJECT_ID
  1. Running the main part of the exploit that triggers our vulnerability chain. We send the GET request, and after 300 ms (when the request is still handled on the server), we send the PUT request. Pay attention that we need to reduce 300 ms timeout to 100-200ms in some cases when the server is fast. The GET request is emulating a slow DB handling due to the usage of MongoDB case-insensitive regex matching. In the previous step, we added 1K entities that aren't matched to this request, however, MongoDB checks them all. The second PUT request is quickly handled in the server and DB and returns the PP payload to the expandResultOnKeyPath function. The function pollutes the evalFunctions property of the object by the value 1. After that, MongoDB returns data for the first GET requests, and Parse Server starts deserialization of this data in the deserializer.ts. The evaluation of deserialized data is disabled by default. However, the option flag of this feature can be polluted. And it even does not have strict checking for a type of this flag, so we can use the PP vulnerability and set up the number 1 to this property to enable this flag. What we successfully did during handling the GET request. Thus, we got arbitrary Command Injection in the Parse Server.

Impact

This is Command Injection and RCE vulnerability in the default configuration, that has a critical impact on the system. The attacker may exploit the vulnerability to get access to any user's data which the same Nodejs node and DB handle. Or the attacker may exploit the vulnerability to get access to the organization's environment.

Occurrences

Also, need to check key against "_bsontype": "Code".

We are processing your report and will contact the parse-community/parse-server team within 24 hours. 7 months ago
Mikhail Shcherbakov modified the report
7 months ago
We have contacted a member of the parse-community/parse-server team and are waiting to hear back 7 months ago
We have sent a follow up to the parse-community/parse-server team. We will try again in 7 days. 7 months ago
We have sent a second follow up to the parse-community/parse-server team. We will try again in 10 days. 7 months ago
We have sent a third and final follow up to the parse-community/parse-server team. This report is now considered stale. 7 months ago
Manuel
6 months ago

Maintainer


Thanks!

Manuel validated this vulnerability 4 months ago
Mikhail Shcherbakov has been awarded the disclosure bounty
The fix bounty is now up for grabs
Manuel confirmed that a fix has been merged on 7c8447 4 months ago
Manuel has been awarded the fix bounty
MongoTransform.js#L442 has been validated
to join this conversation