MongoDB Replica Set UAF: Unauthenticated Crash (CVE-2026-118383)
Unauthenticated requests can crash a lagging MongoDB replica set primary before 4.4.32, 5.0.35, 6.0.30, 7.0.48, 8.0.37, 8.3.16 and 9.0.7.
TL;DR
- A use-after-free in MongoDB's replication catch-up lets an unauthenticated client plant waiters that crash a replica set member when it becomes primary. MongoDB published CVE-2026-118383 on November 10, 2026.
- Every replica set member on the affected branches is exposed, including shard and config server members. A standalone
mongoddoes not run catch-up and is not affected. - MongoDB rates it CVSS v4.0 9.5 Critical. Datadog reproduced a crash; MongoDB says code execution may be possible in some conditions. No attacks are reported, and it is not in CISA KEV as of November 13, 2026.
- Upgrade every member to its branch's fixed release: 4.4.32, 5.0.35, 6.0.30, 7.0.48, 8.0.37, 8.3.16 or 9.0.7. See "How do I fix it?" for Atlas and containers.
Am I affected?
You are exposed when all of these hold:
- The server is a MongoDB replica set member, including a shard or config server replica set member in a sharded cluster.
- Its version is in one of the affected ranges in the table.
- An attacker can reach the member's MongoDB port and send
pingcommands that carryreadConcern.afterOpTime. On vulnerable versions the server accepts those requests before it checks credentials, so enabling authentication does not block the setup.
A standalone mongod is not exposed. It has no primary catch-up state and cannot be made to take part in the election the crash needs.
The crash also needs the member to fall behind and then win an election. The attacker cannot cause either event, so the planted waiters are a trap that fires during a real maintenance event or failover.
| Branch | Affected versions | Fixed version |
|---|---|---|
| 9.0 | 9.0.0 to 9.0.6 | 9.0.7 |
| 8.3 | 8.3.0 to 8.3.15 | 8.3.16 |
| 8.0 | 8.0.0 to 8.0.36 | 8.0.37 |
| 7.0 | 7.0.0 to 7.0.47 | 7.0.48 |
| 6.0 | 6.0.0 to 6.0.29 | 6.0.30 |
| 5.0 | 5.0.0 to 5.0.34 | 5.0.35 |
| 4.4 | 4.4.0 to 4.4.31 | 4.4.32 |
MongoDB's advisory does not list the 8.2 branch, which reached end of life on July 31, 2026, and receives no fixes. The public proof of concept reports that 8.2.12 still showed the bug in a lab test that used a test-only setting. Treat 8.2 as unsupported and move it to a fixed branch.
Check the version of every member, not only the primary:
# On each host, including members in containers and members that were offline
mongosh --quiet --eval 'db.version()'
# In a replica set, list every member and its state
mongosh --quiet --eval 'rs.status().members.map(m => m.name + " " + m.stateStr)'
# Docker
docker exec <container> mongosh --quiet --eval 'db.version()'
# Kubernetes, for each member pod
kubectl exec <pod> -- mongosh --quiet --eval 'db.version()'
rs.status() returns an error on a standalone, which confirms that host is not a replica set member. For Atlas, open the cluster's maintenance settings; MongoDB says clusters are upgraded automatically during maintenance.
How do I fix it?
Upgrade every replica set member to the fixed release for its branch. A partial upgrade leaves the remaining members exposed, and a later election can move the primary role to one of them.
- Back up the deployment and confirm the fixed release for each branch from the vendor's downloads page.
- Upgrade one member at a time and let it rejoin before you move to the next. On a replica set, step down the primary first, or upgrade a secondary, so the set keeps a primary during the work.
- After each upgrade, run
db.version()on that member and confirm it reports the fixed build. Check members that were offline during the maintenance window too. - For Docker, pull the fixed image and recreate the container. Replacing the image without recreating the container leaves the old process running. The public proof of concept uses
mongodb/mongodb-community-server:8.0.37-ubi9for its fixed control run. - For Atlas, apply the pending maintenance. If the project defers maintenance, MongoDB recommends applying the upgrade as soon as possible.
There is no server setting that turns the vulnerable catch-up path off. Restricting network access to the member, for example to the application hosts and the other members, reduces who can plant the waiters. It does not remove the waiters already planted, and it does not fix the race. Keep the restriction only until every member is upgraded.
The fixed releases change two things. The catch-up callback now concludes only a live, current catch-up state, and only internal users may send readConcern.afterOpTime. Both changes are needed: the first stops the double cleanup, and the second stops a client from planting the waiters.
What is the root cause?
The primary catch-up completion callback held a raw pointer to a catch-up state that can free itself while the callback runs. An aborted catch-up therefore re-enters abort_inlock() on freed memory.
ReplicationCoordinatorImpl keeps one CatchupState while a newly elected primary applies the operations it missed. The state registers a waiter in _lastAppliedOpTimeWaiterList, a multimap keyed by OpTime. When the applied OpTime reaches the target, the waiter completes and the callback calls abort_inlock() to leave catch-up mode. abort_inlock() resets _catchupState, which destroys the state object.
The vulnerable callback ignored the completion status and did not check that its state was still the current one. In MongoDB 8.0.34, at src/mongo/db/repl/replication_coordinator_impl.cpp line 5267, the callback is:
auto targetOpTimeCB = [this](Status status) {
// Double check the target time since stepdown may signal us too.
const auto myLastApplied = _repl->_getMyLastAppliedOpTime_inlock();
if (_targetOpTime <= myLastApplied) {
...
abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
}
};
The state was held in a std::unique_ptr, so the object had no shared lifetime. The header at line 1972 declares std::unique_ptr<CatchupState> _catchupState;.
The public proof of concept adds the missing step. Two readConcern.afterOpTime waiters with adversarial OpTime terms create an ordering cycle in the multimap, so the real catch-up waiter is never reached by setValueIf_inlock(). The next heartbeat concludes that the member is already caught up and aborts catch-up. That abort resets the unfulfilled waiter, which synchronously runs the callback. The callback calls abort_inlock() again, and the outer frame continues through the state that the inner call freed.
How is it exploited?
- The attacker sends two unauthenticated
pingcommands withreadConcern.afterOpTimeto a reachable member. The chosen OpTime terms make the waiters sort wrongly in the multimap, so the real catch-up waiter cannot complete. - The waiters stay in place. They do not expire, and they survive until the member restarts, rolls back or steps down from primary.
- The member later falls behind another member and wins an election. The attacker cannot cause the lag or the election, so this step depends on a real maintenance event or outage.
- When catch-up ends, cleanup runs twice. The new primary reads and writes memory it has already freed, and it crashes before it accepts writes.
- Datadog reproduced the crash on 8.0.34 with a segmentation fault in
CatchupState::abort_inlock(). The lab used admin access to pause a member and force the failover; only the two ping requests came from an unauthenticated client.
MongoDB's advisory says the flaw may also allow arbitrary code execution in the server process under certain conditions. Datadog did not achieve code execution in its lab, and it does not claim the crash is reliable in every deployment.
How does the attack flow?
An unauthenticated client plants two afterOpTime waiters, a later failover makes that member primary while it is behind, and the catch-up cleanup runs twice on freed memory.
with readConcern.afterOpTime"] --> B["Waiters stored in the
lastAppliedOpTime multimap"] B --> C["OpTime ordering leaves the
real catch-up waiter unfulfilled"] C --> D["Member falls behind,
then wins an election"] D --> E["Catch-up ends and
abort_inlock() resets the waiter"] E --> F["Callback re-enters
abort_inlock() on freed state"] F --> G["New primary crashes
before accepting writes"]
What can an attacker do?
An unauthenticated attacker can crash a replica set member at the moment it becomes primary, which removes the primary from the set and interrupts writes until another member takes over.
The impact depends on the role of the crashed member:
- A crashed primary of a normal replica set causes an election and a short write outage. Another member can take over if one is available.
- A crashed shard replica set primary can stall operations that route through that shard until a new primary is elected.
- A crashed config server primary can block metadata changes for the cluster until the config server set elects a new primary.
The conditions are part of the impact:
- The attacker needs network reachability to the member's MongoDB port.
- The request needs no account and no user interaction.
- The member must later win an election while it is behind another member. The attacker cannot force that, so the crash is a delayed trap rather than an immediate attack.
- A standalone
mongodis not affected.
MongoDB says the flaw may allow arbitrary code execution under certain conditions. No source demonstrates code execution, and Datadog's reproduction ended in a crash. Treat the code-execution outcome as a vendor assessment, not as a demonstrated capability.
Why does the fix work?
The fix gives the catch-up state a shared lifetime and makes the callback check both the waiter's status and whether its state is still current. The callback can no longer run abort_inlock() a second time.
The diff between MongoDB 8.0.34 and 8.0.37 changes the ownership and the callback:
- _catchupState = std::make_unique<CatchupState>(this);
+ _catchupState = std::make_shared<CatchupState>(this);
...
- auto targetOpTimeCB = [this](Status status) {
+ auto targetOpTimeCB = [weakSelf = weak_from_this()](Status status) {
+ if (!status.isOK()) {
+ return;
+ }
+ auto self = weakSelf.lock();
+ if (!self || self->_repl->_catchupState != self) {
+ return;
+ }
const auto myLastApplied = _repl->_getMyLastAppliedOpTime_inlock();
if (_targetOpTime <= myLastApplied) {
...
- abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
+ self->abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
}
};
The header changes CatchupState to inherit from std::enable_shared_from_this<CatchupState> and holds it in a std::shared_ptr. The callback keeps a weak_ptr, so it can only conclude a state that is still alive and still installed in _catchupState. A non-OK status means another path already concluded the catch-up, and the callback returns without touching the state.
The second fix, commit 6111165c32, requires the internal user privilege for a client-supplied readConcern.afterOpTime. That removes the waiter-planting precondition for ordinary clients. The two commits are the 8.0.37 hotfix; the vendor lists the corresponding fixed releases for every affected branch.
How do I check if I was compromised?
No exploitation has been reported, and no indicators of compromise have been published. The checks look for the crash signature and the waiter requests in the server logs.
- Look for a
Slow queryentry with log ID 51803 for an unauthenticatedpingthat carriedreadConcern.afterOpTimeand ended withMaxTimeMSExpired. - Look for a member entering primary catch-up (log ID 21359) and then exiting it twice (log ID 21363) in one transition.
- Look for a fatal signal or backtrace that names
CatchupState::abort_inlock()after those two exits. - Check earlier logs from the same member. The two requests can arrive before the failover, and a member that later becomes primary carries them forward.
A normal election or a single catch-up exit does not match this pattern. Whether the request fields appear in the log depends on the logging settings, and the attr.remote field may show a loopback address instead of the client's real address. A failed attempt may leave no trace.
Timeline
- 2026-10-30: MongoDB authors the two fixes for the 8.0.37 hotfix, one for the catch-up state and one for
readConcern.afterOpTime. - 2026-11-10: MongoDB publishes CVE-2026-118383 at 20:23 UTC and releases the fixed versions. The two fix commits land at 23:07 and 23:09 UTC.
- 2026-11-11: Datadog Security Labs publishes its analysis and reproduction, and the public proof of concept is created.
- 2026-11-12: The Hacker News reports the flaw and MongoDB's statement that it is not aware of exploitation.
- 2026-11-13: CISA's KEV catalog does not list the CVE.
FAQ
Is CVE-2026-118383 exploited in the wild?
No. MongoDB says it found the flaw internally and is not aware of any attempts to take advantage of it. The CVE is not in CISA KEV as of November 13, 2026.
Does authentication stop the attack?
No, not on an affected version. The two ping commands are accepted before the server checks credentials, which is why Datadog could plant the waiters with an unauthenticated client. The 8.0.37 fix rejects a client-supplied readConcern.afterOpTime unless the client is an internal user.
Can a standalone mongod be crashed this way?
No. A standalone has no replica set and no primary catch-up state, so the double cleanup that causes the use-after-free cannot run. The advisory excludes standalone instances.
Can the attacker force the failover?
No. The attacker can plant the waiters, but the member must fall behind and win an election on its own. In the Datadog lab, the researchers used admin access to pause a member and trigger the election; only the two requests were unauthenticated.
What happens to an Atlas cluster?
MongoDB says Atlas clusters are upgraded automatically to fixed versions during cluster maintenance. If the project defers maintenance, MongoDB recommends applying the upgrade as soon as possible.
I run the 8.2 branch. Is it fixed?
The advisory does not list 8.2, which reached end of life on July 31, 2026. The public proof of concept reports that 8.2.12 still showed the bug with a test-only setting. Move the deployment to a supported branch and a fixed release.
Find exposed MongoDB members with Ostorlab
Ostorlab attack surface discovery inventories the internet-facing hosts and services in your estate. Use it to list the MongoDB ports you expose, so you know which members to upgrade or restrict first. It is an inventory of exposed hosts, not a detection of this CVE.
References
- CVE-2026-118383 record, MongoDB CNA, published 2026-11-10, CVSS v4.0 9.5 and CVSS v3.1 9.0: https://www.cve.org/CVERecord?id=CVE-2026-118383 (checked November 13, 2026)
- MongoDB, "Important MongoDB security update available (Action Required) - November 10, 2026", the vendor statement and the Atlas maintenance guidance: https://www.mongodb.com/community/forums/t/important-mongodb-security-update-available-action-required-november-10-2026/344843 (checked November 13, 2026)
- Datadog Security Labs, "CVE-2026-118383: Use-after-free vulnerability in MongoDB", November 11, 2026, the reproduction, the crash logs and the exploitation conditions: https://securitylabs.datadoghq.com/articles/cve-2026-118383-mongodb-server-use-after-free/ (checked November 13, 2026)
- Public proof of concept and analysis, the two-request trigger, the waiter ordering cycle and the fix commits: https://github.com/googlesky/CVE-2026-118383 (checked November 13, 2026)
- Fix commit 23dfc542199ab3b88d6a9420b6cd3f125a897620, "SERVER-138175 [v8.0.37-hotfix] Conclude primary catch-up only from a live, current catch-up state", merged 2026-11-10: https://github.com/mongodb/mongo/commit/23dfc542199ab3b88d6a9420b6cd3f125a897620 (checked November 13, 2026)
- Fix commit 6111165c32bfc74f197e2dfeb9480d0b52154d68, "SERVER-138119 [v8.0.37-hotfix] Allow only internal users to use readConcern.afterOpTime", merged 2026-11-10: https://github.com/mongodb/mongo/commit/6111165c32bfc74f197e2dfeb9480d0b52154d68 (checked November 13, 2026)
- MongoDB 8.0.34 source, the vulnerable callback at
src/mongo/db/repl/replication_coordinator_impl.cppline 5267 and theunique_ptrat line 1972 of the header: https://github.com/mongodb/mongo/blob/r8.0.34/src/mongo/db/repl/replication_coordinator_impl.cpp (checked November 13, 2026) - MongoDB 8.0.37 source, the fixed callback and the shared ownership: https://github.com/mongodb/mongo/blob/r8.0.37/src/mongo/db/repl/replication_coordinator_impl.cpp (checked November 13, 2026)
- MongoDB downloads page, the fixed Community Server builds: https://www.mongodb.com/try/download/community (checked November 13, 2026)
- MongoDB lifecycle schedule, the 8.2 end-of-life date of July 31, 2026: https://www.mongodb.com/legal/support-policy/lifecycles (checked November 13, 2026)
- Docker Hub, the official
mongoimage and its pull count: https://hub.docker.com/_/mongo (checked November 13, 2026) - CISA Known Exploited Vulnerabilities catalog JSON feed, checked for CVE-2026-118383 on November 13, 2026 (not listed): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (checked November 13, 2026)
- The Hacker News, "Critical Pre-Auth MongoDB Flaw (CVE-2026-118383) Can Crash Lagging Servers on Failover", November 12, 2026, the Datadog reproduction and the vendor's statement: https://thehackernews.com/2026/11/critical-pre-auth-mongodb-flaw-cve-2026.html (checked November 13, 2026)