{"schema":"threatline.record/2","record":{"id":"CVE-2026-107213","aliases":["GHSA-r7x5-4969-99p7","GO-2026-6687"],"summary":"Excelize: Nil-pointer dereference in GetSlicers when a worksheet has extLst present but no drawing element","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 <worksheet> 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.\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 `</worksheet>` with `<extLst></extLst></worksheet>` (adds nothing else, no <drawing> 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.\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: ...<sheetData>...</sheetData></worksheet>   (no drawing, no extLst)\n    slicer_extlst_poc_test.go:48: tampered sheet1.xml: ...</sheetData><extLst></extLst></worksheet>\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: ...<sheetData>...</sheetData></worksheet>\n[*] Tampered sheet1.xml (attacker payload -- bare empty <extLst>, still no <drawing>): ...<extLst></extLst></worksheet>\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 `<extLst></extLst>` tag and zero drawing/slicer content -- confirming the claim end-to-end: nil-pointer dereference, unrecovered panic, DoS, matching CWE-476.","categories":["Libraries & packages"],"product":"github.com/xuri/excelize/v2","vendor":"qax-os","ecosystem":"go","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-r7x5-4969-99p7","source_modified_at":"2026-10-08T16:50:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"go","name":"github.com/xuri/excelize/v2"},"range":">= 2.9.0, < 2.11.1-0.20260929015830-8ffeb07ec9a3"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-107213","source_modified_at":"2026-10-07T22:05:46.475Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"excelize","vendor":"qax-os","versions":[{"version":">= 2.9.0, <= 2.11.0","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-r7x5-4969-99p7","source_modified_at":"2026-10-19T21:41:00.604164450Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"github.com/xuri/excelize/v2","ecosystem":"Go","purl":"pkg:golang/github.com/xuri/excelize/v2"},"ranges":[{"type":"SEMVER","events":[{"introduced":"2.9.0"},{"fixed":"2.11.1-0.20260929015830-8ffeb07ec9a3"}]}],"versions":[]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-r7x5-4969-99p7","source_modified_at":"2026-10-08T16:50:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"go","name":"github.com/xuri/excelize/v2"},"version":"2.11.1-0.20260929015830-8ffeb07ec9a3"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-r7x5-4969-99p7","source_modified_at":"2026-10-19T21:41:00.604164450Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"github.com/xuri/excelize/v2","ecosystem":"Go","purl":"pkg:golang/github.com/xuri/excelize/v2"},"version":"2.11.1-0.20260929015830-8ffeb07ec9a3"}],"cvss":8.7,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00217,"percentile":0.11543,"score_date":"2026-10-23"},"published_at":"2026-10-07T17:40:29.865Z","modified_at":"2026-10-19T21:41:00.604164450Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-r7x5-4969-99p7","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-08T16:50:09Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-107213","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-07T22:05:46.475Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-r7x5-4969-99p7","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-19T21:41:00.604164450Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-107213","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/qax-os/excelize/security/advisories/GHSA-r7x5-4969-99p7","tags":["x_refsource_CONFIRM"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-107213","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/qax-os/excelize/pull/2434","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/qax-os/excelize/commit/8ffeb07ec9a350410f6922313f6b7e4f5cc79241","tags":["x_refsource_MISC"]},{"url":"https://github.com/advisories/GHSA-r7x5-4969-99p7","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"none","automatable":"yes","technical_impact":"partial","assessed_at":"2026-10-07T22:00:03.455612Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-19T21:41:00.604164450Z","content_hash":"8e4fd9d5f3dc4cb213a277d49e81287346b80e60703953b8cc88cae36a51eaa8"}}
