GHSA-R7X5-4969-99P7

Vulnerability from github – Published: 2026-10-08 16:50 – Updated: 2026-10-08 16:50
VLAI
Summary
Excelize: Nil-pointer dereference in GetSlicers when a worksheet has extLst present but no drawing element
Details

Summary

GetSlicers guards only if ws.ExtLst == nil { return } (slicer.go:816-818) and then immediately dereferences ws.Drawing.RID with no nil check on ws.Drawing itself. Drawing and ExtLst are two independently optional child elements of in xl/worksheets/sheetN.xml, populated separately by encoding/xml depending on whether each tag is present. I executed a PoC: took a plain NewFile() workbook (no drawing, no slicer), saved it, then injected a bare <extLst></extLst> immediately before </worksheet> in xl/worksheets/sheet1.xml (no other change, no drawing element present) and called GetSlicers. Result: runtime error: invalid memory address or nil pointer dereference, confirmed at the ws.Drawing.RID line via stack trace.

Reachability / who can trigger this

Unauthenticated: any application that opens an untrusted .xlsx and calls the public File.GetSlicers(sheet) API. The trigger is a single empty XML tag with no valid slicer or drawing content required at all, making it an even smaller/simpler payload than candidate 1.

Proof of Concept / Reproduction

Method: 1) Reused the existing clone at C:\Users\vatsa\AppData\Local\Temp\claude\D--\59901fa2-b549-40e1-a433-30c8a3d01718\scratchpad\CVE-Hunt-10k\repos\qax-os__excelize (origin=https://github.com/qax-os/excelize.git, branch master up to date, commit e81f05008b33232151a58f06474fad5f70c4d060 dated 2026-09-27, git describe=v2.11.0-42-ge81f050, i.e. 42 commits past the v2.11.0 tag). 2) Manually re-read the cited source myself (not trusting the claim): slicer.go:805-820 (GetSlicers) and xmlWorksheet.go:21-72 (xlsxWorksheet struct + xlsxDrawing struct) -- confirmed verbatim, matches claim exactly. 3) Confirmed local toolchain: go version go1.27.0 windows/amd64 (/c/Program Files/Go/bin/go). 4) Built the whole package with go build ./... from the repo root -- succeeded with zero errors, proving the current checkout compiles cleanly. 5) Ran the pre-existing PoC test file already present in this checkout (slicer_extlst_poc_test.go, TestGetSlicersNilDrawingPanic) via go test -run TestGetSlicersNilDrawingPanic -v .: it builds a benign excelize.NewFile() workbook, saves it, reopens it, loads the raw zip entry xl/worksheets/sheet1.xml, string-replaces </worksheet> with <extLst></extLst></worksheet> (adds nothing else, no anywhere), rebuilds the zip byte-for-byte otherwise unchanged (rezipReplacing helper, plain archive/zip re-encode), reopens the crafted file with excelize.OpenReader, and calls the public GetSlicers("Sheet1") API inside a recover(). 6) For a fully independent, non-test-harness reproduction, I additionally wrote and built my own separate standalone Go module+program from scratch (excelize-nilptr-repro/{go.mod,main.go} in my scratchpad dir) that imports the real, unmodified excelize package as an external library dependency via a replace github.com/xuri/excelize/v2 => ../CVE-Hunt-10k/repos/qax-os__excelize directive (no copy-pasted/reimplemented function -- the actual library code, used through its real public API from a separate consumer program, simulating "any application that opens an untrusted .xlsx"). This program performs the identical craft-and-open sequence but calls GetSlicers with NO recover() at all. Built with go build -o repro.exe . (succeeded) and ran ./repro.exe directly, observing an unhandled process crash.

Evidence: SOURCE VERIFICATION (current HEAD, read directly): slicer.go:816-820 is exactly as claimed -- if ws.ExtLst == nil { return slicers, err } followed immediately by target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID) with no nil check on ws.Drawing. xmlWorksheet.go:54 Drawing *xlsxDrawingxml:"drawing"and xmlWorksheet.go:64 `ExtLst *xlsxExtLst `xml:"extLst" are both independently-optional pointer fields on xlsxWorksheet, populated only when their respective XML tag is present in the worksheet part -- confirming Drawing and ExtLst are unrelated/independent. getSheetRelationshipsTargetByID(sheet, rID string) string (sheet.go:721) takes a plain string, so ws.Drawing.RID is evaluated (dereferencing the nil pointer) at the call site itself.

RUN 1 -- existing repo test, go test -run TestGetSlicersNilDrawingPanic -v .: === RUN TestGetSlicersNilDrawingPanic slicer_extlst_poc_test.go:38: original sheet1.xml: ...... (no drawing, no extLst) slicer_extlst_poc_test.go:48: tampered sheet1.xml: ... slicer_extlst_poc_test.go:59: CONFIRMED: GetSlicers panicked on nil ws.Drawing dereference: runtime error: invalid memory address or nil pointer dereference --- PASS: TestGetSlicersNilDrawingPanic (0.01s) PASS ok github.com/xuri/excelize/v2 0.100s

