Vulnerability GHSA-2j4c-ffch-9f23

High Risk
HIGH RISK
CVSS Score: 7.5
Score Range: 7.0–8.9
High severity vulnerabilities (CVSS 7.0–8.9). Serious vulnerabilities that should be prioritized soon after critical fixes.
6 hours ago
October 08, 2026 at 04:31 PM UTC
Excelize Decrypt: unrecoverable panics on malformed OLE/CFB encrypted workbooks
v2.3.1 - v2.11.0
v2.3.1 - v2.11.0

Summary

Excelize Decrypt: unrecoverable panics on malformed OLE/CFB encrypted workbooks

Details

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 input Panic Site
1 standard, len(EncryptionInfo) 4–11 slice bounds [:12] crypt.go:238
2 standard, attacker-controlled headerSize uint32 slice bounds [12:12+headerSize] / fixed-offset header reads crypt.go:238-249
3 standard, verifier remainder < 72 (AES) / 60 (RC4) bytes slice bounds in standardEncryptionVerifier crypt.go:282-295
4 standard, header.KeySize = 0xFFFFFFFF slice bounds [:536870911] with capacity 48 crypt.go:321
5 standard, EncryptedPackage stream missing/short slice bounds [8:0] crypt.go:268
6 agile, len(EncryptionInfo) 4–7 slice bounds [8:4] crypt.go:407
7 agile, valid XML without <keyEncryptors> index out of range [0] with length 0 crypt.go:416, 433
8 agile, saltValue decoded length ≠ AES block cipher.NewCBCDecrypter: IV length must equal block size crypt.go:425 → 512
9 any, keyData blockSize="0" integer divide by zero crypt.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.

Timeline

Published
6 hours ago
October 08, 2026 at 04:31 PM UTC
Last Modified
6 hours ago
October 08, 2026 at 04:45 PM UTC