ExportXMLWordPrintableJSON

    • None
    • 0.1
    • Fully Compatible
    • Dotnet Drivers
    • Not Needed
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      A user who controls the field names in a replaced document can smuggle update operators, silently changing what the application writes to the database.

      Impact

      The server executes attacker-chosen update operators ($set/$unset/$rename/$inc/...) against the matched document instead of storing a literal replacement, silently corrupting records and enabling manipulation of application-managed fields (e.g. preserving privileges a replace was meant to wipe, or setting nested fields the application never exposed).
      Severity: high
      Exploitability: exploitable — Any caller-supplied replacement whose first top-level key starts with '$' is serialized and executed unmodified; the attacker needs only to feed such a key to an application that passes untrusted field names to FindOneAndReplace, a common pattern with BsonDocument/dynamic models.
      Customer data: customer_content (none)

      Location

      • src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs:133 in GetCommandValidator
      • src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs:114 in CreateCommand
      • src/MongoDB.Driver/MongoCollectionImpl.cs:1057 in CreateFindOneAndReplaceOperation
      • src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs:180 in CreateOperation
      • src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs:42 in CommandUsingCommandMessageWireProtocol (field _commandValidator)

      Reproduction / trigger path

      Analyzed trigger path from static analysis — not an executed PoC. Confirm with a concrete repro before handing off.

      Attacker controls: Attacker controls the top-level field names and values of the replacement document passed to IMongoCollection.FindOneAndReplace; a first key such as "$set" selects operator-update semantics, after which every top-level key is an attacker-chosen update operator with dotted-path targets.

      1. (source) src/MongoDB.Driver/MongoCollectionImpl.cs:463 in FindOneAndReplace — Public driver API accepts the caller-supplied replacement with only a null check — no content or element-name validation.
      2. (hop) src/MongoDB.Driver/MongoCollectionImpl.cs:1057 in CreateFindOneAndReplaceOperation — Replacement is wrapped raw in a BsonDocumentWrapper; serialization (and thus any validation opportunity) is deferred to wire time.
      3. (hop) src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs:114 in CreateCommand — Raw replacement becomes the 'update' field of the findAndModify command — the field whose first key selects replace vs operator semantics on the server.
      4. (hop) src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs:133 in GetCommandValidator — Returns NoOpElementNameValidator.Instance instead of the replacement validator the driver uses on every other replace path.
      5. (hop) src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs:180 in CreateOperation — The no-op validator is installed as the WriteCommandOperation's CommandValidator — the only wire-level element-name gate for this command.
      6. (sink) src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs:42 in CommandUsingCommandMessageWireProtocol — On OP_MSG the command validator is stored but never applied (in-code TODO); the command is serialized unvalidated and the server interprets the '$'-prefixed first key as update operators.

      Root cause

      • FindOneAndReplaceOperation.GetCommandValidator returns NoOpElementNameValidator.Instance (src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs:133), so no element name in the command — including the replacement — is ever validated; the constructor only null-checks the replacement (line 44).
      • The raw replacement is embedded as the findAndModify 'update' field (FindOneAndReplaceOperation.cs:114), the field whose first key the server inspects to choose replacement vs operator-update semantics.
      • The no-op is installed as the sole wire-level gate (FindAndModifyOperationBase.cs:180), and on the modern OP_MSG protocol even that validator is never invoked (CommandUsingCommandMessageWireProtocol.cs:42 stores it with a TODO and never uses it).
      • The driver's own convention proves the gap: bulk/ReplaceOne pushes ReplacementElementNameValidator, which rejects top-level names starting with '$' (RetryableUpdateCommandOperation.cs:162, ReplacementElementNameValidator.cs:52), and FindOneAndUpdate routes 'update' through UpdateElementNameValidator (FindOneAndUpdateOperation.cs:183) — findOneAndReplace is the one replace path with no validation anywhere.

      Ownership

      Assigned teams: @mongodb/dbx-csharp-dotnet


      Filed from Aegis finding 6fc65c50212c (scan scan-2f1d896ebfb5) · primitive: Attacker-controlled first field name flips a literal document replacement into server-executed update operators because the driver skips replacement element-name validation. · categories: NoSQL Injection, Data Corruption

      https://docs.google.com/document/d/1osPjSiI-pvMXtrI49QVqwrIK21_nU38fsp7tJlQpDlY/edit?tab=t.0#heading=h.biijq6ke6z0p

            Assignee:
            Oleksandr Poliakov
            Reporter:
            Oleksandr Poliakov
            None
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: