Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-100580

$out should require renameCollection privileges in addition to insert and remove privileges

    • Type: Icon: Improvement Improvement
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 6.0.13
    • Component/s: None
    • Query Execution

      Please excuse the duplication, but I no longer have permission to view the SERVER-100504 ticket. Therefore, I had to submit this clarification in a new ticket. On the original Jira ticket, I received an error: "You can't view this issue"

       


       

      In MongoDB version 6.0.13-10, a user without permission to rename collections or execute the drop command is still able to overwrite a protected collection using the $out operator in an aggregation pipeline.

      According to the "$out (aggregation)" documentation, the $out stage "calls the renameCollection command with dropTarget: true to rename the temporary collection to the destination collection." Therefore, if a user lacks permission for the rename or drop commands, they should not be able to overwrite a protected collection via an aggregation pipeline.

      MongoDB version: 6.0.13-10

      The following operations correctly prevent execution due to insufficient permissions on the app_test collection:

      db.getCollection("app_test").renameCollection("app_test_2");
      db.getCollection("app_test").renameCollection("app_test_2", { dropTarget: true });
      db.getCollection("app_test").drop();
      

      They all return (it is the except behavior):

      MongoServerError[Unauthorized]: not authorized on admin to execute command...
      

      However, the following aggregation is capable of overwriting the app_test collection:

      db.getCollection("test").aggregate([ { $out: "app_test" } ]);
      

      We need the ability to insert, update, and delete data in a specific collection (i.e., perform basic CRUD operations). Additionally, we require the capability to generate reports or temporary/helper collections using the aggregation framework, which we then materialize with the $out operator.

      However, we want to prevent the accidental overwriting of an existing collection's data. For this reason, we created a custom role that allows for both CRUD operations and the use of the $out operator. Unfortunately, there was an incident where an operator unintentionally used $out and overwrote an existing collection, resulting in data loss.

      Based on the $out documentation, under the "Replace Existing Collection" section, the following command is executed internally:
      https://www.mongodb.com/docs/v6.0/reference/operator/aggregation/out/

      db.getCollection("tmp.agg_out.ce468f99-a4fa-4a6a-ad5a-5cc04f481979").renameCollection("app_test", { dropTarget: true });
      

      This command renames the temporary collection to "app_test" and drops the existing "app_test" collection if it exists.
      However, I do not have permission to drop the "app_test" collection.

      Based on my understanding, I am able to perform an operation that I don't have permission for, and as a result, I could inadvertently cause data loss using the `$out` operator. a következőhöz: pár másodperc
      Based on my understanding, I can execute an operation without having the required permissions, which can inadvertently lead to data loss using the $out operator.

      Below is the code that reproduces the issue:

      use mydb;
      // Create test data
      if (!db.getCollectionNames().includes("app_test")) {
          db.createCollection("app_test");
          db.app_test.insertMany([{ name: "item1" }, { name: "item2" }]);
      }
      if (!db.getCollectionNames().includes("test")) {
          db.createCollection("test");
          db.test.insertMany([{ name: "other1" }, { name: "other2" }]);
      }
      
      // Create a custom role
      db.createRole(
          {
              role: "customReadWriteRole",
              privileges: [
                  {
                      resource: { db: "mydb", collection: "" }, actions: [
                          "createCollection",
                          //"dropCollection", --> dropCollection not allowed by default
                          "find",
                          "insert",
                          "listCollections",
                          "remove",
                          //"renameCollectionSameDB", --> renameCollectionSameDB not allowed by default
                          "update",
                      ]
                  },
              ],
              roles: []
          },
          { w: "majority", wtimeout: 5000 }
      );
      
      // Create a user with custom role
      db.createUser({
          user: "limitedUser",
          pwd: "password",
          roles: [{ role: "customReadWriteRole", db: "mydb" }]
      });
      

      Login with limited user

      mongosh -u limitedUser -p password --authenticationDatabase mydb
      
      // Use my test database
      use mydb;
      
      // Try to drop or rename a collection, I want to see an error (and I got an error, so it is okay)
      db.getCollection("app_test").renameCollection("app_test_2");
      db.getCollection("app_test").renameCollection("app_test_2", { dropTarget: true });
      db.getCollection("app_test").drop();
      
      // I try an aggregate, I want to see an error because I do not have the right to drop or rename a collection
      // But I do not see an error, I can replace the original app_test collection with the result of the aggregate
      db.getCollection("test").aggregate([ { $out: "app_test" } ]);
      

      Impact:
      A user can inadvertently execute an aggregation pipeline with $out that drops an existing collection—even though they do not have explicit permission to perform such operations—leading to potential data loss.

      Conclusion:
      The $out stage should enforce the same authorization checks as the underlying renameCollection and drop commands. The current behavior allows privilege escalation, as the internal operation bypasses user permissions, which is a security risk.

            Assignee:
            Unassigned Unassigned
            Reporter:
            f.imre.90@gmail.com Imre Fazekas
            Votes:
            0 Vote for this issue
            Watchers:
            12 Start watching this issue

              Created:
              Updated: