# CVE-2026-63268 — LFI via calcext:data-mappings, sql provider and sdbc:flat:file:// db href (LibreOffice Calc)

## Summary

LibreOffice Calc persists external data-source links (`calcext:data-mappings`) inside ODS
documents and restores them when the document is opened. In vulnerable versions the
restoration is performed for *every* provider named in the document, including the
unfinished `org.libreoffice.calc.sql` provider. On load, that provider parses the saved
`calcext:id` as `table@database`, resolves the `database` component through
`sdb::DatabaseContext` (which interprets an arbitrary unregistered name as a URL and
loads it), connects to the resulting database and copies the result of
`SELECT * FROM <table>` into a named database range of the sheet. Because an attacker
can make that database resolve to an odb that names a **folder of local text files** as
a `text/csv` file-based database — i.e. an `sdbc:flat:file://<victim folder>` connection
URL handled by the flat Text/CSV SDBC driver, which exposes every text file in the
folder as a table — merely opening the crafted ODS reads a local victim text file into
the sheet (Local File Inclusion / information disclosure).

## Impact

- Package/component affected: `sc` (LibreOffice Calc), external data mapping
  import/restore path: `sc/source/filter/xml/xmlmappingi.cxx`
  (`ScXMLMappingContext`), `sc/source/ui/dataprovider/sqldataprovider.cxx`
  (`SQLFetchThread`), `dbaccess` `ODatabaseContext::getByName`/`loadObjectFromURL`,
  `connectivity` flat (Text/CSV) SDBC driver.
- Affected versions: LibreOffice before 26.2.5 / 26.8.0 (verified on the official
  `LibreOffice 26.2.4.2` deb build `0229ac93fcf0d7cbc6376066c6f35021cef002dc`).
- Fixed versions: LibreOffice 26.2.5 / 26.8.0 (verified on the official
  `LibreOffice 26.2.5.2` deb build `cd7284b4cbbfeb507e630c1aac019f4157393acb`).
- Risk level and consequences: Medium (advisory severity). Reading any local text
  file readable by the victim user into the document. The disclosed content is under
  attacker layout control inside the sheet (attacker-chosen destination range), so in
  the classic scenario — a document that is later saved and returned to the sender, or
  a screenshot/preview — the local file contents are disclosed to the attacker.

## Impact Parity

- Disclosed/claimed maximum impact: local text file contents read into the sheet
  through a document-named `sdbc:flat:file://` database href (info leak / LFI), no code
  execution claimed.
- Reproduced impact from this run: **full parity**. Both fresh vulnerable product
  attempts read a unique per-attempt secret from a local victim file
  (`$HOME/victim_secrets/secretfile`) into the sheet; the leaked marker is present in
  the sheet exported by the product (`repro/proof/vulnerable-*/leaked-sheet.csv`).
  Both fixed product attempts opened the same document and did not leak (the
  document-named `sql` mapping was ignored and the attacker-hosted odb was never even
  fetched over HTTP).
- Parity: `full`.
- Not demonstrated: nothing beyond the claim (no code execution, no memory-safety
  corruption involved in this issue).

## Root Cause

1. `ScXMLMappingContext` (`sc/source/filter/xml/xmlmappingi.cxx`) imports every
   `calcext:data-mapping` element and — in vulnerable versions — inserts an
   `sc::ExternalDataSource` for **whatever provider the document names**, then its
   destructor calls `ExternalDataSource::refresh(pDoc, true)`.
2. `DataProviderFactory::getDataProvider` maps `org.libreoffice.calc.sql` to
   `SQLDataProvider`, which parses the saved `calcext:id` as `table@database` and
   resolves `database` through `sdb::DatabaseContext::getByName`.
3. `ODatabaseContext::getByName` (dbaccess) **interprets an unregistered name as a
   URL** and calls `loadObjectFromURL`, so an attacker-chosen odb URL becomes a live
   data source. The crafted odb declares
   `<db:file-based-database xlink:href="file://<victim folder>" db:media-type="text/csv"/>`,
   which LibreOffice maps (Drivers.xcu `sdbc:flat:*` → MediaType `text/csv`) to the
   connection URL `sdbc:flat:file://<victim folder>` — the document-named
   `sdbc:flat:file://` db href.
