{"id":"GHSA-gx4c-2hqx-cw2r","title":"rclone: S3 backend does not strip X-Amz-Security-Token on a same-host HTTPS->HTTP redirect","summary":"rclone: S3 backend does not strip X-Amz-Security-Token on a same-host HTTPS->HTTP redirect","severity":"low","cvss":3.1,"cwe":["CWE-319","CWE-522"],"vendor":"rclone","product":"github.com/rclone/rclone","ecosystem":"go","affected":["github.com/rclone/rclone <= 1.74.3"],"patched":["github.com/rclone/rclone 1.74.4"],"published":"2026-08-05","updated":"2026-08-05","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-gx4c-2hqx-cw2r","references":[{"url":"https://github.com/rclone/rclone/security/advisories/GHSA-gx4c-2hqx-cw2r"},{"url":"https://github.com/rclone/rclone/commit/1a28451ea6fc8ac1806b0e9923dcb5b3f543f7fa"},{"url":"https://github.com/rclone/rclone/releases/tag/v1.74.4"},{"url":"https://github.com/advisories/GHSA-gx4c-2hqx-cw2r"}],"tags":["ghsa","go"],"ingestedAt":"2026-08-05T20:51:27.803Z","slug":"GHSA-gx4c-2hqx-cw2r","body":"## Overview\n\n## Vulnerability Details\n\n**File**: `backend/s3/s3.go`\n**Lines**: 1359-1380 (functions `s3CheckRedirect` / `s3RedirectCrossesHost`)\n\n### Root Cause\nCommit `e7b1eb774` (released in v1.74.3) added a `CheckRedirect` policy for\nthe S3 HTTP client whose purpose is to strip the `X-Amz-Security-Token`\nheader (the AWS STS session token) whenever a redirect chain \"crosses a\nhost\", so the token isn't forwarded to an unintended origin.\n\n`s3RedirectCrossesHost` decides this purely by comparing `url.URL.Host`\n(hostname[:port]); it never looks at `url.URL.Scheme`. A redirect that keeps\nthe exact same host:port but changes the scheme from `https://` to `http://`\ntherefore compares as \"same host\" and `X-Amz-Security-Token` is *not*\nstripped — it is sent again, this time over plaintext HTTP.\n\n```go\nfunc s3RedirectCrossesHost(req *http.Request, via []*http.Request) bool {\n\tif len(via) == 0 {\n\t\treturn false\n\t}\n\thost := via[0].URL.Host\n\tfor _, redirect := range via[1:] {\n\t\tif redirect.URL.Host != host {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn host != req.URL.Host\n}\n```\n\n### Attack Scenario\n1. The user configures an `s3` remote (or `--s3-endpoint` pointing at a\n   self-hosted/third-party S3-compatible service) using temporary\n   credentials that include an STS `session_token` (common for assumed-role\n   / CI / Kubernetes IRSA setups).\n2. The configured endpoint responds to a request with a 3xx redirect to the\n   *same* host:port but with `http://` instead of `https://` (TLS-front\n   misconfiguration, maintenance redirect, or a malicious/compromised\n   storage provider trying to harvest the token).\n3. rclone's S3 HTTP client follows the redirect and re-sends the request,\n   including `X-Amz-Security-Token`, over the now-unencrypted connection to\n   that same host.\n4. Any passive observer on that now-plaintext network path can read the STS\n   session token from the request headers.\n\n### Impact\nDisclosure of the AWS STS session token (`X-Amz-Security-Token`) in\ncleartext for the remainder of its validity window. This is the exact class\nof leak that `e7b1eb774` was written to close — it just doesn't cover the\nscheme-downgrade axis of \"crossing a host\".\n\n### Vulnerable Code\n```go\nfunc s3RedirectCrossesHost(req *http.Request, via []*http.Request) bool {\n\tif len(via) == 0 {\n\t\treturn false\n\t}\n\thost := via[0].URL.Host\n\tfor _, redirect := range via[1:] {\n\t\tif redirect.URL.Host != host {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn host != req.URL.Host\n}\n```\n\n### Recommended Fix\nAlso compare `URL.Scheme`, so a scheme downgrade on the same host is treated\nthe same as a host change:\n\n```go\nfunc s3RedirectCrossesHost(req *http.Request, via []*http.Request) bool {\n\tif len(via) == 0 {\n\t\treturn false\n\t}\n\tscheme, host := via[0].URL.Scheme, via[0].URL.Host\n\tfor _, redirect := range via[1:] {\n\t\tif redirect.URL.Host != host || redirect.URL.Scheme != scheme {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn host != req.URL.Host || scheme != req.URL.Scheme\n}\n```\n\n### Verification\nAdded a unit test (`backend/s3/redirect_scheme_test.go`) that calls the real,\nunmodified `s3RedirectCrossesHost` / `s3CheckRedirect` with an\n`https://bucket.example.com` -> `http://bucket.example.com` redirect chain.\n\nOn unpatched code (commit 16091ce365, current master / v1.74.3):\n- `s3RedirectCrossesHost` returns `false`\n- `s3CheckRedirect` leaves `X-Amz-Security-Token: SECRET-SESSION-TOKEN`\n  intact on the outgoing (plaintext) request.\n\n```\n=== RUN   TestSchemeDowngradeNotDetectedAsCrossHost\n    redirect_scheme_test.go:23: initial=https://bucket.example.com final=http://bucket.example.com s3RedirectCrossesHost=false\n--- PASS: TestSchemeDowngradeNotDetectedAsCrossHost (0.00s)\n```\n\nAfter applying the one-line fix above (also adding scheme comparison), the\ntoken is correctly stripped and all existing redirect tests\n(`TestClientRemovesSecurityTokenOnCrossHostRedirect`,\n`TestClientDoesNotRestoreSecurityTokenAfterCrossHostRedirect`,\n`TestClientKeepsSecurityTokenOnSameHostRedirect`,\n`TestClientStopsAfterTenRedirects`) continue to pass.\n\nA minimal fix commit is ready and can be pushed to a private fork once this\nreport is acknowledged.\n\n## Affected packages\n\n- `github.com/rclone/rclone <= 1.74.3`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `github.com/rclone/rclone 1.74.4`","depth":"sunlit","depthScore":17,"depthScoreParts":{"impact":17.1,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}