Skip to content

Euro-Office - Heap Buffer Out-of-Bounds Read in DOC Binary Format Parser (CHPX) #17

Description

@CVE-Hunter-Leo

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.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

  1. Attacker crafts a malicious .doc file
  2. 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)
  3. User opens the malicious .doc → FormattedDiskPageCHPX parses the FKP → heap OOB read
  4. 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

  1. Validate wordOffset * 2 + 1 + cb <= 512 before the memcpy
  2. Clamp cb to 512 - wordOffset * 2 - 1
  3. Validate that bytes[wordOffset * 2] index is within bounds before dereferencing
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions