PoC for CVE-2026-118383 — MongoDB unauthenticated use-after-free in ReplicationCoordinatorImpl::CatchupState during primary catch-up via readConcern.afterOpTime waiter poisoning, with deterministic crash oracle and fixed-version control.
# CVE-2026-118383 — MongoDB pre-auth use-after-free in primary catch-up
| | |
|---|---|
| **CVE** | CVE-2026-118383 |
| **Component** | MongoDB server, replication (`ReplicationCoordinatorImpl`) |
| **Affected** | 8.0.x before 8.0.37 (8.0.34 tested); 8.2 builds before the backport (8.2.12 tested) |
| **Fixed in** | 8.0.37 (`23dfc54219`, `6111165c32`) |
| **CWE** | CWE-416 — use-after-free |
| **Trigger** | Unauthenticated `readConcern.afterOpTime` waiter poisoning + primary catch-up abort |
| **Impact** | mongod crash / memory corruption in the replication coordinator |
A `ReplicationCoordinatorImpl::CatchupState` object can free itself while one
of its own methods is still running. The completion callback registered for the
primary catch-up waiter captures a raw `this`, ignores the completion status,
and calls `abort_inlock(kSucceeded)` from inside `abort_inlock()`. The inner
call resets `_repl->_catchupState` and destroys the object; the outer frame then
continues to read and write through the freed pointer.
The waiter can be kept unfulfilled by an unauthenticated client. Two
`readConcern.afterOpTime` waiters with adversarial opTime terms create an
ordering cycle in `std::multimap<OpTime, Waiter>`, so the real catch-up waiter
is never reached by `setValueIf_inlock()`. The next heartbeat concludes
"already caught up" and aborts catch-up, which destroys the unfulfilled waiter
and re-enters `abort_inlock()`.
Two server fixes shipped in 8.0.37:
- `23dfc542199ab3b88d6a9420b6cd3f125a897620` — SERVER-138175. `CatchupState`
becomes `enable_shared_from_this`, `_catchupState` becomes a `shared_ptr`, and
the callback holds a `weak_ptr`, returns on non-OK status, and only concludes
catch-up if it still owns the current state.
- `6111165c32bfc74f197e2dfeb9480d0b52154d68` — SERVER-138119. Rejects
client-supplied `readConcern.afterOpTime` unless the client holds
`ActionType::internal`.
## Quick start
```bash
# Vulnerable 8.0.34: deterministic double abort (UAF f