Last active
December 12, 2021 23:27
-
-
Save mchow01/49f8979829f1c488d922 to your computer and use it in GitHub Desktop.
MongoDB Request Injection Attack in Node.js + Express Web Applications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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!