-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Minor - P4
-
None
-
Affects Version/s: None
-
Component/s: BSON
-
None
-
None
-
Java Drivers
-
Not Needed
-
None
-
None
-
None
-
None
-
None
-
None
-
- Summary
The Java driver's `RawBsonDocument` metadata accessors accept a malformed BSON document that contains a string field with declared length `0`, even though canonical BSON document decoding rejects the same bytes as an invalid string. This matters because `size()`, `containsKey(...)`, and `getFirstKey()` can report attacker-controlled malformed data as a real field before value validation has run.
- Summary
-
- Affected
- Project: mongodb-mongo-java-driver
- Repo: https://github.com/mongodb/mongo-java-driver
- Pinned ref: a1cd54a4fd9d7afc85884e1278b9e3bd250923f7
- Severity: CVSS 3.1 5.9/10 — `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N`
-
- Root cause
`RawBsonDocument(byte[], offset, length)` stores caller-supplied BSON bytes after only null, offset, and minimum document-size checks at `bson/src/main/org/bson/RawBsonDocument.java:105`; it does not decode element values before exposing metadata accessors. `size()` walks element headers, increments the count, reads each name, and calls `skipValue()` at `bson/src/main/org/bson/RawBsonDocument.java:211`, while `getFirstKey()` returns the first name immediately after `readName()` at `bson/src/main/org/bson/RawBsonDocument.java:242`, and `containsKey(...)` returns true as soon as the read name matches at `bson/src/main/org/bson/RawBsonDocument.java:254`. On the skip path, `BsonBinaryReader.skipValue()` handles `STRING` by reading a size and skipping that many bytes at `bson/src/main/org/bson/BsonBinaryReader.java:356`; its helper rejects only negative sizes at `bson/src/main/org/bson/BsonBinaryReader.java:379`, so a declared string length of `0` is accepted for skipping. The canonical decode path does validate the value: `BsonDocumentCodec.decode()` appends each field by calling `readValue(...)` at `bson/src/main/org/bson/codecs/BsonDocumentCodec.java:80`, `BsonStringCodec.decode()` reaches `reader.readString()` at `bson/src/main/org/bson/codecs/BsonStringCodec.java:30`, and `ByteBufferBsonInput.readString()` rejects non-positive string sizes at `bson/src/main/org/bson/io/ByteBufferBsonInput.java:124`.
- Root cause
-
- Reproduction
```bash
bash ./poc/run.sh
```
- Reproduction
```text
TRIGGERED: java RawBsonDocument accessors accepted malformed zero-length string field
```
This line is emitted only when `size()`, `containsKey("a")`, and `getFirstKey()` accept the malformed raw document while full `BsonDocumentCodec` decoding rejects the same bytes with the expected non-positive string-length error. A build or setup failure, or an accepted full decode, does not produce this fingerprint.
-
- Impact
A remote malicious MongoDB-compatible server peer that supplies BSON documents to a Java driver client can provide malformed BSON bytes that pass `RawBsonDocument` metadata checks as if field `a` exists, while canonical decoding rejects those bytes as invalid. The practical preconditions are that the application receives or constructs a `RawBsonDocument` from peer-controlled bytes before full value decoding, and then uses `size()`, `containsKey(...)`, or `getFirstKey()` as a validation, routing, authorization, or policy predicate. Under those conditions the bug is a data-integrity issue: application logic can make decisions on malformed data that should have been rejected, while the constructor and BSON type/name guards are satisfied and the full string-value validation is bypassed.
- Impact
-
- Suggested fix
```001-fix.diff
diff --git a/bson/src/main/org/bson/BsonBinaryReader.java b/bson/src/main/org/bson/BsonBinaryReader.java
index 7f2a2a1..ce20a7b 100644- a/bson/src/main/org/bson/BsonBinaryReader.java
+++ b/bson/src/main/org/bson/BsonBinaryReader.java
@@ -354,10 +354,10 @@ public class BsonBinaryReader extends AbstractBsonReader {
skip = 0;
break;
case STRING:
- a/bson/src/main/org/bson/BsonBinaryReader.java
- Suggested fix
- skip = readSize();
+ skip = readStringSize();
break;
case SYMBOL: - skip = readSize();
+ skip = readStringSize();
break;
case TIMESTAMP:
skip = 8;
@@ -366,7 +366,7 @@ public class BsonBinaryReader extends AbstractBsonReader {
skip = 0;
break;
case DB_POINTER: - skip = readSize() + 12; // String followed by ObjectId
+ skip = readStringSize() + 12; // String followed by ObjectId
break;
default:
throw new BSONException("Unexpected BSON type: " + getCurrentBsonType());
@@ -385,6 +385,15 @@ public class BsonBinaryReader extends AbstractBsonReader { return size; }
+ private int readStringSize() {
+ int size = readSize();
+ if (size <= 0)
+ return size;
+ }
+
protected Context getContext() {
return (Context) super.getContext();
}
diff --git a/bson/src/main/org/bson/RawBsonDocument.java b/bson/src/main/org/bson/RawBsonDocument.java
index 9fc60e3..963997e 100644
— a/bson/src/main/org/bson/RawBsonDocument.java
+++ b/bson/src/main/org/bson/RawBsonDocument.java
@@ -243,7 +243,9 @@ public class RawBsonDocument extends BsonDocument {
try (BsonBinaryReader bsonReader = createReader()) {
bsonReader.readStartDocument();
try
catch (BsonInvalidOperationException e) {
throw new NoSuchElementException();
}
@@ -259,9 +261,10 @@ public class RawBsonDocument extends BsonDocument {
try (BsonBinaryReader bsonReader = createReader()) {
bsonReader.readStartDocument();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
- if (bsonReader.readName().equals(key)) {
+ String name = bsonReader.readName();
+ bsonReader.skipValue();
+ if (name.equals(key)) { return true; } - bsonReader.skipValue();
}
bsonReader.readEndDocument();
}
```
-
- References
- https://github.com/mongodb/mongo-java-driver/blob/a1cd54a4fd9d7afc85884e1278b9e3bd250923f7/bson/src/main/org/bson/RawBsonDocument.java#L105
- https://github.com/mongodb/mongo-java-driver/blob/a1cd54a4fd9d7afc85884e1278b9e3bd250923f7/bson/src/main/org/bson/RawBsonDocument.java#L211
- https://github.com/mongodb/mongo-java-driver/blob/a1cd54a4fd9d7afc85884e1278b9e3bd250923f7/bson/src/main/org/bson/RawBsonDocument.java#L254
- https://github.com/mongodb/mongo-java-driver/blob/a1cd54a4fd9d7afc85884e1278b9e3bd250923f7/bson/src/main/org/bson/BsonBinaryReader.java#L356
- https://github.com/mongodb/mongo-java-driver/blob/a1cd54a4fd9d7afc85884e1278b9e3bd250923f7/bson/src/main/org/bson/io/ByteBufferBsonInput.java#L124
- is related to
-
GODRIVER-3931 Go driver bson.Raw.Validate panic on zero-length BSON document
-
- Needs Triage
-
-
NODE-7598 js-bson: three input-validation gaps in parsing/serialization
-
- Investigating
-