MongoDB is a non-relational database system known for its flexibility, scalability, and performance. Unlike relational databases that use tables with predefined structures, MongoDB stores data in key, value pairs. MongoDB has features that include high performance, query API, horizontal scalability, and high availability. It is supported by Linux, Windows, and macOS operating systems.
Wazuh is a free and open source security platform with unified XDR and SIEM capabilities. It protects workloads across on-premises, virtualized, containerized, and cloud-based environments.
This blog post demonstrates using Wazuh to monitor the activities performed on a MongoDB database.
Infrastructure
To get started with this demonstration, use the following environment for set-up:
- Wazuh OVA 4.8.0: A pre-built Wazuh OVA that contains all Wazuh central components. Use this guide to run the installation process of this environment.
- Ubuntu 22.04 (“Jammy”): We will install MongoDB 7.0 Enterprise Edition on this endpoint. This endpoint runs the Wazuh agent 4.8.0 which will be enrolled to the Wazuh server.
Configure Wazuh to monitor MongoDB
In this section, configure the Wazuh Logcollector module to collect logs and send them to the Wazuh server. Then, write custom rules to process the events occurring in the MongoDB database.
Ubuntu endpoint
Install MongoDB
Perform the following steps to install MongoDB on the Ubuntu endpoint. We recommend you run these commands with superuser privileges.
1. Import the public key for the package manager and create a list file for MongoDB:
$ curl -fsSL https://pgp.mongodb.com/server-7.0.asc | \ sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \ --dearmor $ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.com/apt/ubuntu jammy/mongodb-enterprise/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-enterprise-7.0.list
2. Update all the local package databases:
$ sudo apt-get update
3. Install the MongoDB package:
$ sudo apt-get install -y mongodb-enterprise
4. Enable the authentication and audit log. To keep track of authorized users that perform actions in the database. To set this up, configure the /etc/mongod.conf
file as seen in the highlighted section below:
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options # Where and how to store data. storage: dbPath: /var/lib/mongodb # engine: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # network interfaces: net: port: 27017 bindIp: 127.0.0.1 # how the process runs processManagement: timeZoneInfo: /usr/share/zoneinfo #security: security: authorization: enabled #operationProfiling: #replication: #sharding: ## Enterprise-Only Options: #auditLog: auditLog: destination: file format: JSON path: /var/log/mongodb/auditLog.json filter: '{ atype: { $in: [ "authCheck", "authenticate" ] } }' setParameter: { auditAuthorizationSuccess: true }
5. Start the MongoDB service:
$ sudo systemctl daemon-reload $ sudo systemctl start mongod $ sudo systemctl status mongod
Configure the Wazuh Logcollector module
1. Append the following configuration to the Wazuh agent /var/ossec/etc/ossec.conf
file. This configuration collects and forwards MongoDB logs to the Wazuh server for analysis:
<!-- Configure the Wazuh agent to collect and forward the MongoDB logs to the Wazuh server for analysis --> <ossec_config> <localfile> <log_format>json</log_format> <location>/var/log/mongodb/auditLog.json</location> </localfile> </ossec_config>
2. Restart the Wazuh agent to enable the configuration made above:
$ sudo systemctl restart wazuh-agent
Wazuh server
The Wazuh server natively decodes JSON type of log format hence there will be no need to create a decoder. When the logs are parsed, they will be decoded natively and subsequently trigger the rules attributed to them.
Perform the following steps to configure the Wazuh server to detect the activities performed on the MongoDB database.
1. Create the file mongodb_rules.xml in the /var/ossec/etc/rules directory
:
$ sudo touch /var/ossec/etc/rules/mongodb_rules.xml
2. Add the below detection rules to the /var/ossec/etc/rules/mongodb_rules.xml
file:
<group name="mongodb,"> <!-- This rule detects when a user authenticates to a database --> <rule id="100091" level="1"> <field name="atype" type="pcre2">authenticate</field> <field name="result" type="pcre2">^0$</field> <description>The user $(param.user) has successfully authenticated into the $(param.db) database.</description> </rule> <rule id="100092" level="9" frequency="3" timeframe="60"> <if_matched_sid>100091</if_matched_sid> <description>The user $(param.user) has successfully authenticated to the $(param.db) database.</description> <mitre> <id>T1556</id> </mitre> </rule> <!-- This rule will detect when a user authentication failure occurs --> <rule id="100093" level="9"> <field name="atype" type="pcre2">authenticate</field> <field name="result" type="pcre2">(?!0\b)\d+</field> <description>Authentication attempt failure from a user named $(param.user) to the $(param.db) database.</description> <mitre> <id>T1110</id> </mitre> </rule> <!-- This rule detects when a database is created.--> <rule id="100094" level="4"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">listCollections</field> <field name="result" type="pcre2">^0$</field> <description>A database name $(param.args.$db) has been created.</description> <mitre> <id>T1565.001</id> </mitre> </rule> <!-- This rule detects when a collection is created.--> <rule id="100095" level="4"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^create$</field> <field name="result" type="pcre2">^0$</field> <description>A collection name $(param.args.create), has been created in the $(param.args.$db) database.</description> <mitre> <id>T1485</id> </mitre> </rule> <!-- This rule detects when a document is being added to a collection "C" in the CRUD framework --> <rule id="100096" level="4"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^insert$</field> <field name="result" type="pcre2">^0$</field> <description>A document has been added to the $(param.args.insert) collection in the $(param.args.$db) database.</description> <mitre> <id>T1565</id> </mitre> </rule> <!-- This rule detects when a document is being read from your collection. "R" in the CRUD framework --> <rule id="100097" level="4"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^find$</field> <field name="result" type="pcre2">^0$</field> <description>A document has been read from the $(param.args.find) collection in the $(param.args.$db) database.</description> </rule> <!-- This rule detects when an update is being made to the collection of the database or a database. "U" in the CRUD framework --> <rule id="100098" level="4"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^update$</field> <field name="result" type="pcre2">^0$</field> <description>A document has been updated from the $(param.args.update) collection in the $(param.args.$db) database.</description> <mitre> <id>T1485</id> <id>T1565</id> </mitre> </rule> <!-- This rule detects when a delete action is being made to the collection of the database. "D" in the CRUD framework --> <rule id="100099" level="7"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^delete$</field> <field name="result" type="pcre2">^0$</field> <description>A document has been deleted from the $(param.args.delete) collection in the $(param.args.$db) database.</description> <mitre> <id>T1485</id> </mitre> </rule> <!-- This rule detects when a collection is dropped from a database --> <rule id="100100" level="7"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^drop$</field> <field name="result" type="pcre2">^0$</field> <description>A collection name $(param.args.drop), has been dropped from the $(param.args.$db) database.</description> <mitre> <id>T1485</id> </mitre> </rule> <!-- This rule detects when a database is dropped or deleted --> <rule id="100101" level="7"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">^dropDatabase$</field> <field name="result" type="pcre2">^0$</field> <description>A database name $(param.args.$db) has been deleted.</description> <mitre> <id>T1485</id> </mitre> </rule> <!-- This rule will detect when a user performs unauthorized CRUD action --> <rule id="100102" level="9"> <field name="atype" type="pcre2">authCheck</field> <field name="param.command" type="pcre2">(listCollections|create|insert|find|update|delete|drop|dropDatabase|createUser)</field> <field name="result" type="pcre2">(?!0\b)\d+</field> <description>An unauthorized user with details $(users) performed a $(param.command) command on the $(param.args.$db) database</description> <mitre> <id>T1565</id> </mitre> </rule> </group>
Where:
- Rule ID
100091
,100092
is triggered when a user is authenticated into a database. Authentication events are triggered 3 times, hence we have two rules to regulate it to alert once. - Rule ID
100093
is triggered when a user authentication fails. - Rule ID
100094
is triggered when a database is created. - Rule ID
100095
is triggered when a collection is created. - Rule ID
100096
is triggered when a document is added to a collection. - Rule ID
100097
is triggered when a document is read from a collection. - Rule ID
100098
is triggered when a document is updated in a collection. - Rule ID
100099
is triggered when a document is deleted from a collection. - Rule ID
100100
is triggered when a collection is deleted from a database. - Rule ID
100101
is triggered when a database is deleted. - Rule ID
100102
is triggered when a user performs an unauthorized CRUD action
3. Restart the Wazuh manager for the changes made to take effect:
$ sudo systemctl restart wazuh-manager
Testing the configuration
In MongoDB, data records are stored as documents. These documents could be gathered together and called a collection. A database in MongoDB can be referred to as an entity that stores one or more collections.
In the Ubuntu endpoint, the activities carried out to test our configuration in the MongoDB database are:
- Authenticating a user
- Authentication failure by a user
- Creating a database
- Creating a collection in a database
- Adding a document to a collection
- Reading a document from a collection
- Updating a document in a collection
- Deleting a document from a collection
- Dropping a collection from a database
- Deleting a database
- Unauthorized action performance
Creating a user
Perform the following steps to create an admin user in a specific database to be used for testing the configuration.
1. Run the following command to log in as the default mongodb
user:
$ mongosh
Enterprise test>
2. Switch to admin
database to use the admin privilege to create a user:
Enterprise test> use admin
Switched to db admin
3. Enter the username and password you wish to allocate to the user:
Enterprise admin> db.createUser({ user: "adminUser", pwd: passwordPrompt(), roles: [{role: "root", db: "admin"}]})
Enter password
4. Assign a password to the newly created user adminUser
:
Enter password ********
{ok:1}
5. Exit after successful completion:
Enterprise admin> exit()
Authenticating a user
1. Run the following command to authenticate as the adminUser
user to the admin
database:
$ mongosh --port 27017 --authenticationDatabase "admin" -u "adminUser" -p
Enter password
2. Enter the password you assigned to the user: adminUser
:
Enter password ********
Enterprise admin>
Authentication failure by a user
1. Run the following command to authenticate as an unauthorized user first_User
to the admin
database:
Enterprise admin> db.auth("first_User", passwordPrompt())
Enter password
2. Enter a password:
Enter password ********
MongoServerError[AuthenticationFailed]: Authentication failed.
Creating a database
Create the database test_db
by running the command below:
Enterprise test> use test_db
switched to db test_db
Creating a collection in a database
Run the following command to create a collection in the database test_db
:
Enterprise test_db> db.createCollection("users")
{ok:1}
Adding a document to a collection
While connected to the database test_db
, run the following command to create/insert a document into the collection users
. The command inserts the values firstname
, 27
, into the fields name
, age
, respectively:
Enterprise test_db> db.users.insertOne({ name: "firstname", age: 27,})
{ “Acknowledge”: true, “Inserted”: ObjectedId(“667192ff29228eb723597193”) }
Reading a document from a collection
While connected to the database test_db
which we have inserted the document to run the following command below to read the content of the document:
Enterprise test_db> db.users.find()
{ _id: ObjectId('667192ff29228eb723597193'), name: 'firstname', age: 27 }
Updating a document in a collection
While connected to the test_db
database, run the below command to update the document in the collection:
Enterprise test_db> db.users.updateOne({ name: "firstname" }, { $set: { email: "firstname@test.com" } })
{ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0 }
Deleting a document from a collection
In the test_db
database, the delete command will remove documents from the collection.
While connected to the test_db
database, run the following command to delete the document with _id <DOCUMENT_ID>
from the collection:
Enterprise test_db> db.users.deleteOne({ _id: ObjectId('<DOCUMENT_ID>')})
{ acknowledged: true, deletedCount: 1 }
Dropping a collection from a database
In the test_db
database, a collection is similar to a table in an RDBMS type of database, which is simply a collection of information. Similarly, we will delete this collection. Run the command given below to delete the collection:
Enterprise test_db> db.users.drop()
true
Deleting a database
Run the following command to delete the database test_db
:
Enterprise test_db> db.dropDatabase()
{ ok: 1, dropped: 'test_db' }
Unauthorized action performed by a user
1. Switch to test
database to test the action authorization of the current user adminUser
:
Enterprise admin> use test
Switched to db test
2. Create a user without the role to perform certain commands in the test database. Perform the following command given below:
Enterprise test> db.createUser({ user: "user", pwd: passwordPrompt(), roles:[]})
Enter password
- Enter a password to assigned to the user:
Enter password ********
{ok:1}
- Run the following command to authenticate as an unauthorized user
user
to thetest
database:
Enterprise test> db.auth("user", passwordPrompt())
Enter password
- Enter the password assigned in the previous step:
Enter password ********
{ok:1}
- From this point, any CRUD command as stated above, will be captured as
unauthorized
from the useruser
. Run the following to create a collection in the test database:
Enterprise test> db.createCollection("customers")
MongoServerError[Unauthorized]: not authorized on test to execute command { create: "customers", lsid: { id: UUID("63ce4396-7a21-472b-b5e2-469190b0295f") }, $db: "test" }
Visualizing alerts on the Wazuh dashboard
Perform the following steps to view the alerts on the Wazuh dashboard. From the Endpoint dashboard,
1. Navigate to Threat Hunting > +Add filter.
2. Then filter for rule.groups
in the Field dropdown.
3. Use the is
Operator fields.
4. Filter for mongodb
, in the Values field.
5. Click Save.
Conclusion
In this blog post, we demonstrate how Wazuh monitors the activities performed on a MongoDB database. This helps to provide visibility to the activities in a MongoDB database.
Wazuh is a free and open source enterprise security solution for uncovering security threats, incident response, and meeting regulatory requirements. Wazuh has an ever-growing community where users are supported. To learn more about Wazuh, please check out our documentation and blog posts.
References