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:

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 the test 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 user user. 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.

MongoDB Wazuh Dashboard

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