# Cloudreve OAuth Admin.Read scope can update OneDrive storage policy credentials

Cloudreve's OneDrive OAuth signin route lacked an Admin.Write scope guard, letting Admin.Read-only OAuth tokens overwrite storage policy credentials.

- Payout: Research
- Severity: high
- Program: Cloudreve
- Found by: DavidCarliez
- Disclosed: 2026-07-24
- CVE: CVE-2026-55502
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-hq88-5x99-x3gf
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-hq88-5x99-x3gf
- Tags: area:web domain:api domain:web impact:integrity kind:research platform:ghsa sev:high tech:go tech:oauth technique:source-review vuln:access-control vuln:auth-bypass vuln:misconfiguration

**Target:** Admin storage policy OAuth signin endpoint (POST /api/v4/admin/policy/oauth/signin)
**Root cause:** The admin route group only enforces RequiredScopes(Admin.Read); the OneDrive OAuth signin route omits the local RequiredScopes(Admin.Write) guard that sibling policy mutation routes add, so a read-only scoped token reaches a handler that persists caller-supplied secret and app_id into the storage policy.
**Steps:**
1. Obtain an OAuth bearer token for an admin user scoped to `Admin.Read` only (no `Admin.Write`).
2. Send `POST /api/v4/admin/policy/oauth/signin` with `Authorization: Bearer <admin OAuth token scoped to Admin.Read only>` and JSON body `{"id":1,"secret":"attacker-secret","app_id":"attacker-app-id"}`.
3. Request reaches `AdminOdOAuthURL`; `GetOauthRedirectService.GetOAuth()` writes `policy.SecretKey = service.Secret` and `policy.BucketName = service.AppID` then `Upsert`s the policy.
**How found:** Compared sibling policy mutation routes (create, update, CORS, OAuth callback, delete) which each add a local Admin.Write scope guard against the signin route, which lacked it; a focused scope test confirmed Admin.Read alone fails CheckScope(Admin.Write) while Admin.Write implies Admin.Read, so write routes need the explicit guard.
**Exploitation:** A read-only-scoped OAuth client of an admin user sends one POST to the signin route, overwriting the OneDrive policy's stored secret and app ID and redirect URI, breaking the backend and redirecting future OAuth setup to attacker-controlled app parameters.
**Insight:** When an API uses hierarchical scopes (Write implies Read), the broad group-level Read guard silently authorizes every route; enumerate routes in that group and diff which ones add the narrower write guard — any handler that persists caller input is a candidate.

## Proof of concept (copied exactly from the original report)

**Read-only OAuth token updates OneDrive policy credentials**

```
POST /api/v4/admin/policy/oauth/signin HTTP/1.1
Authorization: Bearer <admin OAuth token scoped to Admin.Read only>
Content-Type: application/json

{"id":1,"secret":"attacker-secret","app_id":"attacker-app-id"}
```

