All vulnerabilities
GHSA-3rqf-7h69-gg5v

github.com/rclone/rclone: rclone WebDAV backend leaks custom credential headers to a different host on redirect

github.com/rclone/rclone
CVSS
3.7Low
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed inNone listedNo source names a fixed version yet
Published9 Oct 2026Updated 9 Oct 2026GitHub

What fixes it

No source in this snapshot names a fixed version.

Description

Summary

The WebDAV backend forwards operator-configured custom credential headers to a different host when the configured server responds with an HTTP 3xx redirect. When a WebDAV remote is created with the headers option (--webdav-headers), each configured header is installed on the backend's shared HTTP client and attached to every outgoing request. If the server returns a cross-host redirect, rclone follows it and re-sends the request, including the operator's custom header, to the new host. Go's standard library strips only four well-known sensitive header names (Authorization, Www-Authenticate, Cookie, Cookie2) and only across a different registrable domain, so a custom-named credential header such as X-Auth-Token or X-Api-Key is disclosed verbatim to the redirect target. An attacker who can influence the server's redirect response (an open redirect, a compromised or malicious endpoint, or an on-path position for a plaintext or same-host hop) harvests the operator's credential and can reuse it against the original service.

Root Cause

The WebDAV backend violates the invariant that operator-configured secret headers must not leave the configured host on a redirect. The backend installs each configured header onto the shared rest.Client via SetHeader, and those per-client headers are copied onto every request. The client's redirect handlers refuse only an HTTPS to HTTP downgrade; they delete no headers. A separate transport-level strip that does remove headers on a host-leaving redirect operates only over the global --header option set, not over per-client headers installed by a backend, so it never sees the WebDAV credential header. The same-backend fix that the project applied to the HTTP backend (deleting each configured header on a host-leaving redirect) and to the S3 backend was never propagated to WebDAV.

backend/webdav/webdav.go:
  NewFs                -> client.CheckRedirect = rest.RefuseHTTPSDowngradeRedirectFn (line 524)
  NewFs                -> addHeaders(opt.Headers)                                   (lines 540-542)
  (*Fs).addHeaders     -> f.srv.SetHeader(key, value)                              (lines 604-609)
lib/rest/rest.go:
  RefuseHTTPSDowngradeRedirectFn (GET/Open)  -> refuses downgrade only, no Header.Del (lines 283-291)
  PreserveMethodRedirectFn       (PROPFIND)  -> refuses downgrade only, no Header.Del (lines 267-278)
  (*Client).Call    -> req.Header.Add(k, v) for each stored header                  (lines 404-414)
fs/fshttp/http.go:
  Transport.headers = ci.Headers  (global --header only)                            (line 364)
  Transport.RoundTrip strips only t.headers on redirectLeavesHost                   (lines 623-631)

Impact

An operator who configures a custom credential header on a WebDAV remote (the documented mechanism for auth material, per the headers option help) leaks that credential to an unintended host whenever the configured server emits a cross-host redirect. The disclosed value is a full, reusable credential header (CWE-200, CWE-522; CWE-319 for a plaintext hop). The attacker prerequisite is the ability to influence the server's redirect response, which is realistic for an open redirect, a compromised or malicious endpoint, or an on-path position on a cleartext or same-host hop. Confidentiality impact is high because a complete credential is disclosed. As a plausible downstream consequence, the recovered credential can be replayed to gain authenticated write access to the victim's WebDAV account; this is a credential-reuse consequence, not a directly demonstrated write in the reviewed evidence. The default configuration is affected: the leak does not depend on the opt-in auth_redirect option.

Proof of Concept

Requires a WebDAV remote configured with a custom credential header and a server that issues a cross-host, non-downgrade redirect. The Go toolchain was unavailable in the review environment, so the accompanying Go tests were reviewed against the source trace and Go net/http redirect-copy semantics rather than executed.

Reproduction steps:

  1. Start two HTTP(S) listeners on different hostnames (for example host-a.example and host-b.example). host-a answers a WebDAV PROPFIND or GET with a 302/307 redirect to the same path on host-b; host-b records the request headers it receives.
  2. Configure a WebDAV remote pointing at host-a with a custom credential header, for example --webdav-headers "X-Auth-Token,SECRET-CREDENTIAL".
  3. Run any listing or read operation (for example rclone lsd webdavremote: or rclone cat webdavremote:file).
  4. Observe that host-b received X-Auth-Token: SECRET...AL, while a standard Authorization header (if also set) is stripped by Go on the cross-domain hop.
