11

Here is the code snippet for accessing to MongoDB.

client = MongoClient()
db = client.test_database
collection = db.test

# Get data from fields
condition = form.getvalue('name')
if condition:
    where = {"$where": "this.name == '"+condition+"'" }
else:
    where = ""

I was told this code is vulnerable to NoSQL injection since condition variable is not properly sanitized. But I could not figure out how the injection works with python. Can anyone give me an example like what input may cause the issues or some references to related injection attack? By the way, I also did some research by myself, but only found the injection based on Javascript and tried not working in this case. Thanks.

Yang Yu
  • 439
  • 3
  • 5
  • 12

2 Answers2

15

The $where operator in MongoDB is a feature which is best avoided. Its performance is abysmal, and not just because it doesn't benefit from indexes. Almost every common use-case can be solved much more efficiently with a common find-query or aggregation, especially one as trivial as this. But this is security stackexchange, not stackoverflow, so let's focus on the security implications.

The $where statement passes a javascript code snippet to the database which the database will then execute once for each document in the collection. Thankfully this script has no access to the db object and other dangerous shell functions and it works on copies of the documents, so the attacker can at least not change the database content like with many SQL injections. But it is for example vulnerable to attacks where the attacker wants to return other results than intended.

Let's make up an example. Let's say we have a blog. Our blog has lots of articles which can be read public, but we also have some private articles which are for our internal use and not supposed to be published. So we have a field hidden in our documents which can be true or false depending on whether or not our visitors are supposed to see the article. Our MongoDB query to get a list of all articles in a certain category for displaying it to the website visitor looks like this:

db.articles.find({"$where": "this.hidden == false && this.category == '"+category+"'" });

That should make sure nobody looks at our hidden articles. Or does it? When the user controls the category-variable, they can set it to this string:

'; return '' == '

The resulting Javascript snippet which gets send to the database is this:

this.hidden == false && this.category == ''; return '' == ''

When you have a javascript snippet with multiple commands separated by ;, they will be executed as a function and a return statement is needed to determine which value will be passed back to the caller. This function will always return true. That means the user will also see all articles in our collection, including those which are supposed to be hidden.

Philipp
  • 48,867
  • 8
  • 127
  • 157
  • If the parameter in question were part of a url, could this count as a sort of xss as well? – KDEx Mar 07 '15 at 00:26
  • @KDEx Is this a technical question or a question about terminology? – Philipp Mar 07 '15 at 00:27
  • technically, it seems that with the $where you're changing the JS that is returned and rendered to the user, just wasn't sure if I was reading your answer the right way. – KDEx Mar 07 '15 at 00:28
  • No, I am injecting JS that is sent to the database, not to the HTML output. In this scenario the injected JS is unlikely to get reflected to the user, and when it does it would likely be a vulnerability which doesn't depend on the database. – Philipp Mar 07 '15 at 00:31
  • But what if you have zero knowledge about the database, for example, if you only know it's using mongodb but not sure which collections it has in DB or even DB name. How serious this issues can be? You know in sql injection, blind sql injection can guess DB name, information for tables. Is there anything similar to NoSQL? – Yang Yu Mar 07 '15 at 00:33
  • @YangYu [Assuming that the attacker has zero knowledge is foolish!](https://en.wikipedia.org/wiki/Security_through_obscurity) Also note the example I gave requires absolutely no knowledge of collection names or schema. It's a trivial stock injection which is reasonable to expect even by an attacker without knowledge. – Philipp Mar 07 '15 at 00:41
8

Can anyone give me an example like what input may cause the issues

For your concrete piece of code this should work:

'; while(1);var foo='bar

'; is used to escape the string and the statement, then follows the actual attack while(1); (DOS attack), and then the still standing ' is transformed to valid syntax via var foo='bar.

Up to version 2.4 of MongoDB, the db object was actually global, so you could change the data in the database, and even retrieve data using blind injection.

As that's not possible anymore, the most an attacker can do is DOS and the filter evasion described by Philipp (which wouldn't be an issue for your example, but can be a problem in general).

That's still pretty bad, so you should defend against it by escaping ', ", and $.

tim
  • 29,018
  • 7
  • 95
  • 119