RUN 2 -- my own independent standalone consumer program (real unmodified library via go.mod replace, NO recover installed), ./repro.exe: [] Saved benign workbook: 6052 bytes [] Original sheet1.xml: ...... [] Tampered sheet1.xml (attacker payload -- bare empty , still no ): ... [] Crafted malicious .xlsx: 6059 bytes [*] Crafted workbook accepted by OpenReader. Calling the public GetSlicers("Sheet1") API now, no recover() installed... panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x20 pc=0x7ff7fc8456b8]

goroutine 1 [running]: github.com/xuri/excelize/v2.(*File).GetSlicers(0x27fa8874908, {0x7ff7fc8a5e8a, 0x6}) C:/.../qax-os__excelize/slicer.go:820 +0x98 main.main() C:/.../excelize-nilptr-repro/main.go:122 +0x67f EXIT CODE: 2

The stack trace pinpoints slicer.go:820 exactly (the ws.Drawing.RID dereference), from an ordinary external caller with no recover(), on a workbook whose only "malicious" content is one empty <extLst></extLst> tag and zero drawing/slicer content -- confirming the claim end-to-end: nil-pointer dereference, unrecovered panic, DoS, matching CWE-476.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/xuri/excelize/v2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.9.0"
            },
            {
              "fixed": "2.11.1-0.20260929015830-8ffeb07ec9a3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-107213"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-476"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-08T16:50:08Z",
    "nvd_published_at": "2026-10-07T18:17:18Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nGetSlicers guards only `if ws.ExtLst == nil { return }` (slicer.go:816-818) and then immediately dereferences `ws.Drawing.RID` with no nil check on ws.Drawing itself. `Drawing` and `ExtLst` are two independently optional child elements of \u003cworksheet\u003e in xl/worksheets/sheetN.xml, populated separately by encoding/xml depending on whether each tag is present. I executed a PoC: took a plain NewFile() workbook (no drawing, no slicer), saved it, then injected a bare `\u003cextLst\u003e\u003c/extLst\u003e` immediately before `\u003c/worksheet\u003e` in xl/worksheets/sheet1.xml (no other change, no drawing element present) and called GetSlicers. Result: `runtime error: invalid memory address or nil pointer dereference`, confirmed at the ws.Drawing.RID line via stack trace.\n\n### Reachability / who can trigger this\n\nUnauthenticated: any application that opens an untrusted .xlsx and calls the public `File.GetSlicers(sheet)` API. The trigger is a single empty XML tag with no valid slicer or drawing content required at all, making it an even smaller/simpler payload than candidate 1.\n\n### Proof of Concept / Reproduction\n\n**Method:**\n1) Reused the existing clone at C:\\Users\\vatsa\\AppData\\Local\\Temp\\claude\\D--\\59901fa2-b549-40e1-a433-30c8a3d01718\\scratchpad\\CVE-Hunt-10k\\repos\\qax-os__excelize (origin=https://github.com/qax-os/excelize.git, branch master up to date, commit e81f05008b33232151a58f06474fad5f70c4d060 dated 2026-09-27, `git describe`=v2.11.0-42-ge81f050, i.e. 42 commits past the v2.11.0 tag). 2) Manually re-read the cited source myself (not trusting the claim): slicer.go:805-820 (GetSlicers) and xmlWorksheet.go:21-72 (xlsxWorksheet struct + xlsxDrawing struct) -- confirmed verbatim, matches claim exactly. 3) Confirmed local toolchain: go version go1.27.0 windows/amd64 (/c/Program Files/Go/bin/go). 4) Built the whole package with `go build ./...` from the repo root -- succeeded with zero errors, proving the current checkout compiles cleanly. 5) Ran the pre-existing PoC test file already present in this checkout (slicer_extlst_poc_test.go, TestGetSlicersNilDrawingPanic) via `go test -run TestGetSlicersNilDrawingPanic -v .`: it builds a benign excelize.NewFile() workbook, saves it, reopens it, loads the raw zip entry xl/worksheets/sheet1.xml, string-replaces `\u003c/worksheet\u003e` with `\u003cextLst\u003e\u003c/extLst\u003e\u003c/worksheet\u003e` (adds nothing else, no \u003cdrawing\u003e anywhere), rebuilds the zip byte-for-byte otherwise unchanged (rezipReplacing helper, plain archive/zip re-encode), reopens the crafted file with excelize.OpenReader, and calls the public GetSlicers(\"Sheet1\") API inside a recover(). 6) For a fully independent, non-test-harness reproduction, I additionally wrote and built my own separate standalone Go module+program from scratch (excelize-nilptr-repro/{go.mod,main.go} in my scratchpad dir) that imports the real, unmodified excelize package as an external library dependency via a `replace github.com/xuri/excelize/v2 =\u003e ../CVE-Hunt-10k/repos/qax-os__excelize` directive (no copy-pasted/reimplemented function -- the actual library code, used through its real public API from a separate consumer program, simulating \"any application that opens an untrusted .xlsx\"). This program performs the identical craft-and-open sequence but calls GetSlicers with NO recover() at all. Built with `go build -o repro.exe .` (succeeded) and ran `./repro.exe` directly, observing an unhandled process crash.\n\n**Evidence:**\nSOURCE VERIFICATION (current HEAD, read directly): slicer.go:816-820 is exactly as claimed -- `if ws.ExtLst == nil { return slicers, err }` followed immediately by `target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)` with no nil check on ws.Drawing. xmlWorksheet.go:54 `Drawing *xlsxDrawing `xml:\"drawing\"`` and xmlWorksheet.go:64 `ExtLst *xlsxExtLst `xml:\"extLst\"`` are both independently-optional pointer fields on xlsxWorksheet, populated only when their respective XML tag is present in the worksheet part -- confirming Drawing and ExtLst are unrelated/independent. getSheetRelationshipsTargetByID(sheet, rID string) string (sheet.go:721) takes a plain string, so `ws.Drawing.RID` is evaluated (dereferencing the nil pointer) at the call site itself.\n\nRUN 1 -- existing repo test, `go test -run TestGetSlicersNilDrawingPanic -v .`:\n=== RUN   TestGetSlicersNilDrawingPanic\n    slicer_extlst_poc_test.go:38: original sheet1.xml: ...\u003csheetData\u003e...\u003c/sheetData\u003e\u003c/worksheet\u003e   (no drawing, no extLst)\n    slicer_extlst_poc_test.go:48: tampered sheet1.xml: ...\u003c/sheetData\u003e\u003cextLst\u003e\u003c/extLst\u003e\u003c/worksheet\u003e\n    slicer_extlst_poc_test.go:59: CONFIRMED: GetSlicers panicked on nil ws.Drawing dereference: runtime error: invalid memory address or nil pointer dereference\n--- PASS: TestGetSlicersNilDrawingPanic (0.01s)\nPASS\nok  \tgithub.com/xuri/excelize/v2\t0.100s\n\nRUN 2 -- my own independent standalone consumer program (real unmodified library via go.mod replace, NO recover installed), `./repro.exe`:\n[*] Saved benign workbook: 6052 bytes\n[*] Original sheet1.xml: ...\u003csheetData\u003e...\u003c/sheetData\u003e\u003c/worksheet\u003e\n[*] Tampered sheet1.xml (attacker payload -- bare empty \u003cextLst\u003e, still no \u003cdrawing\u003e): ...\u003cextLst\u003e\u003c/extLst\u003e\u003c/worksheet\u003e\n[*] Crafted malicious .xlsx: 6059 bytes\n[*] Crafted workbook accepted by OpenReader. Calling the public GetSlicers(\"Sheet1\") API now, no recover() installed...\npanic: runtime error: invalid memory address or nil pointer dereference\n[signal 0xc0000005 code=0x0 addr=0x20 pc=0x7ff7fc8456b8]\n\ngoroutine 1 [running]:\ngithub.com/xuri/excelize/v2.(*File).GetSlicers(0x27fa8874908, {0x7ff7fc8a5e8a, 0x6})\n\tC:/.../qax-os__excelize/slicer.go:820 +0x98\nmain.main()\n\tC:/.../excelize-nilptr-repro/main.go:122 +0x67f\nEXIT CODE: 2\n\nThe stack trace pinpoints slicer.go:820 exactly (the ws.Drawing.RID dereference), from an ordinary external caller with no recover(), on a workbook whose only \"malicious\" content is one empty `\u003cextLst\u003e\u003c/extLst\u003e` tag and zero drawing/slicer content -- confirming the claim end-to-end: nil-pointer dereference, unrecovered panic, DoS, matching CWE-476.",
  "id": "GHSA-r7x5-4969-99p7",
  "modified": "2026-10-08T16:50:08Z",
  "published": "2026-10-08T16:50:08Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/qax-os/excelize/security/advisories/GHSA-r7x5-4969-99p7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-107213"
    },
    {
      "type": "WEB",
      "url": "https://github.com/qax-os/excelize/pull/2434"
    },
    {
      "type": "WEB",
      "url": "https://github.com/qax-os/excelize/commit/8ffeb07ec9a350410f6922313f6b7e4f5cc79241"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/qax-os/excelize"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Excelize: Nil-pointer dereference in GetSlicers when a worksheet has extLst present but no drawing element"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…