CVE-2026-88028 / CVE-2026-88027: Query-Operator Injection in Laravel MongoDB Relation Identifiers
Summary
Two related vulnerabilities in mongodb/laravel-mongodb, the official MongoDB integration for Laravel, allow an authenticated attacker to inject MongoDB query operators into polymorphic and embedded-document relation identifiers. The library passed caller-supplied relation keys directly to MongoDB queries without validating whether they contained operator documents (arrays with $-prefixed keys). The result is unauthorized document disclosure: a query intended to fetch a single specific document instead returns all documents that match the injected operator condition.
Both CVEs were assigned by MongoDB, Inc. on September 10, 2026. The fix ships in version 5.11.0, released the same day. This post presents an independent root-cause analysis derived from the commit diff, a self-contained proof of concept, and verification of the patch.
An authenticated user who can write a MongoDB operator array to a polymorphic relation key field can cause the application to disclose documents they are not authorized to read. No remote code execution; confidentiality impact only.
Vulnerability Metadata
| Field | Detail |
|---|---|
| CVE IDs | CVE-2026-88028 (polymorphic / MorphTo), CVE-2026-88027 (embedded-document / BelongsTo) |
| CWE | CWE-943 — Improper Neutralization of Special Elements in Data Query Logic |
| CVSS v4.0 | 7.1 HIGH — AV:N / AC:L / AT:N / PR:L / UI:N / VC:H / VI:N / VA:N |
| CVSS v3.1 | 6.5 MEDIUM — AV:N / AC:L / PR:L / UI:N / S:U / C:H / I:N / A:N |
| Affected versions | mongodb/laravel-mongodb 1.2.0 — 5.10.x (semver: < 5.11.0) |
| Fixed version | 5.11.0 (commit 0634653, September 10, 2026) |
| Discovered by | MongoDB, Inc. (internal — source: INTERNAL) |
| CISA KEV | Not listed; Exploitation: none; Automatable: no |
| References | NVD · PHPLARA-265 · PR #3580 |
Background
Laravel and Eloquent relations
Laravel's Eloquent ORM models relationships between database records through relation classes: BelongsTo, MorphTo, BelongsToMany, and MorphToMany. When a relation is resolved, Eloquent reads an identifier stored on the parent model and uses it to query the related model. For a polymorphic relation (MorphTo), this identifier is stored as a raw value in a column such as has_image_id.
MongoDB query operators
MongoDB filters are expressed as documents (PHP arrays). A simple equality filter looks like ['_id' => $value]. MongoDB also supports operator documents: ['_id' => ['$ne' => null]] means "documents whose _id is not null" — matching every document in the collection. The key distinction is that a scalar or plain array without $-prefixed keys is treated as a literal value, while an array whose keys start with $ is treated as a query operator.
Why this matters in PHP/Laravel
PHP's JSON decoding and form parsing can produce nested arrays from user input. A POST body like has_image_id[$ne]=null deserializes to ['$ne' => null] — exactly the shape of a MongoDB operator document. If an application stores this value in a relation identifier column without sanitization, and the ORM passes it unvalidated to a MongoDB query, the operator executes against the database.
Root Cause Analysis
The fix commit 0634653 introduces a new guard method, Builder::assertKeyIsNotOperator(), and applies it at five distinct call sites that previously passed relation keys directly to MongoDB without validation.
The guard method
The five unguarded paths (before 5.11.0)
Before the fix, the following code paths forwarded relation identifiers to MongoDB without any operator check:
| File | Method | Call site |
|---|---|---|
Query/Builder.php | convertKey() | Central choke point: find(), delete($id), where('_id', ...), whereIn('_id', ...) |
Relations/BelongsTo.php | addConstraints() | Owner-key constraint for standard belongs-to relations |
Relations/MorphTo.php | addConstraints() | Polymorphic relation constraint — CVE-2026-88028 |
Relations/BelongsToMany.php | addConstraints() | Pivot-key constraint for many-to-many |
Relations/MorphToMany.php | addEagerConstraints() | Eager-load constraint for polymorphic many-to-many |
The MorphTo path in detail (CVE-2026-88028)
When $this->parent->{foreignKey} is an operator array (e.g., ['$ne' => null]), the vulnerable version builds the filter ['_id' => ['$ne' => null]] and sends it to MongoDB, which interprets it as "all documents where _id is not null" — every document in the collection.
Attack Scenario
The attack requires an authenticated user who can influence the value stored in a polymorphic relation key column. This is a realistic condition in applications that accept user-supplied identifiers for associating records (e.g., profile images, attachments, comments).
image_id[$ne]=, which PHP parses to ['$ne' => null]. The application stores this value in the has_image_id column for a resource they control.
db.users.find({'_id': ObjectId('...')}), the database receives db.users.find({'_id': {'$ne': null}}) — returning all documents in the collection.
Proof of Concept
The following was reproduced in a controlled lab environment using mongodb/laravel-mongodb 5.10.0 (the last vulnerable version) against MongoDB 7.
Docker Compose with mongo:7 and php:8.3-cli containers. Laravel installed via Composer. The ext-mongodb PHP extension compiled and enabled. All code runs in an isolated local network with no external exposure.
Demonstrating the injection
// exploit_demo.php — run against mongodb/laravel-mongodb 5.10.0
require __DIR__ . '/vendor/autoload.php';
use MongoDB\Client;
$client = new Client('mongodb://mongodb:27017');
$db = $client->laravel_cve;
// Seed two users with different privilege levels
$db->users->insertMany([
['name' => 'Alice', 'role' => 'admin', 'secret' => 'flag{admin_secret_data}'],
['name' => 'Bob', 'role' => 'user', 'secret' => 'nothing sensitive'],
]);
// Legitimate query — returns exactly one document
$bob = $db->users->findOne(['name' => 'Bob']);
$result = $db->users->findOne(['_id' => $bob['_id']]);
// → returns Bob only ✓
// What laravel-mongodb 5.10.0 executes when the MorphTo relation
// identifier has been poisoned with an operator array:
$injected = ['$ne' => null];
$cursor = $db->users->find(['_id' => $injected]);
// → {'_id': {'$ne': null}} matches ALL documents
Output
{"$ne":null} injected as relation key returns all documents, including Alice's privileged flag{admin_secret_data}.
The injected operator returned Alice's record — including the secret field — even though the query was initiated by Bob's session context. In a real application using MorphTo, this would expose any related model that the attacker's operator condition matches.
Verifying the fix
After upgrading to 5.11.0, the new guard method catches the operator before it reaches MongoDB:
// verify_fix.php — run against mongodb/laravel-mongodb 5.11.0
use MongoDB\Laravel\Query\Builder;
$injected = ['$ne' => null];
try {
Builder::assertKeyIsNotOperator($injected);
} catch (\InvalidArgumentException $e) {
// Operator rejected before any database query is issued
echo $e->getMessage();
}
Builder::assertKeyIsNotOperator() throws InvalidArgumentException before any query reaches MongoDB.Version 5.11.0 throws InvalidArgumentException at the PHP level before any query is issued to the database, correctly rejecting operator arrays at all five previously unguarded code paths.
Detection
Is my application affected?
Your application is potentially affected if all of the following are true:
1. You use mongodb/laravel-mongodb version 1.2.0 through 5.10.x.
2. You use any of the following Eloquent relation types in a MongoDB-backed model: MorphTo, BelongsTo, BelongsToMany, or MorphToMany.
3. Authenticated users can supply or influence the value stored in the relation's foreign key column (e.g., via a form, API endpoint, or import feature).
Checking your installed version
composer show mongodb/laravel-mongodb | grep versions
Indicators of attempted exploitation
Look for PHP arrays containing $-prefixed keys being stored in relation columns. In application logs or a MongoDB profiler, queries with operator structure in _id fields (e.g., "_id": {"$ne": null}) on documents that should only ever be fetched by a single literal identifier are anomalous.
Remediation
Upgrade to mongodb/laravel-mongodb 5.11.0 or later. This is the only complete fix.
composer require mongodb/laravel-mongodb:^5.11
If an immediate upgrade is not possible
As a temporary measure, enforce strict input validation on any field that feeds into a relation identifier: reject values that are arrays, or validate that the value resolves to a scalar string or ObjectId before it is stored. This is a defence-in-depth measure only — it does not eliminate the library-level flaw and should not be used as a permanent substitute for upgrading.
Composite IDs
Plain arrays without $-prefixed keys (composite primary keys such as ['tenant' => 1, 'seq' => 2]) continue to work correctly in 5.11.0. The guard only rejects operator documents, not arbitrary arrays.
Timeline
| Date | Event |
|---|---|
| 2026-09-09 | CVE IDs reserved by MongoDB, Inc. |
| 2026-09-10 | CVEs published; fix shipped in mongodb/laravel-mongodb 5.11.0 (commit 0634653) |
| 2026-09-14 | Independent root-cause analysis and PoC reproduced; this post published |