GitHub Advisoryadvisory · Go
GHSA-2j4c-ffch-9f23 Excelize Decrypt: unrecoverable panics on malformed OLE/CFB encrypted workbooks
HIGH
Official page ↗
- Aliases
- CVE-2026-107214
- Ecosystem
- Go
- Packages
- github.com/xuri/excelize/v2
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-248
- Reviewed
- 2026-10-08
Affected packages
• Go github.com/xuri/excelize/v2: >= 2.3.1, < 2.11.1-0.20260915055537-22f76f9acb94 → fixed in 2.11.1-0.20260915055537-22f76f9acb94
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 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 ` | 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.
Related
Alias · CVE (1)
- CVE-2026-107214 Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. vulnerability
Weakness · CWE (1)
- CWE-248 Uncaught Exception weakness
References
- https://github.com/qax-os/excelize/security/advisories/GHSA-2j4c-ffch-9f23 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-107214 advisory
- https://github.com/qax-os/excelize/pull/2395 web
- https://github.com/qax-os/excelize/commit/22f76f9acb94b85b3eb9c4365ab4f750cebbc565 web
- https://github.com/qax-os/excelize package