We tracked the following data from "id" Parameter:
GET /no-sqli?id=%7B%22%24ne%22%3A%22%22%7D
id=%7B%22%24ne%22%3A%22%22%7D
...which was accessed within the following code:
Iterable#initialize(), line 76
...and ended up in this database query:
What's the risk?
NoSQL injection is possible when developers hand-build NoSQL statements containing user-supplied data without validation or encoding. The goal of such attacks is to force the database to retrieve and output data to which the user would not otherwise have access. For example, an attacker could use NoSQL Injection on a vulnerable application in order to query the database for customer credit card numbers and other data, even if it wasn't part of the query the developer created. NoSQL injection also allows privilege escalation and account hijacking.
Recommendation
The most effective method of stopping NoSQL injection attacks is to only use a library like
<a href="http://hibernate.org/ogm/">Hibernate OGM</a> that safely handles database interaction.
These APIs utilize bind variables. Both techniques completely stop the injection of code if used properly.
You must still avoid concatenating user supplied input to queries and instead use the binding pattern to keep user input from being misinterpreted as NoSQL commands.
Here's an example of an UNSAFE query:
String user = request.getParameter("user");
String pass = request.getParameter("pass");
String unsafeFunction = "function() {var result = db.myCollection.findOne( { user : " + user + ", password: " + pass + " } ); return doc;}"; //UNSAFE
DB db = mongo.getDB(MONGO_DB_NAME);
Object evalResult = db.doEval(unsafeFunction);
Here's an example of the same query, made SAFE:
String user = request.getParameter("user");
String pass = request.getParameter("pass");
String saferFunction = "function(u,p) {var result = db.myCollection.findOne( { user : u, password: p} ); return doc;}"; //SAFE
DB db = mongo.getDB(MONGO_DB_NAME);
Object evalResult = db.doEval(saferFunction, user, pass);
And even SAFER:
String user = request.getParameter("user");
String pass = request.getParameter("pass");
MongoDatabase mongoDatabase = mongo.getDatabase(MONGO_DB_NAME);
MongoCollection&lt;Document&gt; myCollection = mongoDatabase.getCollection("myCollection");
BasicDBObject findParams = new BasicDBObject();
findParams.put("user", user);
findParams.put("password", pass);
FindIterable&lt;Document&gt; it = myCollection.find(findParams);
Consumer&lt;Document&gt; consumer = new Consumer&lt;Document&gt;() { public void accept(Document queryResult) { ... } };
it.forEach(consumer);
There are some scenarios, like dynamic search, that make it difficult to use parameterized queries because the order and quantity of variables is not predetermined. If you are unable to avoid building such a NoSQL call on the fly, then validation and escaping all user data is necessary. Deciding which characters to escape depends on the database in use and the context into which the untrusted data is being placed.
This is difficult to do by hand, but luckily the ESAPI library offers such functionality. Here's an example of safely encoding a dynamically built JavaScript function for a MongoDB query using untrusted data:
String user = ESAPI.encoder.encodeForJavaScript(request.getParameter("user"));
String pass = ESAPI.encoder.encodeForJavaScript(request.getParameter("pass"));
String unsafeFunction = "function() {var result = db.myCollection.findOne( { user : " + user + ", password: " + pass + " } ); return doc;}";
DB db = mongo.getDB(MONGO_DB_NAME);
Object evalResult = db.doEval(unsafeFunction);
First Event
Stack:
Request.GET(request.rb:341)
Request.block in GET(request.rb:341)
Request.fetch(request.rb:59)
Request.fetch_header(request.rb:59)
Request.GET(request.rb:340)
Vulnerability ID: TP4L-BZN2-31PC-LE36
Application Name: AgentMessageGeneratorRuby
Vulnerability Link: http://localhost:19080/Contrast/static/ng/index.html#/7c6cfec5-a187-4d5e-984a-d11d96d2ef63/applications/7b7c6b72-2652-4e2d-9b52-ada4357bd80a/vulns/TP4L-BZN2-31PC-LE36
What Happened?
We tracked the following data from "id" Parameter:
GET /no-sqli?id=%7B%22%24ne%22%3A%22%22%7D
id=%7B%22%24ne%22%3A%22%22%7D
...which was accessed within the following code:
Iterable#initialize(), line 76
...and ended up in this database query:
What's the risk?
NoSQL injection is possible when developers hand-build NoSQL statements containing user-supplied data without validation or encoding. The goal of such attacks is to force the database to retrieve and output data to which the user would not otherwise have access. For example, an attacker could use NoSQL Injection on a vulnerable application in order to query the database for customer credit card numbers and other data, even if it wasn't part of the query the developer created. NoSQL injection also allows privilege escalation and account hijacking.
Recommendation
The most effective method of stopping NoSQL injection attacks is to only use a library like <a href="http://hibernate.org/ogm/">Hibernate OGM</a> that safely handles database interaction.
If you must execute queries manually, use
For MongoDB: (normal queries) <a href="http://api.mongodb.org/java/current/com/mongodb/client/MongoDatabase.html">com.mongodb.client.MongoDatabase</a> <a href="http://api.mongodb.org/java/current/com/mongodb/client/MongoCollection.html">com.mongodb.client.MongoCollection</a>
(asynchronous queries) <a href="http://api.mongodb.org/java/current/com/mongodb/async/client/MongoDatabase.html">com.mongodb.async.client.MongoDatabase</a> <a href="http://api.mongodb.org/java/current/com/mongodb/async/client/MongoCollection.html">com.mongodb.async.client.MongoCollection</a>
These APIs utilize bind variables. Both techniques completely stop the injection of code if used properly. You must still avoid concatenating user supplied input to queries and instead use the binding pattern to keep user input from being misinterpreted as NoSQL commands.
Here's an example of an UNSAFE query:
Here's an example of the same query, made SAFE:
And even SAFER:
There are some scenarios, like dynamic search, that make it difficult to use parameterized queries because the order and quantity of variables is not predetermined. If you are unable to avoid building such a NoSQL call on the fly, then validation and escaping all user data is necessary. Deciding which characters to escape depends on the database in use and the context into which the untrusted data is being placed.
This is difficult to do by hand, but luckily the ESAPI library offers such functionality. Here's an example of safely encoding a dynamically built JavaScript function for a MongoDB query using untrusted data:
First Event
Last Event
HTTP Request
GET http://localhost:3000/no-sqli?id=%7B%22%24ne%22%3A%22%22%7D HTTP/1.1 ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3 ACCEPT-ENCODING: gzip, deflate, br ACCEPT-LANGUAGE: en-US,en;q=0.9,es-419;q=0.8,es;q=0.7,fr;q=0.6 CACHE-CONTROL: max-age=0 CONNECTION: keep-alive COOKIE: _godzillaSession=c79624cdf624d680c9297fa8b9395560; Rubymine-227b963d=c6db1531-5ef3-47e3-a3ea-a7b1e168e146; __profilin=p%3Dt; _railsgoat_session=RTA1NjNLOWhQNkVFelZMcUdZREdGZEhDQ0JwUHFCTUxrOURvcmZzZVEvd3ptQUt6Z0ZxZjlqdGNqR1NnSy9DelFZcXJESGp5dnpUcnBtVWwwMkV2aGFYa3VIazQvY3B2MWxVeW9RMkRlc1ZEcUVFeHV2L0VkNUc2dVljMTlWMmtLQkVZVVk2cEJ3RmRlZEhPaVl6T3gxT0VwSTRhRm85bWt1d0p0SE9ZVVhUTnRpZ3lvRHNmVzdtb3k5TFRDLzhxRTVlejQvS3BBVGMyR0VIVlpVYnhCUThYNXFQTHl0TWt2dHVFUzNLYThENUNXbGd5SVhUR29FSjFKZVFBZ1hMQi0tSkJHeGRZL3J6c2ZJZmpmaC9XNGhZZz09--f26a37df09baa30c849c775eb85027206f0ec16d; _app_with_scaffold_session=TkJBelhMeFFtelNkRG1PU1Rsbk1rK1NiVEVVb0I0MDg5d0JWaWI2Szl4UDRwNkpXaERnWjlMTHVZN1pLcEVIdDg1cEllaUFCL1BwTkhycjlQbE9ZV0E9PS0tTmJ2NkdJdUdQcVdJWWtRN3hvaFRNdz09--db39525eb51e9387e6eb808daa52880c0312d71f; _mongo-rails-5_0_0_session=N3BOL1NTZkh4TEZCeDZKOXByQjZMd3EzOW9WekcrZjZVQk5yTzBDZStlMnNSazhvSVR4cVgyZWFIbml0bUMxdjNGTTlCRXlXSVFSaGFERXZHcmJlaDV1Snk1VU5Zd3ZPaWlxS0NVVXBWLzFaTHRUNFQzQUlMcXdWNkFYMnVxWnAwOFN3WHZ6T3l... HOST: localhost:3000 IF-NONE-MATCH: W/"ddac82a759aa6dc059a2f0d866a75c49" UPGRADE-INSECURE-REQUESTS: 1 USER-AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36 VERSION: HTTP/1.1
References
https://www.owasp.org/index.php/Top_10_2013-A1-Injection