Skip to content

fix: prevent appconfig path traversal to RCE via public_/remote_ keys (OC10-146) - #41802

Closed
kw-tmueller wants to merge 1 commit into
10.16from
fix/oc10-146-appconfig-path-traversal
Closed

fix: prevent appconfig path traversal to RCE via public_/remote_ keys (OC10-146)#41802
kw-tmueller wants to merge 1 commit into
10.16from
fix/oc10-146-appconfig-path-traversal

Conversation

@kw-tmueller

Copy link
Copy Markdown
Contributor

Summary

An authenticated admin could set the core appconfig key public_webdav (or any public_/remote_ key) to a path-traversal value and have it require_once'd by public.php on the next GET /public.php/webdav, achieving remote code execution.

Two independent defects made this possible; both are fixed here (defense in depth).

1. Sink — public.php had no traversal check (essential fix)

public.php included the stored handler path relative to the app directory with no traversal guard, unlike remote.php which already rejects ../. Added the same guard so a traversal path can never be included — this is the DB- and endpoint-independent gate.

2. Guard bypass — strict $app === 'core' compare

AppConfigController blocked admins from setting public_/remote_ keys on core with a strict $app === 'core' compare. A mangled app id such as "core " (trailing space) is not equal to "core" in PHP, yet the database folds it back to the core row — defeating the guard. This is the same defeat mechanism as the earlier core%81 truncation bypass. The app id is now normalized (cleanAppId + trim + strtolower) before the check, in:

  • AppConfigController::getValue/setValue/deleteKey
  • the legacy core/ajax/appconfig.php endpoint
  • and deleteApp now refuses to wipe the whole core appconfig.

Tests

Adds regression tests covering the mangled core spellings ("core ", " core", "CORE", "core/", "core..") and allowed near-misses (encore, non-service keys on core).

Verified in the ownCloud CI toolchain (PHP 7.4, PHPUnit 9.6, sqlite): OK — 40 tests, 105 assertions. Reverting the guard to the strict compare makes 13 of the new cases fail, confirming they catch the bypass.

@update-docs

update-docs Bot commented Sep 1, 2026

Copy link
Copy Markdown

Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a changelog item based on your changes.

@kw-tmueller kw-tmueller changed the title Fix path traversal via appconfig public_/remote_ keys leading to RCE (OC10-146) fix: prevent appconfig path traversal to RCE via public_/remote_ keys (OC10-146) Sep 1, 2026
@kw-tmueller
kw-tmueller force-pushed the fix/oc10-146-appconfig-path-traversal branch 2 times, most recently from a2d8e3f to 360833a Compare September 1, 2026 14:05
… (OC10-146)

An authenticated admin could set the core appconfig key `public_webdav`
(or any `public_`/`remote_` key) to a path-traversal value and have it
`require_once`'d by public.php on the next `GET /public.php/webdav`,
achieving remote code execution.

Two independent defects made this possible:

1. Sink: public.php included the stored handler path relative to the app
   directory with no traversal check, unlike remote.php which already
   rejects `../`. This is the essential, DB-independent gate: add the same
   guard so a traversal path can never be included.

2. Guard bypass: AppConfigController used a strict `$app === 'core'` compare
   to block admins from setting `public_`/`remote_` keys on core. A mangled
   app id such as `"core "` (trailing space) is not equal to `"core"` in PHP
   yet the database folds it back to the core row, defeating the guard. This
   is the same defeat mechanism as OC10-5 (`core%81` truncation). Normalize
   the app id (cleanAppId + trim + strtolower) before the check, in both the
   controller (getValue/setValue/deleteKey) and the legacy core/ajax/appconfig.php
   endpoint, and block deleting the whole core appconfig via deleteApp.

Adds regression tests covering the mangled `core` spellings and the
allowed near-misses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@oc-tmueller
oc-tmueller force-pushed the fix/oc10-146-appconfig-path-traversal branch from 360833a to 8d71a3b Compare September 2, 2026 10:52
@oc-tmueller

Copy link
Copy Markdown

Closing in favour of a re-signed replacement opened under @oc-tmueller (same fix, commit re-authored and SSH-signed by that account). Replacement PR link to follow in a comment below.

@oc-tmueller

Copy link
Copy Markdown

Superseded by #41803 (same fix, commit re-authored and SSH-signed under @oc-tmueller).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants