{"repro_id":"REPRO-2026-00375","version":6,"title":"LFI and GET SSRF via calcext:data-mappings and csv provider — document open reads a local file into the sheet and issues an attacker-directed GET","repro_type":"security","status":"published","severity":"medium","description":"# CVE-2026-63267 — LFI and GET SSRF via calcext:data-mappings and csv provider (LibreOffice)","root_cause":"# CVE-2026-63267 — RCA: LFI and GET SSRF via calcext:data-mappings (csv provider)\n\n## Summary\n\nLibreOffice Calc persists external csv data-source links inside the document\n(`calcext:data-mappings` / `calcext:data-mapping` with provider\n`org.libreoffice.calc.csv`). In vulnerable versions the link target\n(`xlink:href`) was fetched automatically while the document loaded, with no\nlink-update gate. Opening an attacker-crafted spreadsheet therefore (a) reads\nan arbitrary local file into sheet cells (LFI, e.g. via `file:///etc/passwd`)\nand (b) issues an attacker-directed HTTP GET to a host of the document's\nchoosing (SSRF). The reproduction proves both effects end-to-end through the\nreal LibreOffice Calc document-open path on LibreOffice 26.2.4.2, and proves\nthe negative control on fixed LibreOffice 26.2.5.2, where the fetch is gated\nbehind the same link-update control as other spreadsheet links.\n\n## Impact\n\n- Package/component: LibreOffice Calc (`sc`), external data providers\n  (`calcext:data-mappings`, csv provider `org.libreoffice.calc.csv`).\n- Affected versions: LibreOffice < 26.2.5 / < 26.8.0 (reproduced on\n  26.2.4.2, build `0229ac93fcf0d7cbc6376066c6f35021cef002dc`).\n- Risk: medium. Local file disclosure into the sheet (contents can then be\n  exfiltrated, e.g. via a companion mapping or the sibling data-mapping bugs)\n  and unauthenticated GET SSRF to arbitrary hosts (including internal network\n  targets) triggered merely by opening a document.\n\n## Impact Parity\n\n- Disclosed/claimed maximum impact: local file read into the sheet (LFI) and\n  attacker-directed GET request (SSRF) on document open.\n- Reproduced impact from this run: both — the vulnerable build read a unique\n  marker from a local `file://` CSV into cell A1 (2/2 attempts) and issued\n  HTTP GETs carrying per-attempt unique tokens to the attacker-controlled\n  server (2/2 attempts), both during plain document load.\n- Parity: `full`.\n- Not demonstrated: nothing claimed beyond LFI + GET SSRF; no code execution\n  was claimed or pursued for this ticket.\n\n## Root Cause\n\nWhen a document containing `calcext:data-mappings` is loaded, Calc's external\ndata mapper (`ScExternalDataMapper`) reconstructs the persisted data sources\nand the csv provider immediately refreshed (fetched) the mapping's\n`xlink:href` target during load. Because the fetch happened at load time\nrather than through the `sfx2::LinkManager` update path, neither the\n\"update links when loading\" user setting nor any prompt protected the user:\nthe document decided if and where a fetch happened. Since `xlink:href`\naccepts both `file://` and `http(s)://` URLs, the load-time fetch yields LFI\nand SSRF respectively.\n\nFix: in LibreOffice 26.2.5 / 26.8.0 (fix by Caolán McNamara) a data mapping\nloaded from a document is registered as an external link in the document's\n`LinkManager`, and its data is only refreshed when link updating is allowed —\nthe same gate that governs sheet links and area links. The upstream QA test\n`testLinkUpdateGate` (sc/qa/unit/dataproviders_test.cxx, fixture\n`sc/qa/unit/data/dataprovider/mappinggate.fods`) encodes exactly this:\n\"With updating not allowed, updating the links leaves the saved value.\"\n\n## Reproduction Steps\n\n1. `bundle/repro/reproduction_steps.sh` (self-contained; downloads the\n   official TDF release tarballs if not already cached under\n   `bundle/artifacts/`).\n2. The script:\n   - Downloads and extracts LibreOffice 26.2.4.2 (vulnerable) and 26.2.5.2\n     (fixed) Linux x86-64 deb packages (only `ure`, core, calc, en-us\n     components) from downloadarchive.documentfoundation.org.\n   - Generates a flat ODS (`.fods`) document whose\n     `calcext:data-mapping` (provider `org.libreoffice.calc.csv`) points at\n     `file://<workspace>/local_secret.csv` (LFI probe) and a second document\n     pointing at `http://127.0.0.1:8931/CVE-2026-63267-SSRF-<token>.csv`\n     (SSRF probe). The saved cell content is the sentinel\n     `SAVED_SENTINEL_NOT_FETCHED`.\n   - Starts a Python attacker HTTP server that logs every GET and serves a\n     marker CSV.\n   - Opens each document through the real product binary\n     (`soffice --headless --convert-to csv`), 2 attempts per role\n     (vuln/fixed × lfi/ssrf), each with an isolated user profile.\n   - Evaluates: vulnerable LFI = converted sheet contains the local-file\n     marker; vulnerable SSRF = attacker server received the per-attempt GET;\n     fixed = sheet still holds the sentinel and no GET was received.\n3. Expected evidence of reproduction: `sheet-vuln-lfi-*.csv` contain\n   `CVE-2026-63267_LFI_SECRET_9f4d2c`; `http-server-final.log` contains GETs\n   for the `vuln-ssrf-*` tokens only; `sheet-fixed-*.csv` contain\n   `SAVED_SENTINEL_NOT_FETCHED`.\n\n## Evidence\n\n- Full run log: `bundle/logs/reproduction_steps.log` (two consecutive\n  successful runs, both exit 0).\n- Per-attempt product logs: `bundle/logs/{vuln,fixed}-{lfi,ssrf}-{1,2}.log`.\n- Attacker server request log: `bundle/repro/http-server-final.log`:\n  - `GET /CVE-2026-63267-SSRF-vuln-ssrf-1.csv from 127.0.0.1`\n  - `GET /CVE-2026-63267-SSRF-vuln-ssrf-2.csv from 127.0.0.1`\n  - No `fixed-ssrf-*` request lines.\n- Converted sheets: `bundle/repro/sheet-vuln-lfi-1.csv` =\n  `CVE-2026-63267_LFI_SECRET_9f4d2c` (local file content injected into the\n  sheet at load); `bundle/repro/sheet-fixed-lfi-1.csv` =\n  `SAVED_SENTINEL_NOT_FETCHED`.\n- Verdict counts (both runs): vulnerable LFI 2/2, vulnerable SSRF 2/2,\n  fixed LFI clean 2/2, fixed SSRF clean 2/2.\n- Environment: Linux x86-64, no Docker; official TDF deb builds, headless\n  `svp` VCL plugin. Vulnerable build identity: LibreOffice 26.2.4.2\n  `0229ac93fcf0d7cbc6376066c6f35021cef002dc`, tarball SHA-256\n  `810ef197e190d7804a60e0016052c46ff33792303a200fddda9d5216a64b9900`.\n  Fixed build: LibreOffice 26.2.5.2 `cd7284b4cbbfeb507e630c1aac019f4157393acb`,\n  tarball SHA-256 `2f03bfb2ac9f33ea7c77331b4b7a23300fb0ed7443566046bf8b5bc51c1bed1e`.\n- Runtime manifest with artifact hashes:\n  `bundle/repro/runtime_manifest.json`.\n\n## Recommendations / Next Steps\n\n- Upgrade to LibreOffice 26.2.5 / 26.8.0 or later, where external data links\n  are updated only under the link-update control.\n- The upstream fix approach (registering data mappings as LinkManager links\n  gated by the link-update permission) is confirmed effective by the fixed\n  negative control in this reproduction.\n- Testing recommendation: keep `testLinkUpdateGate`-style coverage for both\n  `file://` and `http(s)://` hrefs and for headless document load.\n\n## Additional Notes\n\n- Idempotency: the script was run twice consecutively in this session; both\n  runs exited 0 with identical verdicts. Re-runs reuse the extracted builds\n  under `bundle/artifacts/libreoffice/` and re-generate all documents,\n  secrets, tokens, and logs.\n- The demonstration uses the real product binary through its normal\n  document-open path (`soffice --headless --convert-to csv`); no sanitizer,\n  no mock, no reimplementation.\n- The SSRF target is a loopback attacker server for safety; the vulnerable\n  code path issues a real HTTP GET to whatever host the document names.\n- Sibling data-mapping advisories (CVE-2026-63266 … CVE-2026-63277) share the\n  same load-time fetch root cause with different providers/impacts; they are\n  separate tickets.\n","cve_id":"CVE-2026-63267","cwe_id":"CWE-200","source_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-63267","package":{"name":"LibreOffice (The Document Foundation)","ecosystem":"vendor","affected_versions":"LibreOffice < 26.2.5 (26.2.x line) and < 26.8.0","fixed_version":"26.2.5"},"reproduced_at":"2026-10-05T21:14:47.992977+00:00","duration_secs":2732.0,"tool_calls":172,"handoffs":2,"total_cost_usd":3.427625,"agent_costs":{"claim_matcher":0.013263,"judge":0.240111,"learning_policy":0.010035,"repro":1.474894,"support":0.085771,"vuln_variant":1.603551},"cost_breakdown":{"claim_matcher":{"gpt-5.4-mini-2026-03-17":0.013263},"judge":{"gpt-5.6-sol":0.240111},"learning_policy":{"gpt-5.4-mini-2026-03-17":0.010035},"repro":{"accounts/fireworks/models/kimi-k3":1.474894},"support":{"accounts/fireworks/models/kimi-k3":0.085771},"vuln_variant":{"accounts/fireworks/models/kimi-k3":1.603551}},"vulnerable_version_variant_outcome":"unknown","fix_bypass_outcome":"unknown","variant_disclosure_state":"unknown","quality":{"confidence":"high","idempotent_verified":false,"community_verifications":0},"evidence":{"workflow":{"profile":"known_vulnerability","schema_version":2,"stages":["support","claim_contract","repro","judge","vuln_variant"]}},"environment":{"sandbox_image":"ghcr.io/n3mes1s/pruva-sandbox@sha256:8096b2518d6022e13d68f885c3b8ded6b4fe607098b1a1ccbfb99abc004d1dc1"},"published_at":"2026-10-05T21:14:48.883287+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":7263,"category":"analysis"},{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":13176,"category":"reproduction_script"},{"path":"bundle/logs/fixed-lfi-1.log","filename":"fixed-lfi-1.log","size":196,"category":"log"},{"path":"bundle/logs/fixed-lfi-2.log","filename":"fixed-lfi-2.log","size":196,"category":"log"},{"path":"bundle/logs/fixed-ssrf-1.log","filename":"fixed-ssrf-1.log","size":199,"category":"log"},{"path":"bundle/logs/fixed-ssrf-2.log","filename":"fixed-ssrf-2.log","size":199,"category":"log"},{"path":"bundle/logs/reproduction_steps.log","filename":"reproduction_steps.log","size":3577,"category":"log"},{"path":"bundle/logs/vuln-lfi-1.log","filename":"vuln-lfi-1.log","size":193,"category":"log"},{"path":"bundle/logs/vuln-lfi-2.log","filename":"vuln-lfi-2.log","size":193,"category":"log"},{"path":"bundle/logs/vuln-ssrf-1.log","filename":"vuln-ssrf-1.log","size":196,"category":"log"},{"path":"bundle/logs/vuln-ssrf-2.log","filename":"vuln-ssrf-2.log","size":196,"category":"log"},{"path":"bundle/repro/http-server-final.log","filename":"http-server-final.log","size":395,"category":"log"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":3271,"category":"other"},{"path":"bundle/repro/sheet-fixed-lfi-1.csv","filename":"sheet-fixed-lfi-1.csv","size":27,"category":"other"},{"path":"bundle/repro/sheet-fixed-lfi-2.csv","filename":"sheet-fixed-lfi-2.csv","size":27,"category":"other"},{"path":"bundle/repro/sheet-fixed-ssrf-1.csv","filename":"sheet-fixed-ssrf-1.csv","size":27,"category":"other"},{"path":"bundle/repro/sheet-fixed-ssrf-2.csv","filename":"sheet-fixed-ssrf-2.csv","size":27,"category":"other"},{"path":"bundle/repro/sheet-vuln-lfi-1.csv","filename":"sheet-vuln-lfi-1.csv","size":33,"category":"other"},{"path":"bundle/repro/sheet-vuln-lfi-2.csv","filename":"sheet-vuln-lfi-2.csv","size":33,"category":"other"},{"path":"bundle/repro/sheet-vuln-ssrf-1.csv","filename":"sheet-vuln-ssrf-1.csv","size":27,"category":"other"},{"path":"bundle/repro/sheet-vuln-ssrf-2.csv","filename":"sheet-vuln-ssrf-2.csv","size":27,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":1256,"category":"other"}]}