ANT-2026-CRFDC4JM · libreoffice/core

heap-buffer-overflow medium

CVE-2026-63272

Severity Claude high · Security research firm high · Maintainer medium

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.

ANT-2026-CRFDC4JM: WMF Unicode-escape text DX-array heap overflow

In the WMF W_META_ESCAPE PRIVATE_ESCAPE_UNICODE path (wmfreader.cxx:1271-1311), nStringLen and nDXCount are read independently from the file with no check that nDXCount >= nStringLen. The KernArray (std::vector) is resized to nDXCount and passed to MtfTools::DrawText, which loops i=0..rText.getLength()-1 and does unchecked (*pDXArry)[i] reads and writes. An attacker sets nStringLen large (e.g. 8000) and nDXCount=1, causing ~64 KB (up to ~256 KB) of doubles to be written past an 8-byte heap allocation. WMF is parsed whenever opened standalone or embedded in ODT/DOCX/RTF/PPTX, so a malicious document triggers heap corruption and likely RCE on open.

Target

Project: libreoffice/core
Location: emfio/source/reader/mtftools.cxx:1717
Discovery: static analysis — not yet dynamically reproduced

Technical Details

Root cause is a missing invariant check: the reader never enforces nDXCount >= nStringLen (the writer side at wmfwr.cxx:516 shows the intended invariant). DrawText then uses unchecked std::vector::operator[] in a loop bounded by the string length rather than the array size, yielding both OOB read (mtftools.cxx:1715) and OOB write (mtftools.cxx:1720). _GLIBCXX_ASSERTIONS is off in default TDF builds, so operator[] performs no bounds check.

Reproduction

  1. Craft a WMF with W_META_CREATEFONTINDIRECT for a ubiquitous font (e.g. Arial/Liberation Sans) followed by SELECTOBJECT to pass the IsFontAvailable gate.
  2. Add a W_META_ESCAPE record carrying the LibreOffice PRIVATE_ESCAPE_UNICODE signature (magic 0x2c2a4f4f / 0x0a) and a correct CRC32 over the payload.
  3. In the escape payload, set nStringLen large (e.g. 8000) with a matching string, and nDXCount=1 with a single DX entry.
  4. Embed the WMF in an ODT/DOCX/RTF/PPTX (or deliver standalone) and send to the victim.
  5. On open, DrawText loops over 8000 chars and writes ~7999 doubles past the 1-element KernArray heap allocation.

[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]

Suggested Fix

Before per-character indexing, ensure the DX-advance array has at least rText.getLength() elements (reject the record or resize/pad the array) so pDXArry is never indexed beyond its size.

Acknowledgement

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-CRFDC4JM.


Reference: ANT-2026-CRFDC4JM
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Ada Logics.

Verdict
true positive
Severity
high
UPSTREAM FIX

The change that resolved this finding.

diff --git a/emfio/source/reader/wmfreader.cxx b/emfio/source/reader/wmfreader.cxx
index a645411267b53..390f7271acf38 100644
--- a/emfio/source/reader/wmfreader.cxx
+++ b/emfio/source/reader/wmfreader.cxx
@@ -1349,7 +1349,7 @@ namespace emfio
                                                     GetFont().GetFamilyName()))
                                             {
                                                 Point aPt;
-                                                sal_uInt32 nStringLen, nDXCount;
+                                                sal_uInt32 nStringLen(0), nDXCount(0);
                                                 KernArray aDXAry;
                                                 SvMemoryStream aMemoryStream(nEscLen);
                                                 aMemoryStream.WriteBytes(pData.get(), nEscLen);
@@ -1368,6 +1368,8 @@ namespace emfio
                                                     OUString aString = read_uInt16s_ToOUString(
                                                         aMemoryStream, nStringLen);
                                                     aMemoryStream.ReadUInt32(nDXCount);
+                                                    if (nDXCount < o3tl::make_unsigned(aString.getLength()))
+                                                        nDXCount = 0;
                                                     if ((static_cast<sal_uInt64>(nDXCount)
                                                         * sizeof(sal_Int32))
                                                         >= (nEscLen - aMemoryStream.Tell()))
@@ -1376,7 +1378,7 @@ namespace emfio
                                                         aDXAry.resize(nDXCount);
                                                     for (sal_uInt32 i = 0; i < nDXCount; i++)
                                                     {
-                                                        sal_Int32 val;
+                                                        sal_Int32 val(0);
                                                         aMemoryStream.ReadInt32(val);
                                                         aDXAry[i] = val;
                                                     }

https://github.com/LibreOffice/core/commit/4406da79b0

TIMELINE

Recorded dates, in order.

  1. 2026-04-02 Discovered or logged
  2. 2026-07-03 Sent to maintainer
  3. 2026-07-06 Maintainer acknowledged
  4. 2026-07-24 Patch released
  5. 2026-09-28 Publicly revealed
PROVENANCE

SHA-3-512 hash:

0cc2bd420b5659200db58805519d367e0f76e3baa0e39bdb85cfbe5ae2687a623a7c42edd42b0b875e954d9a14f8dd9a8fa8d63e23c38f0c84e44f843f7e823a

Committed 2026-07-22 07:32 UTC

Revealed 2026-09-28 21:05 UTC

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-CRFDC4JM",
  "bug_class": "Heap buffer overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-04-16T01:54:40+00:00",
  "description": "In the WMF W_META_ESCAPE PRIVATE_ESCAPE_UNICODE path (wmfreader.cxx:1271-1311), nStringLen and nDXCount are read independently from the file with no check that nDXCount >= nStringLen. The KernArray (std::vector<double>) is resized to nDXCount and passed to MtfTools::DrawText, which loops i=0..rText.getLength()-1 and does unchecked (*pDXArry)[i] reads and writes. An attacker sets nStringLen large (e.g. 8000) and nDXCount=1, causing ~64 KB (up to ~256 KB) of doubles to be written past an 8-byte heap allocation. WMF is parsed whenever opened standalone or embedded in ODT/DOCX/RTF/PPTX, so a malicious document triggers heap corruption and likely RCE on open.",
  "discovered_at": "2026-04-02T00:00:00+00:00",
  "location": "emfio/source/reader/mtftools.cxx:1717",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "LibreOffice/core",
  "reproduction": [
    "1. Craft a WMF with W_META_CREATEFONTINDIRECT for a ubiquitous font (e.g. Arial/Liberation Sans) followed by SELECTOBJECT to pass the IsFontAvailable gate.",
    "2. Add a W_META_ESCAPE record carrying the LibreOffice PRIVATE_ESCAPE_UNICODE signature (magic 0x2c2a4f4f / 0x0a) and a correct CRC32 over the payload.",
    "3. In the escape payload, set nStringLen large (e.g. 8000) with a matching string, and nDXCount=1 with a single DX entry.",
    "4. Embed the WMF in an ODT/DOCX/RTF/PPTX (or deliver standalone) and send to the victim.",
    "5. On open, DrawText loops over 8000 chars and writes ~7999 doubles past the 1-element KernArray heap allocation."
  ],
  "technical_details": "Root cause is a missing invariant check: the reader never enforces nDXCount >= nStringLen (the writer side at wmfwr.cxx:516 shows the intended invariant). DrawText then uses unchecked std::vector<double>::operator[] in a loop bounded by the string length rather than the array size, yielding both OOB read (mtftools.cxx:1715) and OOB write (mtftools.cxx:1720). _GLIBCXX_ASSERTIONS is off in default TDF builds, so operator[] performs no bounds check.",
  "title": "WMF Unicode-escape text DX-array heap overflow",
  "vendor_severity": "high"
}