{"repro_id":"REPRO-2026-00377","version":6,"title":"LFI via calcext:data-mappings, sql provider and sdbc:flat file db href — document reads a local text file into the sheet","repro_type":"security","status":"published","severity":"medium","description":"# CVE-2026-63268 — LFI via calcext:data-mappings, sql provider and sdbc:flat:file:// db href (LibreOffice)","root_cause":"# CVE-2026-63268 — LFI via calcext:data-mappings, sql provider and sdbc:flat:file:// db href (LibreOffice Calc)\n\n## Summary\n\nLibreOffice Calc persists external data-source links (`calcext:data-mappings`) inside ODS\ndocuments and restores them when the document is opened. In vulnerable versions the\nrestoration is performed for *every* provider named in the document, including the\nunfinished `org.libreoffice.calc.sql` provider. On load, that provider parses the saved\n`calcext:id` as `table@database`, resolves the `database` component through\n`sdb::DatabaseContext` (which interprets an arbitrary unregistered name as a URL and\nloads it), connects to the resulting database and copies the result of\n`SELECT * FROM <table>` into a named database range of the sheet. Because an attacker\ncan make that database resolve to an odb that names a **folder of local text files** as\na `text/csv` file-based database — i.e. an `sdbc:flat:file://<victim folder>` connection\nURL handled by the flat Text/CSV SDBC driver, which exposes every text file in the\nfolder as a table — merely opening the crafted ODS reads a local victim text file into\nthe sheet (Local File Inclusion / information disclosure).\n\n## Impact\n\n- Package/component affected: `sc` (LibreOffice Calc), external data mapping\n  import/restore path: `sc/source/filter/xml/xmlmappingi.cxx`\n  (`ScXMLMappingContext`), `sc/source/ui/dataprovider/sqldataprovider.cxx`\n  (`SQLFetchThread`), `dbaccess` `ODatabaseContext::getByName`/`loadObjectFromURL`,\n  `connectivity` flat (Text/CSV) SDBC driver.\n- Affected versions: LibreOffice before 26.2.5 / 26.8.0 (verified on the official\n  `LibreOffice 26.2.4.2` deb build `0229ac93fcf0d7cbc6376066c6f35021cef002dc`).\n- Fixed versions: LibreOffice 26.2.5 / 26.8.0 (verified on the official\n  `LibreOffice 26.2.5.2` deb build `cd7284b4cbbfeb507e630c1aac019f4157393acb`).\n- Risk level and consequences: Medium (advisory severity). Reading any local text\n  file readable by the victim user into the document. The disclosed content is under\n  attacker layout control inside the sheet (attacker-chosen destination range), so in\n  the classic scenario — a document that is later saved and returned to the sender, or\n  a screenshot/preview — the local file contents are disclosed to the attacker.\n\n## Impact Parity\n\n- Disclosed/claimed maximum impact: local text file contents read into the sheet\n  through a document-named `sdbc:flat:file://` database href (info leak / LFI), no code\n  execution claimed.\n- Reproduced impact from this run: **full parity**. Both fresh vulnerable product\n  attempts read a unique per-attempt secret from a local victim file\n  (`$HOME/victim_secrets/secretfile`) into the sheet; the leaked marker is present in\n  the sheet exported by the product (`repro/proof/vulnerable-*/leaked-sheet.csv`).\n  Both fixed product attempts opened the same document and did not leak (the\n  document-named `sql` mapping was ignored and the attacker-hosted odb was never even\n  fetched over HTTP).\n- Parity: `full`.\n- Not demonstrated: nothing beyond the claim (no code execution, no memory-safety\n  corruption involved in this issue).\n\n## Root Cause\n\n1. `ScXMLMappingContext` (`sc/source/filter/xml/xmlmappingi.cxx`) imports every\n   `calcext:data-mapping` element and — in vulnerable versions — inserts an\n   `sc::ExternalDataSource` for **whatever provider the document names**, then its\n   destructor calls `ExternalDataSource::refresh(pDoc, true)`.\n2. `DataProviderFactory::getDataProvider` maps `org.libreoffice.calc.sql` to\n   `SQLDataProvider`, which parses the saved `calcext:id` as `table@database` and\n   resolves `database` through `sdb::DatabaseContext::getByName`.\n3. `ODatabaseContext::getByName` (dbaccess) **interprets an unregistered name as a\n   URL** and calls `loadObjectFromURL`, so an attacker-chosen odb URL becomes a live\n   data source. The crafted odb declares\n   `<db:file-based-database xlink:href=\"file://<victim folder>\" db:media-type=\"text/csv\"/>`,\n   which LibreOffice maps (Drivers.xcu `sdbc:flat:*` → MediaType `text/csv`) to the\n   connection URL `sdbc:flat:file://<victim folder>` — the document-named\n   `sdbc:flat:file://` db href.\n4. The flat Text/CSV SDBC driver treats every text file in that folder as a database\n   table, so `SELECT * FROM \"secretfile\"` returns the contents of the victim's local\n   text file, which `ScDBDataManager::WriteToDoc` copies into the document's named\n   database range (`calcext:database-name`).\n5. Fix commit: `104d2b4f5dae917661b20b18d5d2043fca4407e4`\n   (\"sc: only build the supported data providers when loading a document\", vulnerable\n   parent `b389e707d3a80ea7156808387fe6a2b1602c49d0`). The fix makes\n   `ScXMLMappingContext` ignore any provider other than\n   `org.libreoffice.calc.{csv,html,xml}`; the sql provider is explicitly excluded\n   because it was never finished and was dropped from the Data Provider dialog\n   (tdf#169079).\n\n## Reproduction Steps\n\n1. Reference: `bundle/repro/reproduction_steps.sh` (self-contained; run twice in this\n   session with identical results).\n2. What the script does:\n   - Installs missing runtime libraries, downloads and unpacks the official released\n     deb builds `LibreOffice 26.2.4.2` (vulnerable) and `LibreOffice 26.2.5.2` (fixed\n     control) from the Document Foundation archive (cached under\n     `bundle/repro/cache/`, SHA-256 recorded in the logs).\n   - Writes a fresh victim secret `$HOME/victim_secrets/secretfile` containing a unique\n     per-attempt marker.\n   - Builds the attacker `evil.odb` that names the victim's folder as a `text/csv`\n     file-based database (`sdbc:flat:file://<victim folder>` db href) and hosts it on\n     an attacker HTTP server (localhost, unique port per attempt).\n   - Crafts the attack ODS: a product-generated seed document plus a named database\n     range and a `calcext:data-mapping` with\n     `calcext:provider=\"org.libreoffice.calc.sql\"`,\n     `calcext:id=\"secretfile@http://127.0.0.1:<port>/evil.odb\"`,\n     `xlink:href=\"sdbc:flat:file://<victim folder>\"`,\n     `calcext:database-name=\"leakrange\"`.\n   - Runs **two clean vulnerable** and **two clean fixed** product attempts: each opens\n     the crafted ODS through the real document-open path with an isolated fresh user\n     profile (`-env:UserInstallation=...`), converts the document to CSV, and records\n     per-attempt transcripts, the attacker HTTP access log, the exported sheet, and a\n     strict JSON observation.\n   - Writes `bundle/repro/runtime_manifest.json` with the concrete proof artifacts and\n     their SHA-256 digests.\n3. Expected evidence of reproduction:\n   - `repro/proof/vulnerable-{1,2}/leaked-sheet.csv` contain the unique per-attempt\n     secret marker (local file content read into the sheet), e.g.\n     `CVE-2026-63268-vulnerable-1-LEAKED-SECRET-MARKER-...,do-not-exfiltrate`.\n   - `repro/proof/vulnerable-{1,2}/http-server.log` show LibreOffice itself fetching\n     the attacker-hosted odb (`GET/HEAD /evil.odb` from the product beyond the script's\n     single healthcheck GET).\n   - `repro/proof/fixed-{1,2}/leaked-sheet.csv` contain only the seed cells\n     (`a,b / 1,2`) — the same document opens without leaking, and the fixed product\n     never fetches the attacker-hosted odb (access log shows only the healthcheck GET).\n\n## Evidence\n\n- Run log: `bundle/logs/reproduction_steps.log`\n  - `vulnerable build: LibreOffice 26.2.4.2 0229ac93fcf0d7cbc6376066c6f35021cef002dc`\n  - `fixed build:     LibreOffice 26.2.5.2 cd7284b4cbbfeb507e630c1aac019f4157393acb`\n  - `attempt vulnerable-1: SECRET LEAKED into sheet (odb fetched 3x by product)`\n  - `attempt vulnerable-2: SECRET LEAKED into sheet (odb fetched 3x by product)`\n  - `attempt fixed-1: no leak (odb fetched 1x by product)`\n  - `attempt fixed-2: no leak (odb fetched 1x by product)`\n- Leaked sheet (vulnerable attempt 1), `repro/proof/vulnerable-1/leaked-sheet.csv`:\n  ```\n  CVE-2026-63268-vulnerable-1-LEAKED-SECRET-MARKER-e7a101ec,do-not-exfiltrate\n  row2,second-secret-line\n  ```\n  (the victim-only local file content, now inside the Calc sheet)\n- Fixed attempt 1, `repro/proof/fixed-1/leaked-sheet.csv`:\n  ```\n  a,b\n  1,2\n  ```\n- Product fetch of the attacker-hosted odb, `repro/proof/vulnerable-1/http-server.log`:\n  ```\n  \"HEAD /evil.odb HTTP/1.1\" 200 -\n  \"GET /evil.odb HTTP/1.1\" 200 -\n  ```\n  while `repro/proof/fixed-1/http-server.log` shows only the script's healthcheck GET.\n- Strict per-attempt observations: `repro/proof/{vulnerable,fixed}-{1,2}/observation.json`\n  (`marker_leaked_into_sheet` true/false respectively).\n- Runtime manifest with digests: `bundle/repro/runtime_manifest.json`.\n- Environment: Linux x86-64 (Ubuntu), non-sanitized official product builds\n  (`soffice --headless --convert-to`), attacker resource served by `python3 -m\n  http.server` on 127.0.0.1, `sudo apt-get install` of standard X/GTK runtime\n  libraries only.\n\n## Recommendations / Next Steps\n\n- Upgrade to LibreOffice 26.2.5 / 26.8.0 (fix commit\n  `104d2b4f5dae917661b20b18d5d2043fca4407e4`), where document load restores only the\n  `csv`, `html` and `xml` data providers and logs\n  `ignoring document data mapping for provider \"...\"` for anything else.\n- Defense in depth (upstream hardening opportunities beyond the shipped fix):\n  - `SQLFetchThread` should not resolve document-supplied database names through\n    `DatabaseContext::getByName`, which interprets arbitrary names as loadable URLs.\n  - Document-initiated network fetches of database resources on open should be gated\n    behind the existing link-update/external-link trust controls.\n- Testing recommendation: add an ODS import unit test that a\n  `calcext:data-mapping` with `calcext:provider=\"org.libreoffice.calc.sql\"` (and any\n  other non csv/html/xml provider) is dropped on load; the sibling advisories\n  CVE-2026-63266/CVE-2026-63267/CVE-2026-63269/CVE-2026-63270/CVE-2026-63277 exercise\n  the same restore path through other SDBC providers.\n\n## Additional Notes\n\n- Idempotency: the script was executed twice consecutively with identical results\n  (both runs: 2/2 vulnerable attempts leak, 2/2 fixed attempts fail closed; both runs\n  exit 0). All state is per-run (fresh victim secret, fresh user profiles, unique\n  ports and markers); downloaded product tarballs and unpacked trees are reused from\n  `bundle/repro/cache/` when already present and intact.\n- The attacker odb can equally be referenced through any URL scheme LibreOffice can\n  load (file:// for a locally delivered odb was also verified during analysis; the\n  shipped proof uses an attacker-hosted HTTP URL, matching the remote-attacker\n  scenario). The `xlink:href` stored in the mapping is the `sdbc:flat:file://` db href\n  naming the victim's folder; the operative href inside the odb is what turns that\n  folder into a database.\n- A direct `calcext:id` of `table@sdbc:flat:file://...` does *not* resolve\n  (`ODatabaseContext::loadObjectFromURL` rejects it because the UCB reports it is not\n  a loadable document URL), which is why the odb indirection is part of the working\n  mechanism — consistent with the advisory wording that the link \"names a folder of\n  local text files as a database\".\n- The leak destination is the attacker-chosen named database range, so the leaked\n  content can be laid out anywhere in the sheet; retrieval by the attacker requires\n  the usual save-and-return or preview channel (not exercised here — only the\n  disclosure into the sheet is claimed and was proven).\n","cve_id":"CVE-2026-63268","cwe_id":"CWE-200","source_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-63268","package":{"name":"LibreOffice/core","ecosystem":"Maven","affected_versions":"LibreOffice versions before 26.2.5 / 26.8.0 that restore persisted data-provider links on document load; 26.2.4.2 confirmed as last vulnerable release","fixed_version":"26.2.5"},"reproduced_at":"2026-10-06T05:22:25.648330+00:00","duration_secs":4133.0,"tool_calls":311,"handoffs":2,"total_cost_usd":6.225237,"agent_costs":{"claim_matcher":0.016282,"judge":0.540449,"learning_policy":0.011279,"repro":3.534248,"support":0.068392,"vuln_variant":2.054587},"cost_breakdown":{"claim_matcher":{"gpt-5.4-mini-2026-03-17":0.016282},"judge":{"gpt-5.6-sol":0.540449},"learning_policy":{"gpt-5.4-mini-2026-03-17":0.011279},"repro":{"accounts/fireworks/models/glm-5p3":3.534248},"support":{"accounts/fireworks/models/glm-5p3":0.068392},"vuln_variant":{"accounts/fireworks/models/glm-5p3":2.054587}},"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-06T05:22:26.557355+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":11464,"category":"analysis"},{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":12231,"category":"reproduction_script"},{"path":"bundle/repro/build_odb.py","filename":"build_odb.py","size":3150,"category":"script"},{"path":"bundle/repro/craft_ods.py","filename":"craft_ods.py","size":2505,"category":"script"},{"path":"bundle/repro/proof/fixed-1/crafted.ods","filename":"crafted.ods","size":7028,"category":"other"},{"path":"bundle/repro/proof/fixed-1/fixed-attempt-1.txt","filename":"fixed-attempt-1.txt","size":177,"category":"other"},{"path":"bundle/repro/proof/fixed-1/http-server.log","filename":"http-server.log","size":68,"category":"log"},{"path":"bundle/repro/proof/fixed-1/leaked-sheet.csv","filename":"leaked-sheet.csv","size":8,"category":"other"},{"path":"bundle/repro/proof/fixed-1/observation.json","filename":"observation.json","size":267,"category":"other"},{"path":"bundle/repro/proof/fixed-2/crafted.ods","filename":"crafted.ods","size":7027,"category":"other"},{"path":"bundle/repro/proof/fixed-2/fixed-attempt-2.txt","filename":"fixed-attempt-2.txt","size":177,"category":"other"},{"path":"bundle/repro/proof/fixed-2/http-server.log","filename":"http-server.log","size":68,"category":"log"},{"path":"bundle/repro/proof/fixed-2/leaked-sheet.csv","filename":"leaked-sheet.csv","size":8,"category":"other"},{"path":"bundle/repro/proof/fixed-2/observation.json","filename":"observation.json","size":267,"category":"other"},{"path":"bundle/repro/proof/vulnerable-1/crafted.ods","filename":"crafted.ods","size":7027,"category":"other"},{"path":"bundle/repro/proof/vulnerable-1/http-server.log","filename":"http-server.log","size":569,"category":"log"},{"path":"bundle/repro/proof/vulnerable-1/leaked-sheet.csv","filename":"leaked-sheet.csv","size":100,"category":"other"},{"path":"bundle/repro/proof/vulnerable-1/observation.json","filename":"observation.json","size":281,"category":"other"},{"path":"bundle/repro/proof/vulnerable-1/vulnerable-attempt-1.txt","filename":"vulnerable-attempt-1.txt","size":187,"category":"other"},{"path":"bundle/repro/proof/vulnerable-2/crafted.ods","filename":"crafted.ods","size":7028,"category":"other"},{"path":"bundle/repro/proof/vulnerable-2/http-server.log","filename":"http-server.log","size":569,"category":"log"},{"path":"bundle/repro/proof/vulnerable-2/leaked-sheet.csv","filename":"leaked-sheet.csv","size":100,"category":"other"},{"path":"bundle/repro/proof/vulnerable-2/observation.json","filename":"observation.json","size":281,"category":"other"},{"path":"bundle/repro/proof/vulnerable-2/vulnerable-attempt-2.txt","filename":"vulnerable-attempt-2.txt","size":187,"category":"other"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":4402,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":1743,"category":"other"}]}