host-b received headers:
  X-Auth-Token: ***      <- leaked custom credential header
  (Authorization: ***              <- stripped by Go across registrable domains

Attack Chain

  1. Exposure: The operator sets a custom credential header via the documented headers option on a WebDAV remote whose URL commonly points at a third-party host. NewFs calls addHeaders, which stores the header on the shared rest.Client through SetHeader (backend/webdav/webdav.go:540-542,604-609).
  2. Control: The victim supplies the credential value; the attacker supplies or influences the redirect target (open redirect, compromised or malicious server, or on-path for a cleartext or same-host hop).
  3. Path: rest.Client.Call copies the stored header onto every request via req.Header.Add (lib/rest/rest.go:404-414), covering PROPFIND (List and metadata) and GET (Object.Open). The configured server returns a cross-host 3xx and Go follows it, copying the custom-named header to the new hop.
  4. Guard: The WebDAV redirect handlers RefuseHTTPSDowngradeRedirectFn and PreserveMethodRedirectFn refuse only an HTTPS to HTTP downgrade and delete no headers (lib/rest/rest.go:267-291). The transport-level strip in RoundTrip iterates only the global-header set t.headers, bound to ci.Headers at construction (fs/fshttp/http.go:364,623-631), so it cannot delete the per-client WebDAV header.
  5. Primitive: The outbound request to the attacker-influenced host carries the operator's custom credential header.
  6. Result: The credential is disclosed cross-host and can be replayed against the original WebDAV service.

Bypass Evidence

This is an incomplete-fix and unfixed-sibling-backend condition relative to CVE-2026-88013 (GHSA-486v-q2wf-fp2r), which fixed exactly this leak in the HTTP backend, and relative to GHSA-h4mf-4v27-hggj, which fixed only the WebDAV same-host HTTPS to HTTP downgrade case.

Disproof attempts, each performed against the source at the examined revision:

  1. Hypothesis: the transport strip removes the header. Refuted. Transport.headers is set to ci.Headers (the global --header flag) at fs/fshttp/http.go:364, and the redirect branch of RoundTrip deletes only entries in t.headers. The WebDAV header is installed via rest.Client.SetHeader and lives in the per-client header map, which the loop never iterates, so it is not deleted.
  2. Hypothesis: the WebDAV redirect handlers delete the header. Refuted. Both handlers, read in full at lib/rest/rest.go:267-291, only cap the redirect count and refuse a downgrade; neither calls Header.Del. A cross-host non-downgrade redirect is followed with the header intact.
  3. Hypothesis: this is a duplicate of the HTTP-backend fix or of the WebDAV downgrade fix. Refuted. The HTTP-backend fix lives only at backend/http/http.go:528 (checkRedirect deletes each configured header on a host-leaving hop); the WebDAV backend has no header-deleting redirect handler. The WebDAV downgrade fix addresses only a same-host HTTPS to HTTP downgrade and does nothing for a cross-host, non-downgrade redirect of a custom-named header.
  4. Hypothesis: it requires disabling a security control, so it is not a real issue. Refuted. Setting a credential header is the documented mechanism for WebDAV auth material (backend/webdav/webdav.go:127-138), not the disabling of a control. The leak occurs in default configuration and does not depend on the opt-in auth_redirect option (default false, backend/webdav/webdav.go:189).

Independent basis for the leak, beyond the reviewed tests: Go's net/http redirect copier strips only Authorization, Www-Authenticate, Cookie, and Cookie2, and only across a different registrable domain (hostname-based, port ignored). Custom header names are copied to any redirect target unconditionally.

Affected Versions

  • Ecosystem: go
  • Package: github.com/rclone/rclone
  • Confirmed affected range: <= v1.75.1
  • Latest release checked: v1.75.1 (canonical Go module proxy latest; released 2026-09-04)
  • Fix status: not fixed as of the latest published release

The vulnerable code was verified present in the exact published v1.75.1 module artifact (the current canonical Go proxy latest release): the WebDAV backend wires RefuseHTTPSDowngradeRedirectFn/PreserveMethodRedirectFn and calls addHeaders with no host-change header strip, and the transport strip still covers only the global header set. No released fix exists for this cross-host custom-header vector. The introduction boundary could not be proven against every earlier released artifact: the reviewed local checkout is post-release development (its VERSION marker names an unreleased v1.76.0) with squashed history and no tags, so the earliest affected release was not established. The WebDAV headers option and both redirect handlers are unchanged by the HTTP-backend fix, so the leak plausibly affects every release line, but that lower boundary is not proven against released module artifacts. The confirmed affected range is therefore stated with an open lower bound and an incomplete-confidence introduction boundary.

Suggested Fix

Enforce the same header-stripping invariant for WebDAV that the HTTP and S3 backends already enforce: on any redirect hop that leaves the configured host (comparing scheme, hostname, and effective port, as rest.SameHost already does), delete every operator-configured header before the request is re-sent. The most direct approach is a WebDAV CheckRedirect that both refuses the HTTPS to HTTP downgrade (preserving the current behavior) and deletes each configured header name when the hop leaves the host, mirroring backend/http/http.go checkRedirect. Because per-client headers installed via rest.Client.SetHeader are invisible to the global transport strip, the fix must live in the backend or in the lib/rest redirect handler, not in the global fs/fshttp transport. As an interim mitigation, operators can avoid placing reusable secret headers on WebDAV remotes that point at servers capable of redirecting to other hosts, and can pin remotes to endpoints that do not emit cross-host redirects.

---

Reported by zx (GitHub: @manus-pi).

Text from GitHub Advisory Database

Affected versions 1

  • github.com/rclone/rclone, go>= v1.56.0, <= v1.75.1GitHub