Code-Racing / brickyard

0 stars 0 forks source link

CONTRAST: NoSQL Injection from Untrusted Sources on "/mongoose/findOne" page #27

Open valvolineford opened 4 years ago

valvolineford commented 4 years ago

Vulnerability ID: PV9F-4CH3-LX98-CVN4

Application Name: AgentMessageGeneratorNode

Vulnerability Link: http://localhost:19080/Contrast/static/ng/index.html#/7c6cfec5-a187-4d5e-984a-d11d96d2ef63/applications/d944b35a-2925-43da-a27b-0fa1fac7d8aa/vulns/PV9F-4CH3-LX98-CVN4

What Happened?

We tracked the following data from Untrusted Sources:

POST /mongoose/findOne

{"input":"findOne"}=

...which was accessed within the following code:

NodeCollection.NodeCollection.findOne(), line 44

...and ended up in this database query:

Object

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/&quot;&gt;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&quot;&gt;com.mongodb.client.MongoDatabase&lt;/a&gt; <a href="http://api.mongodb.org/java/current/com/mongodb/client/MongoCollection.html&quot;&gt;com.mongodb.client.MongoCollection&lt;/a&gt;

(asynchronous queries) <a href="http://api.mongodb.org/java/current/com/mongodb/async/client/MongoDatabase.html&quot;&gt;com.mongodb.async.client.MongoDatabase&lt;/a&gt; <a href="http://api.mongodb.org/java/current/com/mongodb/async/client/MongoCollection.html&quot;&gt;com.mongodb.async.client.MongoCollection&lt;/a&gt;

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(&quot;user&quot;);
String pass = request.getParameter(&quot;pass&quot;);
String unsafeFunction =  &quot;function() {var result = db.myCollection.findOne( { user : &quot; + user + &quot;, password: &quot; + pass + &quot; } ); return doc;}&quot;; //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(&quot;user&quot;);
String pass = request.getParameter(&quot;pass&quot;);
String saferFunction =  &quot;function(u,p) {var result = db.myCollection.findOne( { user : u, password: p} ); return doc;}&quot;; //SAFE

DB db = mongo.getDB(MONGO_DB_NAME);
Object evalResult = db.doEval(saferFunction, user, pass);

And even SAFER:

String user = request.getParameter(&quot;user&quot;);
String pass = request.getParameter(&quot;pass&quot;);

MongoDatabase mongoDatabase = mongo.getDatabase(MONGO_DB_NAME);
MongoCollection&amp;lt;Document&amp;gt; myCollection = mongoDatabase.getCollection(&quot;myCollection&quot;);

BasicDBObject findParams = new BasicDBObject();
findParams.put(&quot;user&quot;, user);
findParams.put(&quot;password&quot;, pass);

FindIterable&amp;lt;Document&amp;gt; it = myCollection.find(findParams);
Consumer&amp;lt;Document&amp;gt; consumer = new Consumer&amp;lt;Document&amp;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(&quot;user&quot;));
String pass = ESAPI.encoder.encodeForJavaScript(request.getParameter(&quot;pass&quot;));
String unsafeFunction =  &quot;function() {var result = db.myCollection.findOne( { user : &quot; + user + &quot;, password: &quot; + pass + &quot; } ); return doc;}&quot;;

DB db = mongo.getDB(MONGO_DB_NAME);
Object evalResult = db.doEval(unsafeFunction);

First Event


Stack:
  global.exec(/app/vulnerabilities/mongoose/index.js:54)
  global.api.post.post.post.post(/app/vulnerabilities/mongoose/index.js:26)
  Layer.handle(/app/node_modules/express/lib/router/layer.js:96)
  next(/app/node_modules/express/lib/router/route.js:138)
  Route.dispatch(/app/node_modules/express/lib/router/route.js:113)
  Layer.handle(/app/node_modules/express/lib/router/layer.js:96)
  (/app/node_modules/express/lib/router/index.js:279)
  Function.process_params(/app/node_modules/express/lib/router/index.js:332)
  next(/app/node_modules/express/lib/router/index.js:273)
  Function.handle(/app/node_modules/express/lib/router/index.js:176)
  router(/app/node_modules/express/lib/router/index.js:48)
  Layer.handle(/app/node_modules/express/lib/router/layer.js:96)
  trim_prefix(/app/node_modules/express/lib/router/index.js:314)
  (/app/node_modules/express/lib/router/index.js:282)
  Function.process_params(/app/node_modules/express/lib/router/index.js:332)

Last Event


Stack:
  NativeCollection.NativeCollection.(anonymous function)(/app/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js:147)
  NodeCollection.NodeCollection.findOne(/app/node_modules/mquery/lib/collection/node.js:44)
  model.Query.Query.findOne(/app/node_modules/mquery/lib/mquery.js:2036)
  model.Query.(/app/node_modules/mongoose/lib/query.js:1978)
  model.Query._wrappedThunk(/app/node_modules/mongoose/lib/helpers/query/wrapThunk.js:17)
  process.nextTick(/app/node_modules/kareem/index.js:352)
  process._tickCallback(internal/process/next_tick.js:61)

HTTP Request

POST http://20.42.27.158:8004/mongoose/findOne HTTP/1.1 Accept-Encoding: identity Content-Length: 13 Content-Type: application/x-www-form-urlencoded Cookie: connect.sid=s%3AejDnEecHgIq1kUmVvIl2UKNrUroO1OEU.iXv72P0TKe1Dtf3kAcfpQVJC4upe77FM1CVTjm8Xd3Q Host: 20.42.27.158:8004 X-Screener-Uuid: ebeca323-e03f-4c0b-9c61-66ad44e8fdb0

References

https://www.owasp.org/index.php/Top_10_2013-A1-Injection