[SERVER-57248] Query results are replaced with values from project Created: 15/Apr/21  Updated: 27/Oct/23  Resolved: 27/May/21

Status: Closed
Project: Core Server
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Critical - P2
Reporter: Jonah Werre Assignee: David Storch
Resolution: Works as Designed Votes: 0
Labels: Bug, Security
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

MongoDb 4.4


Operating System: ALL
Sprint: Query Execution 2021-05-31
Participants:

 Description   

Problem Description

When using the select statement with an object and String values the result is replaced with the values from the select statement.  I tried it out in 4.0 and 4.2 and this is not an issue. This is only an issue in version 4.4.

Steps to Reproduce

 

(function(){
  const products = [    'apples',    'peaches',    'bananas',    'oranges',    'grapes',    'watermelons',  ];
  for (let product of products) {     let item = {      id: new Date().getTime(),      name: product,       qty: Math.round( Math.random() * (50 - 1) + 1 )    }    db.products.save(item);  }
 
  let results = db.products.find({}, {name:1, qty:'You have none!!', attack:'<scripts>alert("boo!")</scripts>'});
  printjson(results.toArray());
  db.products.drop();
})();

 

Expected Results

The expected results would be the actual values from the database not fake results from the select statement.

 

 

[
 {
 "_id": ObjectId("6078ad7cc3006933c653ede5"),
 "name": "apples",
 "qty": 44
 },
 {
 "_id": ObjectId("6078ad7cc3006933c653ede6"),
 "name": "peaches",
 "qty": 47
 },
 {
 "_id": ObjectId("6078ad7cc3006933c653ede7"),
 "name": "bananas",
 "qty": 14
 },
 {
 "_id": ObjectId("6078ad7cc3006933c653ede8"),
 "name": "oranges",
 "qty": 14
 },
 {
 "_id": ObjectId("6078ad7cc3006933c653ede9"),
 "name": "grapes",
 "qty": 16
 },
 {
 "_id": ObjectId("6078ad7cc3006933c653edea"),
 "name": "watermelons",
 "qty": 45
 }
]
 
 

Actual Results

 I would expect quantity to be a number and attack to be nonexistent

 

[
 {
 "_id": ObjectId("6078ab09c3006933c653edcd"),
 "name": "apples",
 "qty": "yep",
 "attack": "<scripts>alert(\"boo!\")</scripts>"
 },
 {
 "_id": ObjectId("6078ab09c3006933c653edce"),
 "name": "peaches",
 "qty": "yep",
 "attack": "<scripts>alert(\"boo!\")</scripts>"
 },
...
]

Additional Notes

I set up a Gist here using the Mongoose driver.

https://gist.github.com/jwerre/ef447dc1d60a48865c8574dff73d7a69



 Comments   
Comment by David Storch [ 27/May/21 ]

Hi jonah@surveyplanet.com and others,

Thanks for reporting this issue! After internal review, we have determined that this was an intentional change released as part of MongoDB 4.4 and that the system is working as designed. Although there is certainly a related security consideration for application authors around escaping unsanitized user input, we are not treating this as a security vulnerability. I will further explain and justify this decision below.

For context, the change was made as part of an effort in 4.4 to unify the behavior (as well as the implementation) of projections in aggregate and projections in find. The behavior reported in this ticket for the find command has always existed for the aggregate command. For example, here is a snippet of a shell session against a version 4.2 server:

> db.version()
4.2.9
> db.c.drop()
false
> db.c.insert({name: "applies", qty: 10})
WriteResult({ "nInserted" : 1 })
> db.c.aggregate([{$project: {_id: 0, name: 1, attack: '<scripts>alert("boo!")</scripts>'}}])
{ "name" : "applies", "attack" : "<scripts>alert(\"boo!\")</scripts>" }

As you can see, the observed behavior is not new, it is simply new when the query is expressed as a find command. The unification effort done for MongoDB 4.4 largely adopted the semantics of $project in aggregate, which is why find now behaves like aggregate and not vice versa. The underlying reason for this behavior is that projection values other than 0, 1, true, and false in $project are interpreted as MQL expressions, and a constant/literal string is a valid expression (also see the reference page for $literal).

Another aspect of the discussion here revolves around what the application author is attempting to achieve if they have something like find({}, {fieldName: ?}), where the question mark is filled in based on arbitrary user input. If the application is allowing the user to determine whether to show or suppress some information, wouldn't the user interface be something more like checkboxes or radio buttons as opposed to free-form text? Or, more abstractly, the question mark appears in a position in the MQL grammar where there can be an arbitrary expression. This value is not always interpreted as a constant by the parser. Therefore, if there is indeed some use case like this, then the application must escape/validate the user input in order protect against an injection attack.

Finally, it is important to note that MQL is not immune to injection attacks if the application is written improperly. This is true for projection, as discussed above, but it also holds in other contexts. A canonical example is something like this:

db.restaurants.find({cuisineType: ?});

Here, the application allows the user to enter the cuisine they are interested in. If the application does not validate the input correctly, then the user could type in something like "{$exists: true}", obtaining a dump of the whole database. The "restaurants" collection may not contain sensitive information, but you can extrapolate to see how this type of injection could pose a security risk.

To summarize:

  • The reason behind this change is that find and aggregate projection now fully share their syntax and semantics.
  • Since projections can accept arbitrary expressions, applications must validate user input before passing it through verbatim to an MQL projection.
  • MQL injection, analogous to SQL injection, is possible. Application authors should know how to protect against it.

I'm closing this ticket as "Works as Designed", but let me know if you have any further comments or questions.

Comment by Jonah Werre [ 19/Apr/21 ]

No worries... Good luck!

Comment by Eric Sedor [ 19/Apr/21 ]

Thank you jonah@surveyplanet.com, I'm transferring this ticket to our SECURITY project for investigation.

Comment by Jonah Werre [ 19/Apr/21 ]

Any feedback on this? This is a pretty severe security issue, I hope you can elevate it appropriately. 

Generated at Thu Feb 08 05:41:21 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.