{"id":"CVE-2026-28699","title":"Gitea: OAuth2 access token scope enforcement bypass via HTTP Basic authentication","summary":"Gitea: OAuth2 access token scope enforcement bypass via HTTP Basic authentication","severity":"high","cvss":8.1,"cwe":["CWE-284","CWE-863"],"vendor":"gitea","product":"code.gitea.io/gitea","ecosystem":"go","affected":["code.gitea.io/gitea <= 1.26.1"],"patched":["code.gitea.io/gitea 1.26.2"],"published":"2026-06-16","updated":"2026-06-16","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-9r5x-wg6m-x2rc","references":[{"url":"https://github.com/go-gitea/gitea/security/advisories/GHSA-9r5x-wg6m-x2rc"},{"url":"https://github.com/advisories/GHSA-9r5x-wg6m-x2rc"}],"tags":["ghsa","go","exploit-available"],"ingestedAt":"2026-06-29T14:31:47.459Z","epss":0.00551,"epssPercentile":0.44716,"exploits":{"github":1,"githubRepos":["https://github.com/Alardiians/gitea-CVE-2026-28699"],"checkedAt":"2026-09-21T15:28:37.742Z"},"exploitAvailable":true,"slug":"CVE-2026-28699","body":"## Overview\n\n### Summary\n\nGitea fails to enforce OAuth2 access token scopes when the token is submitted via HTTP Basic authentication instead of a Bearer token. An OAuth2 application granted only `read:user` can use the same token as `Authorization: Basic base64(<token>:x-oauth-basic)` and perform write actions, including modifying profiles, adding email addresses, creating repositories, and deleting repositories as the authorizing user.\n\n### Details\n\n**Root cause:** `services/auth/basic.go` accepts OAuth2 access tokens through the Basic auth path but does not store the token scope in the request context:\n\n```go\n// services/auth/basic.go\nif uid != 0 {\n    store.GetData()[\"LoginMethod\"] = OAuth2TokenMethodName\n    store.GetData()[\"IsApiToken\"] = true   // scope is NOT set\n    return u, nil\n}\n```\n\nThe scope enforcement middleware in `routers/api/v1/api.go` exits early when `ApiTokenScope` is absent:\n\n```go\n// routers/api/v1/api.go — tokenRequiresScopes\nscope, scopeExists := ctx.Data[\"ApiTokenScope\"].(auth_model.AccessTokenScope)\nif ctx.Data[\"IsApiToken\"] != true || !scopeExists {\n    return   //<- exits without checking scope, all actions permitted\n}\n```\n\nWhen a token arrives via Bearer, `ApiTokenScope` is populated and scope checks apply normally. When the same token arrives via Basic auth, `ApiTokenScope` is never set, so `tokenRequiresScopes` returns immediately and no scope is enforced.\n\n**Suggested fix:** When an OAuth2 access token is accepted in `services/auth/basic.go`, populate `ApiTokenScope` in the request context identically to the Bearer-token OAuth2 path.\n\n### PoC\n\n1. Create an OAuth2 application in Gitea.\n2. Authorize it as a normal user with scope `read:user` only.\n3. Take the resulting access token and call a write endpoint both ways:\n\n**Bearer | correctly blocked:**\n```\nAuthorization: Bearer <token>\nPATCH /api/v1/user/settings  ->  403 Forbidden\n```\n\n**Basic | bypass:**\n```\nAuthorization: Basic base64(<token>:x-oauth-basic)\nPATCH /api/v1/user/settings  ->  200 OK\n```\n\n**All verified bypass endpoints using a `read:user`-only token:**\n\n| Endpoint | Bearer | Basic |\n|---|---|---|\n| `PATCH /api/v1/user/settings` | 403 | 200 |\n| `POST /api/v1/user/emails` | 403 | 200 |\n| `POST /api/v1/user/repos` | 403 | 200 |\n| `PATCH /api/v1/repos/{owner}/{repo}` | 403 | 200 |\n| `DELETE /api/v1/repos/{owner}/{repo}` | 403 | 200 |\n\nThe bypass respects the user's normal repository permissions, it does not grant access to repositories the user cannot otherwise reach, and does not escalate to admin.\n\n### Impact\n\nAny OAuth2 application with any restricted scope can silently operate beyond its granted permissions by switching from Bearer to Basic auth. An attacker who obtains a token (e.g. via a malicious OAuth2 app a user authorized) can:\n\n- Modify the victim's profile and settings\n- Add attacker-controlled email addresses to the victim's account\n- Create repositories as the victim\n- Modify or delete the victim's private repositories\n\nThe entire OAuth2 scope system is effectively bypassed for any token submitted via Basic auth.\n\n## Affected packages\n\n- `code.gitea.io/gitea <= 1.26.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `code.gitea.io/gitea 1.26.2`","depth":"midnight","depthScore":57,"depthScoreParts":{"impact":44.6,"likelihood":0.1,"exploitation":12,"ransomware":0},"changes":[{"seq":5061,"id":"CVE-2026-28699","ts":1788887234492,"field":"exploit_available","old":"false","new":"true"},{"seq":3944,"id":"CVE-2026-28699","ts":1788886362026,"field":"exploit_available","old":"true","new":"false"},{"seq":2765,"id":"CVE-2026-28699","ts":1788883028725,"field":"exploit_available","old":"false","new":"true"},{"seq":1794,"id":"CVE-2026-28699","ts":1788882431771,"field":"exploit_available","old":"true","new":"false"},{"seq":900,"id":"CVE-2026-28699","ts":1788881865693,"field":"exploit_available","old":"false","new":"true"}]}