# Root Cause Analysis — CVE-2026-63277

## Summary

LibreOffice Calc 26.2.4.2 and earlier can automatically process a persisted `calcext:data-mappings` SQL data provider while opening an attacker-crafted `.ods`. The mapping's identifier can point to a remote `.odb` database document. That database document controls the JDBC driver class name and `JavaDriverClassPath`; vulnerable LibreOffice passes an HTTP class-path URL into its Java class loader without restricting it to local files. As a result, opening the spreadsheet causes Calc to retrieve and initialize an attacker-controlled Java class. This run demonstrated that behavior through the real `soffice` document-open path and recorded target-local code-execution markers.

## Impact

- **Affected component:** LibreOffice Calc external data mappings, SQL data provider, database document loader, JDBC connector, and `jvmaccess::ClassPath`.
- **Affected versions:** Reproduced with official LibreOffice 26.2.4.2 (build commit `0229ac93fcf0d7cbc6376066c6f35021cef002dc`). The vendor states the issue is fixed in 26.2.5 and 26.8.0.
- **Fixed control:** Official LibreOffice 26.2.5.2 (build commit `cd7284b4cbbfeb507e630c1aac019f4157393acb`).
- **Risk:** High. A malicious spreadsheet can execute Java bytecode with the privileges of the LibreOffice user when the document is opened. The executed Java code can access files, processes, and network resources available to that account.

## Impact Parity

- **Disclosed/claimed maximum impact:** Code execution from a crafted Calc document.
- **Reproduced impact:** Attacker-controlled Java code execution in the real LibreOffice process path. The remote driver's static initializer wrote `CVE-2026-63277_REMOTE_JDBC_CODE_EXECUTION` to a unique marker for each vulnerable attempt.
- **Parity:** `full`
- **Not demonstrated:** No additional privilege escalation was attempted or claimed. The payload intentionally used a harmless file marker rather than launching a shell.

## Root Cause

The ODS importer creates an `ExternalDataSource` for every persisted `calcext:data-mapping`. The vulnerable import path refreshes each mapping immediately when the mapping context is destroyed. For `calcext:provider="org.libreoffice.calc.sql"`, `SQLDataProvider` parses an ID of the form `<table>@<database>` and gives `<database>` to `com.sun.star.sdb.DatabaseContext::getByName`. If the name is not registered, the database context treats it as a URL and loads the referenced `.odb`.

The remotely supplied `.odb` can persist:

- `db:connection-resource` selecting a JDBC URL;
- `db:java-driver-class` selecting an attacker-defined driver class; and
- `db:java-classpath` selecting where that class is loaded from.

In vulnerable builds, `jvmaccess::ClassPath::translateToUrls` converts every class-path entry into a Java URL without constraining its scheme. Therefore an HTTP entry reaches a Java class loader, which downloads and initializes the selected JDBC driver. Java class initialization is already arbitrary code execution; it happens before a successful database query is necessary.

The fixed code adds URL parsing in `jvmaccess/source/classpath.cxx` and raises `IllegalArgumentException` unless every Java class-path entry has the `file` scheme. The ticket identifies fixed releases rather than a specific core commit. The tested fixed release build is `cd7284b4cbbfeb507e630c1aac019f4157393acb`; its `jvmaccess/source/classpath.cxx` contains the nine-line scheme-validation change relative to vulnerable build `0229ac93fcf0d7cbc6376066c6f35021cef002dc`.

## Reproduction Steps

1. Run `bash bundle/repro/reproduction_steps.sh` from any directory. The script honors `PRUVA_ROOT` and uses the prepared project cache when available.
2. The script downloads immutable official LibreOffice archives for 26.2.4.2 and 26.2.5, verifies/logs their SHA-256 hashes, extracts the real products, and installs required runtime libraries and a JDK if absent.
3. It compiles an attacker-controlled JDBC driver whose static initializer writes a unique marker, generates a remote `.odb` containing an HTTP `db:java-classpath`, and generates a Calc `.ods` containing a persisted SQL `calcext:data-mapping` that points to that `.odb`.
4. A local HTTP service hosts the `.odb` and Java class. The script opens isolated document copies with real vulnerable and fixed `soffice` processes: two vulnerable attempts followed by two fixed attempts.
5. Success requires both vulnerable attempts to contain `marker_present=true` and the exact code-execution marker, while both fixed attempts contain `marker_present=false`. The script exits 0 only when this vulnerable/fixed divergence is observed.

## Evidence

- `bundle/repro/vulnerable-attempt-1.txt` and `vulnerable-attempt-2.txt`: each records `marker_present=true` and `CVE-2026-63277_REMOTE_JDBC_CODE_EXECUTION`.
- `bundle/repro/fixed-attempt-1.txt` and `fixed-attempt-2.txt`: each records `marker_present=false` for the same attacker procedure.
- `bundle/repro/http-server-final.txt`: records HTTP retrieval of `/evil.odb` and `/evil/RemoteDriver.class` by vulnerable Calc. There are two vulnerable retrieval sequences and no fixed retrieval of the remote class.
- `bundle/logs/product-archives.sha256`: immutable official archive hashes:
  - 26.2.4.2: `810ef197e190d7804a60e0016052c46ff33792303a200fddda9d5216a64b9900`
  - 26.2.5: `2f03bfb2ac9f33ea7c77331b4b7a23300fb0ed7443566046bf8b5bc51c1bed1e`
- `bundle/logs/vulnerable-version.txt` and `fixed-version.txt`: real product versions and build SHAs.
- `bundle/repro/runtime_manifest.json`: strict runtime manifest binding all finalized proof artifacts and their SHA-256 hashes.
- `bundle/logs/reproduction_steps.log`: full setup and diagnostic transcript. It is intentionally not listed as immutable proof because the script writes to it during execution.

Key vulnerable excerpt:

```text
role=vulnerable attempt=1 exit_code=255
marker_present=true
marker_content_begin
CVE-2026-63277_REMOTE_JDBC_CODE_EXECUTION
marker_content_end
```

Key fixed-control excerpt:

```text
role=fixed attempt=1 exit_code=255
marker_present=false
```

The exit code reflects intentional process-group termination after the bounded observation window, not a product crash. No sanitizer or instrumentation was used.

## Recommendations / Next Steps

- Upgrade to LibreOffice 26.2.5 or 26.8.0 and later.
- Retain the fixed `jvmaccess::ClassPath::translateToUrls` policy that rejects missing, malformed, and non-`file:` class-path entries before creating Java URLs.
- Add regression coverage that opens a real Calc data-mapping document with a remote `.odb`, rather than testing only the lower-level class-path helper.
- Cover HTTP, HTTPS, redirecting URLs, compound class paths, case variants of URL schemes, percent-encoded input, and expansion URLs.
- Keep a fixed negative control proving the remote `.odb` may be encountered but its Java class is never downloaded or initialized.
- Consider separately applying Calc's external-link consent controls to SQL mappings and remote database documents as defense in depth.

## Additional Notes

- **Idempotency:** The final reproducer was executed twice consecutively after completion. Every run recreates the generated ODS/ODB/payload, allocates a fresh loopback port, uses fresh user profiles and document copies, and kills each product process group before the next attempt.
- **User-facing path:** This is a production-path `viewer_document` proof using the official uninstrumented `soffice` product, not a parser or unit harness.
- **Network scope:** The HTTP service binds only to `127.0.0.1`; this safely reproduces the remote URL semantics while preventing exposure outside the sandbox.
- **Payload limitation:** The proof class only writes a marker. That is deliberate and sufficient because it is attacker-authored Java executing through the vulnerable class loader.
