Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
HIGH severity

Excelize Decrypt: unrecoverable panics on malformed OLE/CFB encrypted workbooksGHSA-2j4c-ffch-9f23

HIGHFix: qax-os/excelize#2395

GHSA-2j4c-ffch-9f23 is a high-severity (CVSS 7.5) CWE-248 vulnerability in github.com/xuri/excelize/v2. A fix is available for github.com/xuri/excelize/v2 — see the affected versions and patch details below.

Also known asCVE-2026-107214GO-2026-6684
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 23, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.

Exploitation and automatability from CISA’s SSVC triage for GHSA-2j4c-ffch-9f23.

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs15th percentile — riskier than 15% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

GHSA-2j4c-ffch-9f23 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 394,803 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹github.com/xuri/excelize/v2

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

Any file whose first 8 bytes are the OLE compound-file signature (D0 CF 11 E0 A1 B1 1A E1) is routed by OpenFile/OpenReader/OpenBytes → openReaderAt → Decrypt. The version dispatch only guarantees len(EncryptionInfo) >= 4 before handing attacker-controlled EncryptionInfo/EncryptedPackage buffers to standardDecrypt/agileDecrypt, and no callee validates structure. Malformed but version-valid content therefore fails as an unrecovered runtime panic instead of an error, terminating the calling process.

Details

All panic classes below were execution-confirmed against pristine master (ecd99d761fe0, 2026-09-08). excelize.go:211-213 maps Decrypt errors to ErrWorkbookFileFormat, but panics bypass that path and kill the process.

#Malformed inputPanicSite
1standard, len(EncryptionInfo) 4–11slice bounds [:12]crypt.go:238
2standard, attacker-controlled headerSize uint32slice bounds [12:12+headerSize] / fixed-offset header readscrypt.go:238-249
3standard, verifier remainder < 72 (AES) / 60 (RC4) bytesslice bounds in standardEncryptionVerifiercrypt.go:282-295
4standard, header.KeySize = 0xFFFFFFFFslice bounds [:536870911] with capacity 48crypt.go:321
5standard, EncryptedPackage stream missing/shortslice bounds [8:0]crypt.go:268
6agile, len(EncryptionInfo) 4–7slice bounds [8:4]crypt.go:407
7agile, valid XML without <keyEncryptors>index out of range [0] with length 0crypt.go:416, 433
8agile, saltValue decoded length ≠ AES blockcipher.NewCBCDecrypter: IV length must equal block sizecrypt.go:425 → 512
9any, keyData blockSize="0"integer divide by zerocrypt.go:539

Note the asymmetry pinpointing the missing constraint: the agile path already checks len(EncryptedPackage) >= 8 (crypt.go:520-523) but the standard path does not (#5). Existing tests only cover the error paths (short <4 bytes → ErrUnknownEncryptMechanism, bad XML, base64 errors), never these panic paths.

PoC

Standalone programs (public API only, inputs built in memory) were provided to the maintainer by email: 1-decrypt-panic builds seven malformed CFB containers and shows each panic escaping the public Decrypt API plus one end-to-end OpenReader crash. All cases print PANIC on master and BLOCKED with the proposed patch. A regression guard proves legitimate decryption is unaffected: a workbook encrypted with the package's own Encrypt() still opens through the same code path.

(A separate advisory covers the unbounded/negative allocation in extractPart.)

Impact

Any service that calls OpenFile/OpenReader/OpenBytes on untrusted input (upload processing, mail scanning, spreadsheet conversion) can be killed remotely and without authentication by a file of ~100 bytes to ~3 KB. No password is required — panics occur during structural/parameter handling before successful decryption. Site variety means filtering one pattern does not help.

Proposed fix

A recover() boundary in Decrypt mapping any panic to ErrWorkbookFileFormat (restores the documented error-routing contract; legitimate standard/agile decryption unaffected). A complete patch has been provided to the maintainer; per-site length validation is recommended as defense in depth.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/xuri/excelize/v2≥ 2.3.1&&< 2.11.1-0.20260915055537-22f76f9acb942.11.1-0.20260915055537-22f76f9acb94go get github.com/xuri/excelize/v2@v2.11.1-0.20260915055537-22f76f9acb94

Affected Products

1 product · 1 configurations
Application
excelizeexcelize
≥ 2.3.1 && ≤ 2.11.0
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/xuri/excelize/v2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/xuri/excelize/v2 to 2.11.1-0.20260915055537-22f76f9acb94 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-2j4c-ffch-9f23 is resolved across your whole dependency graph.

  3. Workarounds

    Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.

Frequently Asked Questions

### Summary Any file whose first 8 bytes are the OLE compound-file signature (`D0 CF 11 E0 A1 B1 1A E1`) is routed by `OpenFile`/`OpenReader`/`OpenBytes` → `openReaderAt` → `Decrypt`. The version dispatch only guarantees `len(EncryptionInfo) >= 4` before handing attacker-controlled `EncryptionInfo`/`EncryptedPackage` buffers to `standardDecrypt`/`agileDecrypt`, and no callee validates structure. Malformed but version-valid content therefore fails as an **unrecovered runtime panic** instead of an error, terminating the calling process. ### Details All panic classes below were execution-confi
O3 Security · Impact-Aware SCA

Is GHSA-2j4c-ffch-9f23 in your dependencies?

Find it across Go, including transitive dependencies.

Excelize Decrypt: unrecoverable panics on malformed…