Heap Buffer Out-of-Bounds Read in DOC Binary Format Parser (CHPX)
Severity: HIGH
CVSS Estimate: 7.8 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
Description
Euro-Office core is vulnerable to a heap out-of-bounds read when parsing legacy Microsoft Word binary documents (.doc).
In MsBinaryFile/DocFile/FormattedDiskPageCHPX.cpp, the FormattedDiskPageCHPX constructor reads a 512-byte Formatted Disk Page (FKP) structure from the document into a heap-allocated buffer. The code uses an attacker-controlled wordOffset value (read from the file) to calculate source offsets and sizes for copying Character Property Exceptions (CHPX) data via memcpy. Specifically, cb (also attacker-controlled) is read from bytes[wordOffset * 2], and memcpy is performed from bytes + (wordOffset * 2) + 1 for cb bytes without validating that the read stays within the 512-byte buffer boundary.
When wordOffset is set to a high value (e.g., 255) and cb is sufficiently large, the read extends beyond the allocated buffer, resulting in a heap out-of-bounds read.
The same vulnerability pattern exists in FormattedDiskPagePAPX.cpp for Paragraph Property Exceptions (PAPX) parsing.
An attacker can exploit this issue by supplying a specially crafted malicious .doc file. Successful exploitation may lead to information disclosure (heap memory leakage) or a denial of service (crash). Under certain conditions, it may contribute to further memory corruption.
Component
MsBinaryFile/DocFile/FormattedDiskPageCHPX.cpp
Vulnerable Location
Function: FormattedDiskPageCHPX::FormattedDiskPageCHPX() at lines 48-114
Root Cause
The wordOffset field is read from a single byte in the file (line 87: unsigned char wordOffset = bytes[j]), giving it a range of 0-255. This offset, when multiplied by 2, is used as an index into a 512-byte buffer bytes without proper bounds checking. The subsequent cb value (also a single byte from the file) controls the allocation size AND the memcpy read size, but neither wordOffset*2 + 1 + cb is checked against the 512-byte buffer boundary.
Vulnerable Code (Lines 87-99)
// line 87: wordOffset read from file, value 0-255
unsigned char wordOffset = bytes[j];
rgb[i] = wordOffset;
j++;
if (wordOffset != 0)
{
// line 95: cb read at position wordOffset*2 (max 510, within bounds)
unsigned char cb = bytes[wordOffset * 2];
// line 98: allocation uses cb (max 255)
chpx = new unsigned char[cb];
// line 99: OUT-OF-BOUNDS READ: reads cb bytes starting at bytes + wordOffset*2 + 1
// If wordOffset*2 + 1 + cb > 512, this reads beyond the heap buffer
memcpy(chpx, (bytes + (wordOffset * 2) + 1), cb);
grpchpx[i] = new CharacterPropertyExceptions(chpx, cb, nWordVersion);
RELEASEARRAYOBJECTS(chpx);
}
Arithmetic Analysis
- The
bytes buffer is exactly 512 bytes (line 56: new unsigned char[512])
wordOffset is a uint8 (0-255), so wordOffset * 2 ranges from 0 to 510
cb is also a uint8 (0-255)
- The read offset is
wordOffset * 2 + 1
- The read range is
[wordOffset * 2 + 1, wordOffset * 2 + 1 + cb)
- Worst case:
wordOffset = 255 → offset = 511; cb = 255 → reads bytes[511..765]
- This reads 254 bytes beyond the 512-byte heap buffer
Exploit Path
- Attacker crafts a malicious .doc file
- The FKP (Formatted Disk Page) at a page boundary is structured with:
bytes[511] = proper crun count (e.g., 10)
- The character property offset table points to entries at high offsets
bytes[wordOffset*2] = cb set to a large value (e.g., 255)
- User opens the malicious .doc →
FormattedDiskPageCHPX parses the FKP → heap OOB read
- The OOB data is used as CHPX (Character Property Exceptions) data, which can influence memory operations downstream
Impact
- Information Disclosure: Heap memory contents leaked into CHPX data structures
- Potential RCE: If the leaked data is used to construct objects or indices, it could lead to type confusion or control flow hijacking
- Crash/DoS: Reading unmapped memory pages beyond the heap
Same Vulnerability Class in FormattedDiskPagePAPX.cpp (Line 124-136)
The identical bug pattern exists in the PAPX (Paragraph Property Exceptions) parser:
// FormattedDiskPagePAPX.cpp:124-136
unsigned char cw = bytes[bx.wordOffset * 2]; // cw is uint8
// ...
int sz = cw * 2; // sz = up to 510
unsigned char* papx = new unsigned char[sz];
memcpy(papx, (bytes + (bx.wordOffset * 2) + padbyte + 1), sz);
// worst case: wordOffset=255, padbyte=1 → offset=512, sz=510 → reads [512..1021]
This reads up to 510 bytes beyond the 512-byte heap buffer, which is a more severe out-of-bounds read.
Remediation
- Validate
wordOffset * 2 + 1 + cb <= 512 before the memcpy
- Clamp
cb to 512 - wordOffset * 2 - 1
- Validate that
bytes[wordOffset * 2] index is within bounds before dereferencing
- Consider using
std::vector<unsigned char> with .at() for automatic bounds checking
References
- CWE-125: Out-of-bounds Read
- CWE-787: Out-of-bounds Write
Heap Buffer Out-of-Bounds Read in DOC Binary Format Parser (CHPX)
Affected product: https://github.com/Euro-Office/core
Severity: HIGH
CVSS Estimate: 7.8 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
Description
Euro-Office core is vulnerable to a heap out-of-bounds read when parsing legacy Microsoft Word binary documents (.doc).
In MsBinaryFile/DocFile/FormattedDiskPageCHPX.cpp, the FormattedDiskPageCHPX constructor reads a 512-byte Formatted Disk Page (FKP) structure from the document into a heap-allocated buffer. The code uses an attacker-controlled wordOffset value (read from the file) to calculate source offsets and sizes for copying Character Property Exceptions (CHPX) data via memcpy. Specifically, cb (also attacker-controlled) is read from bytes[wordOffset * 2], and memcpy is performed from bytes + (wordOffset * 2) + 1 for cb bytes without validating that the read stays within the 512-byte buffer boundary.
When wordOffset is set to a high value (e.g., 255) and cb is sufficiently large, the read extends beyond the allocated buffer, resulting in a heap out-of-bounds read.
The same vulnerability pattern exists in FormattedDiskPagePAPX.cpp for Paragraph Property Exceptions (PAPX) parsing.
An attacker can exploit this issue by supplying a specially crafted malicious .doc file. Successful exploitation may lead to information disclosure (heap memory leakage) or a denial of service (crash). Under certain conditions, it may contribute to further memory corruption.
Component
MsBinaryFile/DocFile/FormattedDiskPageCHPX.cppVulnerable Location
Function:
FormattedDiskPageCHPX::FormattedDiskPageCHPX()at lines 48-114Root Cause
The
wordOffsetfield is read from a single byte in the file (line 87:unsigned char wordOffset = bytes[j]), giving it a range of 0-255. This offset, when multiplied by 2, is used as an index into a 512-byte bufferbyteswithout proper bounds checking. The subsequentcbvalue (also a single byte from the file) controls the allocation size AND the memcpy read size, but neitherwordOffset*2 + 1 + cbis checked against the 512-byte buffer boundary.Vulnerable Code (Lines 87-99)
Arithmetic Analysis
bytesbuffer is exactly 512 bytes (line 56:new unsigned char[512])wordOffsetis a uint8 (0-255), sowordOffset * 2ranges from 0 to 510cbis also a uint8 (0-255)wordOffset * 2 + 1[wordOffset * 2 + 1, wordOffset * 2 + 1 + cb)wordOffset = 255→ offset =511;cb = 255→ reads bytes[511..765]Exploit Path
bytes[511]= propercruncount (e.g., 10)bytes[wordOffset*2]=cbset to a large value (e.g., 255)FormattedDiskPageCHPXparses the FKP → heap OOB readImpact
Same Vulnerability Class in FormattedDiskPagePAPX.cpp (Line 124-136)
The identical bug pattern exists in the PAPX (Paragraph Property Exceptions) parser:
This reads up to 510 bytes beyond the 512-byte heap buffer, which is a more severe out-of-bounds read.
Remediation
wordOffset * 2 + 1 + cb <= 512before the memcpycbto512 - wordOffset * 2 - 1bytes[wordOffset * 2]index is within bounds before dereferencingstd::vector<unsigned char>with.at()for automatic bounds checkingReferences