4. The flat Text/CSV SDBC driver treats every text file in that folder as a database
   table, so `SELECT * FROM "secretfile"` returns the contents of the victim's local
   text file, which `ScDBDataManager::WriteToDoc` copies into the document's named
   database range (`calcext:database-name`).
5. Fix commit: `104d2b4f5dae917661b20b18d5d2043fca4407e4`
   ("sc: only build the supported data providers when loading a document", vulnerable
   parent `b389e707d3a80ea7156808387fe6a2b1602c49d0`). The fix makes
   `ScXMLMappingContext` ignore any provider other than
   `org.libreoffice.calc.{csv,html,xml}`; the sql provider is explicitly excluded
   because it was never finished and was dropped from the Data Provider dialog
   (tdf#169079).

## Reproduction Steps

1. Reference: `bundle/repro/reproduction_steps.sh` (self-contained; run twice in this
   session with identical results).
2. What the script does:
   - Installs missing runtime libraries, downloads and unpacks the official released
     deb builds `LibreOffice 26.2.4.2` (vulnerable) and `LibreOffice 26.2.5.2` (fixed
     control) from the Document Foundation archive (cached under
     `bundle/repro/cache/`, SHA-256 recorded in the logs).
   - Writes a fresh victim secret `$HOME/victim_secrets/secretfile` containing a unique
     per-attempt marker.
   - Builds the attacker `evil.odb` that names the victim's folder as a `text/csv`
     file-based database (`sdbc:flat:file://<victim folder>` db href) and hosts it on
     an attacker HTTP server (localhost, unique port per attempt).
   - Crafts the attack ODS: a product-generated seed document plus a named database
     range and a `calcext:data-mapping` with
     `calcext:provider="org.libreoffice.calc.sql"`,
     `calcext:id="secretfile@http://127.0.0.1:<port>/evil.odb"`,
     `xlink:href="sdbc:flat:file://<victim folder>"`,
     `calcext:database-name="leakrange"`.
   - Runs **two clean vulnerable** and **two clean fixed** product attempts: each opens
     the crafted ODS through the real document-open path with an isolated fresh user
     profile (`-env:UserInstallation=...`), converts the document to CSV, and records
     per-attempt transcripts, the attacker HTTP access log, the exported sheet, and a
     strict JSON observation.
   - Writes `bundle/repro/runtime_manifest.json` with the concrete proof artifacts and
     their SHA-256 digests.
3. Expected evidence of reproduction:
   - `repro/proof/vulnerable-{1,2}/leaked-sheet.csv` contain the unique per-attempt
     secret marker (local file content read into the sheet), e.g.
     `CVE-2026-63268-vulnerable-1-LEAKED-SECRET-MARKER-...,do-not-exfiltrate`.
   - `repro/proof/vulnerable-{1,2}/http-server.log` show LibreOffice itself fetching
     the attacker-hosted odb (`GET/HEAD /evil.odb` from the product beyond the script's
     single healthcheck GET).
   - `repro/proof/fixed-{1,2}/leaked-sheet.csv` contain only the seed cells
     (`a,b / 1,2`) — the same document opens without leaking, and the fixed product
     never fetches the attacker-hosted odb (access log shows only the healthcheck GET).

## Evidence

- Run log: `bundle/logs/reproduction_steps.log`
  - `vulnerable build: LibreOffice 26.2.4.2 0229ac93fcf0d7cbc6376066c6f35021cef002dc`
  - `fixed build:     LibreOffice 26.2.5.2 cd7284b4cbbfeb507e630c1aac019f4157393acb`
  - `attempt vulnerable-1: SECRET LEAKED into sheet (odb fetched 3x by product)`
  - `attempt vulnerable-2: SECRET LEAKED into sheet (odb fetched 3x by product)`
  - `attempt fixed-1: no leak (odb fetched 1x by product)`
  - `attempt fixed-2: no leak (odb fetched 1x by product)`
- Leaked sheet (vulnerable attempt 1), `repro/proof/vulnerable-1/leaked-sheet.csv`:
  ```
  CVE-2026-63268-vulnerable-1-LEAKED-SECRET-MARKER-e7a101ec,do-not-exfiltrate
  row2,second-secret-line
  ```
  (the victim-only local file content, now inside the Calc sheet)
- Fixed attempt 1, `repro/proof/fixed-1/leaked-sheet.csv`:
  ```
  a,b
  1,2
  ```
- Product fetch of the attacker-hosted odb, `repro/proof/vulnerable-1/http-server.log`:
  ```
  "HEAD /evil.odb HTTP/1.1" 200 -
  "GET /evil.odb HTTP/1.1" 200 -
  ```
  while `repro/proof/fixed-1/http-server.log` shows only the script's healthcheck GET.
- Strict per-attempt observations: `repro/proof/{vulnerable,fixed}-{1,2}/observation.json`
  (`marker_leaked_into_sheet` true/false respectively).
- Runtime manifest with digests: `bundle/repro/runtime_manifest.json`.
- Environment: Linux x86-64 (Ubuntu), non-sanitized official product builds
  (`soffice --headless --convert-to`), attacker resource served by `python3 -m
  http.server` on 127.0.0.1, `sudo apt-get install` of standard X/GTK runtime
  libraries only.

## Recommendations / Next Steps

- Upgrade to LibreOffice 26.2.5 / 26.8.0 (fix commit
  `104d2b4f5dae917661b20b18d5d2043fca4407e4`), where document load restores only the
  `csv`, `html` and `xml` data providers and logs
  `ignoring document data mapping for provider "..."` for anything else.
- Defense in depth (upstream hardening opportunities beyond the shipped fix):
  - `SQLFetchThread` should not resolve document-supplied database names through
    `DatabaseContext::getByName`, which interprets arbitrary names as loadable URLs.
  - Document-initiated network fetches of database resources on open should be gated
    behind the existing link-update/external-link trust controls.
- Testing recommendation: add an ODS import unit test that a
  `calcext:data-mapping` with `calcext:provider="org.libreoffice.calc.sql"` (and any
  other non csv/html/xml provider) is dropped on load; the sibling advisories
  CVE-2026-63266/CVE-2026-63267/CVE-2026-63269/CVE-2026-63270/CVE-2026-63277 exercise
  the same restore path through other SDBC providers.

## Additional Notes

- Idempotency: the script was executed twice consecutively with identical results
  (both runs: 2/2 vulnerable attempts leak, 2/2 fixed attempts fail closed; both runs
  exit 0). All state is per-run (fresh victim secret, fresh user profiles, unique
  ports and markers); downloaded product tarballs and unpacked trees are reused from
  `bundle/repro/cache/` when already present and intact.
- The attacker odb can equally be referenced through any URL scheme LibreOffice can
  load (file:// for a locally delivered odb was also verified during analysis; the
  shipped proof uses an attacker-hosted HTTP URL, matching the remote-attacker
  scenario). The `xlink:href` stored in the mapping is the `sdbc:flat:file://` db href
  naming the victim's folder; the operative href inside the odb is what turns that
  folder into a database.
- A direct `calcext:id` of `table@sdbc:flat:file://...` does *not* resolve
  (`ODatabaseContext::loadObjectFromURL` rejects it because the UCB reports it is not
  a loadable document URL), which is why the odb indirection is part of the working
  mechanism — consistent with the advisory wording that the link "names a folder of
  local text files as a database".
- The leak destination is the attacker-chosen named database range, so the leaked
  content can be laid out anywhere in the sheet; retrieval by the attacker requires
  the usual save-and-return or preview channel (not exercised here — only the
  disclosure into the sheet is claimed and was proven).
