NoSQL injection via equality-filter simplification of dollar-keyed documents in the LINQ provider (port to main branch)

XMLWordPrintableJSON

    • None
    • 0.2
    • Dotnet Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      Attackers who control a document compared for equality can smuggle query operators, making the database match and reveal records it never should.

      Impact

      Untrusted input intended as a literal equality comparand is executed as query operators, letting attackers read records they should not see, bypass equality-based lookup/authorization checks, and cause updates or deletes to hit the wrong documents.
      Severity: critical
      Exploitability: exploitable — Any application comparing a document-typed field to untrusted input via LINQ equality is exploitable with a single crafted payload such as

      {"$ne": null}

      ; the attacker fully controls the injected operator document.
      Customer data: customer_content, credentials, pii (api_response)

      Location

      • src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs:163 in VisitFieldOperationFilter
      • src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs:234 in VisitFieldOperationFilter (IsFieldEqValue guard)
      • src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs:184 in VisitFieldOperationFilter ($elemMatch $eq collapse)
      • src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstImpliedOperationFilterOperation.cs:41 in Render
      • src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFieldOperationFilter.cs:61 in Render
      • src/MongoDB.Driver/Linq/LinqProviderAdapter.cs:152 in TranslateExpressionToFilter

      Reproduction / trigger path

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

      Attacker controls: Full control of the operator document applied to the compared field: any query operator and arguments ($ne, $gt, $lt, $in, $regex, $exists, $where is not reachable since injection is field-level), enabling match-everything, match-nothing, and blind regex-based data extraction.

      1. (source) src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ComparisonExpressionToFilterTranslator.cs:94 in Translate — Attacker-influenced constant comparand (e.g. user-supplied BsonDocument) is serialized without any operator-shape validation.
      2. (hop) src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFilter.cs:91 in Compare — Builds AstFieldOperationFilter with AstComparisonFilterOperation(Eq, value) — the safe explicit {field:{$eq:value}} form.
      3. (hop) src/MongoDB.Driver/Linq/LinqProviderAdapter.cs:152 in TranslateExpressionToFilter — Every LINQ-expression filter (Find, Builders.Filter.Where; pipelines via AstPipelineOptimizer.cs:23) is passed through AstSimplifier unconditionally.
      4. (hop) src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs:163 in VisitFieldOperationFilter — IsFieldEqValue (line 232-238, regex-only guard) matches; explicit $eq is replaced by AstImpliedOperationFilterOperation carrying the raw dollar-keyed document. Same collapse for $elemMatch $eq at line 184.
      5. (sink) src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFieldOperationFilter.cs:61 in Render — Renders {field: rawValue}

        (via AstImpliedOperationFilterOperation.Render:41) into the query document sent to the MongoDB server, which parses the dollar-keyed value as query operators — the injection sink.

      Root cause

      • The translator serializes a constant equality comparand of any shape and builds the safe explicit form {field:{$eq:value}} with no operator-shape check (src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ComparisonExpressionToFilterTranslator.cs:94-95, AstFilter.cs:89-91).
      • AstSimplifier.VisitFieldOperationFilter rewrites {field:{$eq:value}} (and {field:{$elemMatch:{$eq:value}}}) to implied equality {field:value}

        ; its guards IsFieldEqValue and IsFieldElemMatchEqValue only exclude BsonType.RegularExpression, admitting BsonDocument values whose first element name starts with '$' (AstSimplifier.cs:161-164, 184, 232-238, 271-287).

      • AstImpliedOperationFilterOperation.Render returns the raw value and AstFieldOperationFilter.Render emits {fieldPath: value}

        with no re-validation (AstImpliedOperationFilterOperation.cs:39-42, AstFieldOperationFilter.cs:61).

      • The simplifier runs unconditionally on every LINQ filter surface: Find/Builders.Filter.Where (LinqProviderAdapter.cs:152), elemMatch filters (LinqProviderAdapter.cs:137), and aggregate/queryable pipelines (AstPipelineOptimizer.cs:23); the MongoDB server parses a dollar-keyed document in that position as query operators.

      Ownership

      Assigned teams: @mongodb/dbx-csharp-dotnet


      Filed from Aegis finding b82befbd7791 (scan scan-320b0a32a42c) · primitive: Dollar-keyed document constants survive equality-filter simplification and render as raw query operators, converting literal equality comparisons into attacker-chosen predicates. · categories: NoSQL Injection, Data Corruption, Authentication Bypass

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

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

              Created:
              Updated: