Table of Contents
- BLUF (Bottom Line Up Front)
- Executive Summary
- Key Findings
- Vulnerability Metrics
- Timeline
- Affected Versions
- Recommendations
- Mitigations
- Root Cause Analysis
- MITRE ATT&CK Mapping
- Threat Activity
- Detection
- References
- Public PoCs
BLUF (Bottom Line Up Front)
UPDATE 29.12.2025: CISA added CVE-2025-14847 to its Known Exploited Vulnerabilities (KEV) catalogue.
CVE-2025-14847 lets an unauthenticated attacker read heap memory - credentials, tokens, private keys, adjacent connection data - from any MongoDB instance that has network compression enabled. It is a length-handling bug in the compressed wire-protocol path, structurally the same class of defect as Heartbleed: the server trusts an attacker-declared length and returns the difference from memory. Public PoCs exist and we observe scanning against port 27017. Patch to a fixed version; if you cannot patch, drop zlib from the negotiated compressors. Treat credentials on any internet-facing instance as exposed. MongoDB Atlas is not affected - MongoDB patched all Atlas deployments before public disclosure.
Executive Summary
MongoBleed (CVE-2025-14847) is a pre-authentication heap over-read in MongoDB’s decompression of OP_COMPRESSED wire-protocol messages. When compression is negotiated, the server sizes the decompressed message from an attacker-controlled length field but never verifies how many bytes the decompressor actually produced. The unwritten remainder of the buffer - uninitialized heap - is parsed as part of the message and can be reflected back to the attacker.
No authentication is required: compression sits below the command layer, so any peer that has completed the initial hello handshake can send compressed frames. Repeated requests let an attacker walk adjacent heap and reassemble secrets across many reads.
Risk: High for any internet-exposed MongoDB that offers zlib compression, which a default server configuration does - the attacker negotiates it as the connecting client, so legitimate driver settings are irrelevant. Shodan shows roughly 200,000 exposed instances. MongoDB Atlas is not affected - MongoDB patched all Atlas deployments before public disclosure.
Immediate action: Patch to a fixed version or drop zlib from the negotiated compressors; restrict internet exposure; enforce authentication and TLS; monitor for anomalous OP_COMPRESSED traffic on port 27017.
Key Findings
- Pre-auth exploitation: No credentials required; a peer that completed the
hellohandshake can send craftedOP_COMPRESSEDframes to TCP/27017. - Root cause: A length parameter inconsistency (CWE-130) - the message length is taken from the attacker’s declared
uncompressedSizerather than the number of bytes the decompressor actually wrote. - Wide version impact: Affects MongoDB 3.6 through 8.2 with network compression enabled.
- Public PoCs: 8+ proof-of-concept scripts are published on GitHub (as of 28.12.2025).
- Exposure: Shodan shows roughly 200,000 MongoDB instances reachable from the internet.
- No ransomware attribution yet: We have not linked any ransomware victim to this CVE. The disclosure itself yields no code execution - its value to an attacker is the recovered secrets.
Vulnerability Metrics
| Metric | Value |
|---|---|
| CVE | CVE-2025-14847 |
| CWE | CWE-130 - Improper Handling of Length Parameter Inconsistency |
| CVSS 4.0 | 8.7 (High) |
| CVSS 4.0 Vector | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N |
| CVSS 3.1 | 7.5 (High) |
| CVSS 3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| EPSS | 0.98 |
| CISA KEV | Listed (Dec 29, 2025) |
| Vendor Advisory | SERVER-115508 |
Timeline
| Date | Event |
|---|---|
| Dec 15, 2025 | MongoDB releases security advisory |
| Dec 19, 2025 | NVD publishes CVE-2025-14847 |
| Dec 23-27, 2025 | Release of multiple PoCs on GitHub |
Affected Versions
| Branch | Vulnerable | Fixed |
|---|---|---|
| 8.2 | < 8.2.3 | 8.2.3+ |
| 8.0 | < 8.0.17 | 8.0.17+ |
| 7.0 | < 7.0.28 | 7.0.28+ |
| 6.0 | < 6.0.27 | 6.0.27+ |
| 5.0 | < 5.0.32 | 5.0.32+ |
| 4.4 | < 4.4.30 | 4.4.30+ |
| 4.2 | All versions | No patch available |
| 4.0 | All versions | No patch available |
| 3.6 | All versions | No patch available |
MongoDB Atlas: Not affected. MongoDB patched all Atlas deployments before public disclosure. Only self-managed installations require action.
Recommendations
Assume in-memory secrets were leaked. Rotate database users, application secrets, and session tokens. Review egress logs for data staging to unfamiliar hosts.
- Inventory all MongoDB instances including development, staging, production, and test environments.
- Prioritise patching by internet exposure and data sensitivity.
- Drop zlib from the compressor list if you cannot patch immediately.
- Assume credential exposure on any unpatched, reachable instance.
- Rotate secrets - database users, application secrets, API tokens, session keys.
- Review network logs for unusual traffic to port 27017 from unfamiliar sources.
Mitigations
- Remove direct internet exposure.
- Enforce IP allowlists, VPN, or privileged network segments.
- Require SCRAM authentication and TLS for all connections.
Patching
Upgrade to a fixed version:
- MongoDB 8.2.3+
- MongoDB 8.0.17+
- MongoDB 7.0.28+
- MongoDB 6.0.27+
- MongoDB 5.0.32+
- MongoDB 4.4.30+
Temporary Workaround
Only the zlib path is vulnerable, so removing zlib from the negotiated compressors makes decompressData unreachable. Keep snappy and zstd if you rely on compression:
| |
The change requires service restart.
Root Cause Analysis
MongoBleed is a heap over-read: the server returns more bytes than the attacker’s message legitimately contains, and the surplus is whatever happened to be adjacent in the process heap.
The bug lives in how MongoDB reconstructs a compressed wire-protocol message, on a path that runs before authentication is enforced. It is the same shape of defect as Heartbleed (CVE-2014-0160) - a declared length the code trusts without confirming it against the data actually present.
The MongoDB wire protocol
Every message on TCP/27017 begins with a 16-byte standard header:
| |
To save bandwidth, client and server negotiate compression during the initial hello handshake. Once negotiated, either side may wrap any message in an OP_COMPRESSED envelope (opcode 2012):
| |
Two fields matter for the bug: uncompressedSize and compressorId. Both are fully attacker-controlled, and both are read before any authentication check - compression sits underneath the command layer, so an unauthenticated peer that has completed the hello handshake can already send OP_COMPRESSED frames.
The decompression path
When the server receives an OP_COMPRESSED frame with compressorId = 2 (zlib), it turns the compressed body back into a normal message. Conceptually:
- Read
uncompressedSizefrom the frame. - Allocate an
outputbuffer of that size. - Call the zlib compressor to decompress
compressedDatainto that buffer, and take back the number of bytes it produced. - Treat the buffer, for that produced length, as a normal uncompressed message and hand it to the command dispatcher.
The defect is in step 3’s bookkeeping: the zlib decompressor reports the size of the buffer it was handed, not the amount of data it actually wrote into it.
The bug: returning the buffer size instead of the decompressed size
The zlib path lives in ZlibMessageCompressor::decompressData (src/mongo/transport/message_compressor_zlib.cpp). Here is the vulnerable function; the patch differed by the single return line, shown below it:
| |
length is one variable doing two jobs. Going in, it holds the capacity of output - a buffer sized to the attacker’s uncompressedSize. zlib treats that argument (destLen) as in/out: on a Z_OK return it has overwritten length with the number of bytes it actually wrote. So after the call, length is the true decompressed size while output.length() is still the full allocated capacity. The two diverge exactly when the compressed body inflates to less than the declared size.
The patch is one line - which of those two the function returns:
| |
That single substitution is the whole vulnerability. A decompressor’s return value is how much data the caller believes the message contains. By returning output.length(), the function reports the full attacker-declared size even though zlib wrote only a fraction of the buffer.
Why heap leaks (CWE-130)
Take an attacker frame with:
uncompressedSize = 60000, sooutputis a 60000-byte buffer- a small but well-formed zlib body that inflates to ~40 bytes
::uncompress() writes 40 bytes into the front of the buffer, sets length = 40, and returns Z_OK. It does not touch the remaining ~59960 bytes - zlib only guards against overflowing the buffer, it never zeroes the tail. Those bytes are whatever the allocator last left there: fragments of other connections’ buffers, command arguments, authentication material, session data, pointers.
The vulnerable return {output.length()} then tells the caller the message is 60000 bytes long. The transport layer forwards all 60000 - 40 bytes of real content plus 59960 bytes of uninitialized heap - into the message pipeline, and the surplus is surfaced back to the attacker in the reply. Vary uncompressedSize across requests and an attacker walks adjacent heap, reassembling secrets over many reads. This is exactly Heartbleed’s primitive: ask for more than you sent, receive the difference from memory. Hence MongoBleed.
The fix (SERVER-115508)
The patch (commit 505b660) changes return {output.length()} to return {length} - the decompressor now reports the bytes zlib actually produced, so no uninitialized tail is ever forwarded. The snappy and zstd compressors already returned their true output length; only the zlib path carried the defect, which is why omitting zlib specifically is a valid workaround.
Why zlib is the exposure
The bug is reachable whenever the server will accept a zlib-compressed frame, and that is a lower bar than it sounds. A MongoDB server advertises all three algorithms - snappy, zstd, and zlib - in its default net.compression.compressors set, and in this attack the adversary is the client. Compression is negotiated per connection by whoever connects, so an unauthenticated peer can simply pick zlib during the hello handshake - even if none of your legitimate drivers use compression at all. Removing zlib from net.compression.compressors is therefore a complete workaround: the server stops offering it, ZlibMessageCompressor::decompressData is never called, and snappy and zstd keep working. MongoDB Atlas was patched ahead of disclosure, so managed Atlas clusters were never exposed.
MITRE ATT&CK Mapping
| Technique | ID | Description |
|---|---|---|
| Exploit Public-Facing Application | T1190 | Attackers exploit MongoDB instances exposed to the internet |
| Unsecured Credentials | T1552 | Memory disclosure leaks credentials from the server heap |
| Network Service Discovery | T1046 | Mass scanning for port 27017 |
Threat Activity
Exploitation Status
Public PoCs are available on GitHub, and our sensors record scanning against port 27017. The vulnerability is pre-authentication and requires a single crafted message, which lowers the barrier to opportunistic use.
Internet Exposure (Shodan)
| Metric | Value |
|---|---|
| Exposed MongoDB instances | 201,887 |
| Top countries | USA, Netherlands, China, Germany |
| Vulnerable versions observed | 8.0.15, 7.0.5, 5.0.28, 4.0.9 |
Baysec Honeypot Data (24h Snapshot)
In 24-hour window the honeypot logged 58 exploitation attempts on port 27017.
Ransomware
As of this writing we have not linked any ransomware victim to CVE-2025-14847. Memory disclosure on its own does not grant code execution; its value to an attacker is the credentials, keys, and tokens recovered from heap, which can enable follow-on access.
Detection
- Spikes in
OP_COMPRESSEDtraffic to port 27017 from unfamiliar IPs. - Large response payloads returned to small compressed requests - the signature of an over-read.
- Repeated compressed messages from one source with varying declared sizes.
- Scanning activity from new ASNs targeting 27017.
References
Official Advisory
- NVD - CVE-2025-14847 (MongoDB, Inc. CNA)
- MongoDB JIRA - SERVER-115508
- CIRCL - CVE-2025-14847
Background
- MongoDB Wire Protocol - OP_COMPRESSED
- zlib manual - uncompress() and the destLen out-parameter
- CVE-2014-0160 (Heartbleed) - the reference over-read
Public PoCs
Warning: Verify any PoC before running it. Threat actors distribute malware through fake exploit repositories, targeting researchers with lures for recent CVEs. In December 2025 we observed campaigns distributing Webrat RAT through weaponised PoC repos. Review code manually, run in isolated environments, and cross-reference with trusted sources. See our December 2025 Threats Summary.
- ProbiusOfficial/CVE-2025-14847 (Python)
- cybertechajju/CVE-2025-14847_Expolit (Python)
- onewinner/CVE-2025-14847 (Go)
- Black1hp/mongobleed-scanner (Python)
- saereya/CVE-2025-14847—MongoBleed (Go)
- nma-io/mongobleed (Go)
- Ashwesker/Blackash-CVE-2025-14847 (Python)
- KingHacker353/CVE-2025-14847_Expolit (Python)