{"id":"GHSA-hq88-5x99-x3gf","summary":"Cloudreve OAuth Admin.Read scope can update OneDrive storage policy credentials","details":"## Summary\n\nCloudreve 4.16.1 has an OAuth scope authorization bypass in the admin storage policy routes. An OAuth bearer token scoped to `Admin.Read` but not `Admin.Write` can call `POST /api/v4/admin/policy/oauth/signin` and update OneDrive storage policy credentials.\n\nThe route is inside the admin group that requires `Admin.Read`, but it does not add the local `Admin.Write` guard used by sibling policy mutation routes. Its handler persists attacker-supplied `secret` and `app_id` values into the selected OneDrive storage policy before returning an OAuth URL.\n\n## Impact\n\nAn OAuth application that was granted only read-only admin scope can modify persistent storage backend configuration for a OneDrive policy. This can break the storage backend, replace the stored application secret and app ID, and redirect future OAuth setup for that policy to attacker-controlled application parameters. The attack crosses the intended OAuth scope boundary because `Admin.Write` is required for sibling storage policy mutation routes.\n\n## Reproduction\n\nPreconditions:\n\n1. The instance has a OneDrive storage policy.\n2. An admin user authorizes an OAuth client for `Admin.Read` but not `Admin.Write`.\n3. The OAuth client obtains a bearer access token for that admin user.\n\nSend the following request with that read-only admin scoped token:\n\n```http\nPOST /api/v4/admin/policy/oauth/signin HTTP/1.1\nAuthorization: Bearer \u003cadmin OAuth token scoped to Admin.Read only\u003e\nContent-Type: application/json\n\n{\"id\":1,\"secret\":\"attacker-secret\",\"app_id\":\"attacker-app-id\"}\n```\n\nExpected secure result: the request is rejected with an insufficient-scope error because it changes storage policy credentials.\n\nActual result: the request reaches `AdminOdOAuthURL`, and `GetOauthRedirectService.GetOAuth()` persists the supplied values to the storage policy.\n\n## Root cause\n\n`routers/router.go` applies `RequiredScopes(types.ScopeAdminRead)` to the authenticated admin route group. Sibling policy mutation routes add local `RequiredScopes(types.ScopeAdminWrite)` guards, for example policy create, policy update, CORS creation, OAuth callback, and policy delete.\n\nThe OneDrive OAuth signin route is missing that local write-scope guard:\n\n```go\noauth.POST(\"signin\",\n    controllers.FromJSON[adminsvc.GetOauthRedirectService](adminsvc.GetOauthRedirectParamCtx{}),\n    controllers.AdminOdOAuthURL,\n)\n```\n\nThe handler performs a persistent write in `service/admin/policy.go`:\n\n```go\npolicy.Settings.OauthRedirect = routes.MasterPolicyOAuthCallback(dep.SettingProvider().SiteURL(c)).String()\npolicy.SecretKey = service.Secret\npolicy.BucketName = service.AppID\npolicy, err = storagePolicyClient.Upsert(c, policy)\n```\n\nThe request fields `secret` and `app_id` come directly from the caller.\n\n## PoC evidence\n\nA focused scope test confirmed that a request context with only `Admin.Read` fails `CheckScope(c, types.ScopeAdminWrite)`, while `Admin.Write` implies `Admin.Read`. Therefore write routes must add `RequiredScopes(types.ScopeAdminWrite)` explicitly. The vulnerable route does not do so, and the handler writes the policy fields shown above.\n\n## Remediation\n\nAdd `middleware.RequiredScopes(types.ScopeAdminWrite)` to `oauth.POST(\"signin\", ...)` before the JSON handler. Consider auditing other admin test routes that perform outbound network or mail actions under only `Admin.Read`, but this persistent credential update should be fixed first.","aliases":["CVE-2026-55502","GO-2026-6101"],"modified":"2026-08-18T15:10:58.079748003Z","published":"2026-07-24T20:49:14Z","database_specific":{"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-07-24T20:49:14Z","nvd_published_at":null,"cwe_ids":["CWE-863"]},"references":[{"type":"WEB","url":"https://github.com/cloudreve/cloudreve/security/advisories/GHSA-hq88-5x99-x3gf"},{"type":"WEB","url":"https://github.com/cloudreve/cloudreve/commit/9e9fb43e7288924cca052e5fdbb70d5365ef1ede"},{"type":"PACKAGE","url":"https://github.com/cloudreve/cloudreve"},{"type":"WEB","url":"https://github.com/cloudreve/cloudreve/releases/tag/4.17.0"}],"affected":[{"package":{"name":"github.com/cloudreve/Cloudreve/v4","ecosystem":"Go","purl":"pkg:golang/github.com/cloudreve/Cloudreve/v4"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"4.17.0"}]}],"database_specific":{"last_known_affected_version_range":"\u003c 4.0.0-20260613030954-9e9fb43e7288","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-hq88-5x99-x3gf/GHSA-hq88-5x99-x3gf.json"}},{"package":{"name":"github.com/cloudreve/Cloudreve/v3","ecosystem":"Go","purl":"pkg:golang/github.com/cloudreve/Cloudreve/v3"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"3.0.0-20250225100611-da4e44b77af4"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-hq88-5x99-x3gf/GHSA-hq88-5x99-x3gf.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L"}]}