MongoBleed (CVE-2025-14847): Critical Unauthenticated MongoDB Memory Disclosure
A critical vulnerability identified as CVE-2025-14847 (dubbed “MongoBleed“) affects MongoDB Server instances, exposing systems to unauthenticated information disclosure. This vulnerability allows a remote attacker to read sensitive data from the server’s memory without requiring authentication.
Severity: Critical | CVSS Score: 8.7 (v4.0) / 7.5 (v3.1)
Vulnerability Details
The vulnerability originates in message_compressor_zlib.cpp, where a logic error causes the decompression routine to return the allocated buffer size rather than the actual decompressed payload length. This discrepancy results in uninitialized heap memory being included in server responses.
Attack vectors include:
- Attacker transmits specially crafted compressed network packets to the target MongoDB server
- The decompression handler processes these malformed packets, triggering the length calculation error
- Uninitialized heap memory is exposed and returned to the attacker in the response
- Sensitive data residing in memory (credentials, database content, cryptographic material) may be disclosed
Resulting impacts:
- Exposure of sensitive data (PII, credentials, API keys)
- Bypass of authentication mechanisms via stolen tokens
- Reconnaissance for further attacks
Versions Affected
The following versions of MongoDB Server are vulnerable if network compression (zlib) is enabled:
| Product | Version | Affected Versions | Fixed In |
MongoDB Server | 8.2.x | 8.2.0 – 8.2.2 | 8.2.3 |
| 8.0.x | 8.0.0 – 8.0.16 | 8.0.17 | |
| 7.0.x | 7.0.0 – 7.0.26 | 7.0.28 | |
| 6.0.x | 6.0.0 – 6.0.26 | 6.0.27 | |
| 5.0.x | 5.0.0 – 5.0.31 | 5.0.32 | |
| 4.4.x | 4.4.0 – 4.4.29 | 4.4.30 |
Impact
Exploitation of this vulnerability could result in:
- Data Exfiltration: Attackers can continuously scrape server memory for sensitive business data.
- Credential Theft: Plaintext passwords or active session tokens residing in memory can be captured.
- Security Bypass: Information gathered can be used to bypass ASLR or other binary protections.
Recommendations for Mitigation
Immediate Actions
Upgrade: Update to one of the patched versions immediately:
- 8.2.3
- 8.0.17
- 7.0.28
- 6.0.27
- 5.0.32
- 4.4.30
Security Hardening
- Restrict Access: Ensure MongoDB ports (e.g., 27017) are NOT accessible from the public internet.
- Apply Network Policies: Use firewalls or Security Groups to whitelist traffic only from trusted application servers.
- Disable Legacy Opcodes: If possible, configure newer drivers to avoid using deprecated opcodes like OP_QUERY.
Long-term Measures
- Maintain updated software and subscribe to vendor security alerts.
- Implement strict network segmentation for database layers.
- Monitor database logs for anomalous query patterns.
Snowbit Response
- Scanned all SRC customer environments where MongoDB-related logs were available to identify vulnerable deployments, exposed services, suspicious access attempts, and potential exploitation activity aligned with MongoBleed.
- Based on the CVE-specific exploitation techniques and expected indicators, we have created custom hunting queries to detect which we have attached below
- We strongly recommend enabling MongoDB Audit Logs to improve detection capability and investigation depth.
References
- https://jira.mongodb.org/browse/SERVER-115508
- https://github.com/joe-desimone/mongobleed
Dataprime Hunting Queries
Detect Rapid, Pre-Authentication Connection Bursts from all IP
source logs
| filter in($d.logRecord.attributes.message,
'Connection not authenticating','Checking authorization failed')
|| in($d.cx_security.event_name,
'Connection not authenticating','Checking authorization failed')
| create $d.raw_remote from firstNonNull(
$d.logRecord.attributes.client,
$d.logRecord.attributes.remote,
$d.attr.remote
)
| filter $d.raw_remote != null
| create $d.client_ip from $d.raw_remote.splitParts(':', 1)
| groupby $d.client_ip aggregate count() as $d.event_count
| orderby $d.event_count:number desc
Detect Rapid, Pre-Authentication Connection Bursts from Public IP
source logs
| filter in($d.logRecord.attributes.message,
'Connection not authenticating','Checking authorization failed')
|| in($d.cx_security.event_name,
'Connection not authenticating','Checking authorization failed')
// pull remote endpoint and extract the IP (strip :port if present)
| create $d.raw_remote from firstNonNull(
$d.logRecord.attributes.client,
$d.logRecord.attributes.remote,
$d.attr.remote
)
| filter $d.raw_remote != null
| create $d.client_ip from $d.raw_remote.splitParts(':', 1)
// second octet as string for 172.16–31 check (no regex needed)
| create $d.o2 from $d.client_ip.splitParts('.', 2)
// keep only public IPv4 (drop localhost, RFC1918, link-local)
| filter $d.client_ip != null
&& $d.client_ip != '127.0.0.1' // localhost
&& !startsWith($d.client_ip, '10.') // RFC1918
&& !startsWith($d.client_ip, '192.168.') // RFC1918
&& !(startsWith($d.client_ip, '172.') // RFC1918 (16–31)
&& in($d.o2, '16','17','18','19','20','21','22','23','24',
'25','26','27','28','29','30','31'))
&& !startsWith($d.client_ip, '169.254.') // link-local
| groupby $d.client_ip aggregate count() as $d.event_count
| orderby $d.event_count:number desc
Sudden Spike in “Connection Closed” Event
source logs
| create $d.msg from firstNonNull($d.logRecord.attributes.message, '')
| create $d.ev from firstNonNull($d.cx_security.event_name, '')
| filter $d.msg == 'Connection ended' || $d.ev == 'Connection ended'
| create $d.raw_remote from firstNonNull(
$d.cx_security.source_ip,
$d.logRecord.attributes.client_ip,
$d.logRecord.attributes.client,
$d.logRecord.attributes.remote,
$d.attr.remote
)
| create $d.source_ip from if(contains($d.raw_remote, ':'), $d.raw_remote.splitParts(':', 1), $d.raw_remote)
/* exclude localhost and RFC1918 (10/8, 192.168/16, 172.16–31/12) */
| filter $d.source_ip != null
&& $d.source_ip != '127.0.0.1'
&& !startsWith($d.source_ip, '10.')
&& !startsWith($d.source_ip, '192.168.')
&& !(startsWith($d.source_ip, '172.')
&& in($d.source_ip.splitParts('.', 2),
'16','17','18','19','20','21','22','23','24',
'25','26','27','28','29','30','31'))
/* pull ISO timestamp safely via bracket access */
| create $d.tdate from $d['t']['$date']
| filter $d.tdate != null
/* minute grain without date funcs */
| create $d.hour_part from $d.tdate.splitParts(':', 1) // e.g., 2025-12-29T07
| create $d.minute_part from $d.tdate.splitParts(':', 2) // e.g., 40
| create $d.minute_key from concat($d.hour_part, ':', $d.minute_part)
| groupby $d.source_ip, $d.minute_key aggregate count() as $d.closes
| orderby $d.minute_key desc, $d.closes:number desc