-
Type:
Task
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: None
-
None
-
0.2
-
Minor Change
-
Dotnet Drivers
-
Not Needed
-
None
-
None
-
None
-
None
-
None
-
None
Summary
Someone who can hand an app a raw binary document can smuggle extra fields into, or delete fields from, the database commands the app sends.
Impact
Structural injection into MongoDB commands built by the application — added or removed query conditions, command options, and update/insert fields — enabling scope-filter bypass (cross-tenant reads), unauthorized writes, and persistent data corruption.
Severity: high (upper bound critical)
Exploitability: likely_exploitable — The attacker controls every byte of the slice including the length prefix; the driver-side chain is fully open, and exploitation only needs an app that forwards external BSON bytes via the raw/lazy document types.
Customer data: customer_content, customer_metadata (api_response)
Location
- src/MongoDB.Bson/IO/BsonBinaryWriter.cs:518 in WriteRawBsonDocument
- src/MongoDB.Bson/IO/BsonBinaryWriter.cs:496 in WriteRawBsonArray
- src/MongoDB.Bson/IO/BsonStreamExtensions.cs:298 in WriteSlice
- src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs:61 in RawBsonDocument(byte[])
- src/MongoDB.Bson/ObjectModel/LazyBsonDocument.cs:60 in LazyBsonDocument(byte[])
- src/MongoDB.Bson/ObjectModel/RawBsonArray.cs:47 in RawBsonArray(IByteBuffer)
- src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs:70 in SerializeValue
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 raw slice bytes: the declared inner length, and all element types/names/values, letting the attacker choose exactly which sibling fields appear in or disappear from the enclosing command document.
- (source) src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs:61 in RawBsonDocument(byte[]) — Attacker-supplied byte blob is wrapped with no validation of the embedded length prefix against bytes.Length (delegates to the IByteBuffer ctor at :47-55 which only null-checks).
- (hop) src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs:70 in SerializeValue — When the app embeds the raw document in a filter/update/insert, serialization hands value.Slice directly to the binary writer.
- (hop) src/MongoDB.Bson/IO/BsonBinaryWriter.cs:518 in WriteRawBsonDocument — Slice is written verbatim; no context is pushed so BackpatchSize and the MaxDocumentSize check are skipped (lines 520-533), and the inner prefix is never checked against slice.Length.
- (sink) src/MongoDB.Bson/IO/BsonStreamExtensions.cs:316 in WriteSlice — stream.WriteBytes emits the attacker bytes unmodified into the outbound OP_MSG; the server parses the enclosing command using the attacker's inner length prefix, shifting element boundaries (injection or absorption of sibling fields).
Root cause
- The public constructors RawBsonDocument(byte[]) (src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs:61, delegating to :47-55), LazyBsonDocument(byte[]) (src/MongoDB.Bson/ObjectModel/LazyBsonDocument.cs:60), and RawBsonArray (src/MongoDB.Bson/ObjectModel/RawBsonArray.cs:47) accept arbitrary bytes and never compare the embedded int32 length prefix to the actual byte count.
- Their serializers pass the slice straight to the writer (src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs:70, LazyBsonDocumentSerializer.cs:63).
- BsonBinaryWriter.WriteRawBsonDocument (src/MongoDB.Bson/IO/BsonBinaryWriter.cs:518) and WriteRawBsonArray (:496) copy the slice verbatim; no context is pushed, so BackpatchSize and the MaxDocumentSize check never run (:520-533).
- WriteSlice (src/MongoDB.Bson/IO/BsonStreamExtensions.cs:298-320) only null-checks and emits slice.Length bytes at :316; the enclosing document's size is backpatched from actual stream position, so the outer OP_MSG stays wire-valid while the inner prefix decides where the server ends the subdocument — a short prefix injects the trailing bytes as sibling elements of the enclosing command, a long prefix absorbs subsequent app/driver-written fields into the attacker's subdocument.
Ownership
Assigned teams: @mongodb/dbx-csharp-dotnet
Filed from Aegis finding 2d0a8fa0e921 (scan scan-e837ccfa32ab) · primitive: Attacker-chosen inner length prefix shifts element boundaries in the enclosing command, injecting sibling fields or absorbing following fields into the attacker's subdocument. · categories: NoSQL Injection, Data Corruption, Cross-Tenant Access