Database technology continues to evolve to meet different application needs. One example of this is the adoption of NoSQL databases used by many different modern web applications. NoSQL databases depart from the traditional table-based storage mechanisms widely known and loved (mildly appreciated?), and instead store simple key-value data pairs, JSON documents, graph data, or tuples (and variations of these types).
A popular NoSQL database type is MongoDB. MongoDB falls under the document-based NoSQL storage model, requiring that all data is stored in JSON-formatted documents. This integrates nicely with lots of web applications that need fast access to data in a format that's convenient and easy to work with.
As a pen tester, I've run across some MongoDB databases. Here are some tips on effectively pillaging MongoDB.
MongoDB Files
From your shell, look for a MongoDB process running. Check for command-line arguments that indicate the location of the mongodb.conf configuration file. If the mongod process isn't running, still check the usual places for a mongodb.conf file (/etc, /usr/local/etc, et cetera).
The mongodb.conf file is straightforward, but it has a lot of whitespace and comments. The interesting entry is "dbpath", which points to the location of the database files.
The MongoDB files themselves require a little explanation:
- cust.0, local.0 - These are database files for MongoDB. Each MongoDB instance has "local" as a database, and one or more additional user databases. Database files are preallocated in size and populated as needed, growing to "cust.1", "cust.2" as needed.
- cust.ns, local.ns - Namespace files, which also store indexes and collections of data within a database (conveniently referred to as a "collection"; more on collections below).
- journal/ - Used for transaction logging and recovery following a MongoDB crash.
- mongod.lock - Present when MongoDB is running, removed when it stops cleanly.
- storage.bson - MongoDB storage metadata resource.
- _tmp - Temporary use directory by MongoDB.
MongoDB Data Replication
You can inspect data locally on the server, or tar up the mongodb directory and copy it to a local box for analysis.
Use any file transfer technique you have available; I'm using Netcat here. First, on my analysis system:
Next, tar and send the data to your listener:
I'm using a Mac for analysis with Homebrew for package management. Installing MongoDB is a simple "brew install mongodb" for me. Debian-based Linux users can use "apt-get install mongodb".
Once MongoDB is installed, create a simple configuration file as shown then start the mongod process with the "-config" argument. You can put it in the same directory as the database files:
With a local copy of the data, we can start to examine the database, collection, and document data.
Tip: If you get the error message "old lock file: ./mongod.lock. it probably means unclean shutdown" and mongod.lock exists, remove the mongod.lock file and start mongod again.
MongoDB Data Inspection
The "mongo" utility is used to access the local MongoDB data files locally. By default, this requires no authentication. First, we query the available databases (these names also match the *.ns files in the dbpath directory):
Here we see the two databases as expected: cust, and local. The local database is used for instance-specific data, while the cust database is data stored by the customer application. Next, we switch from the default database name of "test" to the "cust" database and enumerate the available collections:
In MongoDB, a collection is a grouping of documents within a database. Logically, it's like a table in a traditional RDBMS system, but without the rigid data formatting constraints. Using the constant "db" prefix, we can use the collection name and built-in collection methods available. The db.collection.findOne() method with no arguments will return the first entry in the collection:
Tip: None of these names, passwords, credit card numbers, or phone numbers are real. Thanks.
Now that we have some information about the document structure, we can apply query operators to focus on specific data. For example, if we want to search only for the first entry where the GivenName is "Joshua", we can use the following query with a simple JSON declaration:
If you want to collect all the matches for a given query, use the db.collection.find() method:
Here, the data is not "prettified", but we get more than one record in the results. For more examples of using the query() method with different data sets, see the Find or Query Data with the mongo Shell article.
Using findOne() and find() you can retrieve data from the collection and database, but it's not terribly convenient for large-scale pillaging. As an alternative, we can use the command-line tool "mongoexport". This tool requires a database and collection name, and will generate a JSON file as the output:
If you prefer, you can also export data in CSV format, but you have to specify each of the field names you want exported, as shown:
MongoDB And You
With a little background information on how to interact with a MongoDB instance, you can successfully pillage useful data. Keep these steps in mind, and apply them when you encounter a MongoDB instance in your next pen test.
-Josh Wright
@joswr1ght