# Gitea: OAuth2 access token scope enforcement bypass via HTTP Basic authentication

Gitea skipped OAuth2 scope checks when a token arrived via HTTP Basic auth, letting a read:user token perform writes and deletes as the victim.

- Payout: Research
- Severity: high
- Program: Gitea
- Found by (vendor credit): Alardiians
- Found by: Alardiians
- Disclosed: 2026-06-16
- CVE: CVE-2026-28699
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-9r5x-wg6m-x2rc
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-9r5x-wg6m-x2rc
- Tags: area:web domain:api domain:web impact:ato impact:integrity kind:research platform:ghsa sev:high tech:go technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Gitea API authentication middleware (services/auth/basic.go, routers/api/v1/api.go)
**Root cause:** The Basic auth path accepts OAuth2 access tokens but never sets ApiTokenScope in the request context; tokenRequiresScopes exits early when the scope key is absent, so no scope is enforced.
**Steps:**
1. Create an OAuth2 application in Gitea and authorize it as a normal user with scope `read:user` only.
2. Take the resulting access token and send it as Bearer: `Authorization: Bearer <token>` then `PATCH /api/v1/user/settings` -> 403 Forbidden.
3. Resend the same token via Basic: `Authorization: Basic base64(<token>:x-oauth-basic)` then `PATCH /api/v1/user/settings` -> 200 OK.
4. Repeat against `POST /api/v1/user/emails`, `POST /api/v1/user/repos`, `PATCH /api/v1/repos/{owner}/{repo}`, `DELETE /api/v1/repos/{owner}/{repo}` — all 403 via Bearer, 200 via Basic.
**How found:** Code review of the auth paths: noticed services/auth/basic.go sets IsApiToken but not ApiTokenScope, and that tokenRequiresScopes in routers/api/v1/api.go returns early when the scope key is missing, then confirmed by comparing Bearer vs Basic responses on write endpoints.
**Exploitation:** An attacker who gets a victim to authorize a malicious OAuth2 app with a minimal scope (e.g. read:user) reuses the issued token over HTTP Basic auth to call write endpoints, modifying profile/settings, adding attacker-controlled emails, and creating/modifying/deleting the victim's private repositories.
**Escalation:** Adding an attacker-controlled email to the victim account enables account takeover paths; repository write/delete extends impact beyond the granted read scope.
**Insight:** When two auth transports (Bearer vs Basic) feed the same token type, diff the context they populate — a missing scope/attribute key that triggers an early-return guard silently disables authorization; test every write endpoint with the alternate transport.

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

**Basic auth bypass request with read:user token**

```
Authorization: Basic base64(<token>:x-oauth-basic)
PATCH /api/v1/user/settings  ->  200 OK
```

**Bearer request correctly blocked by scope**

```
Authorization: Bearer <token>
PATCH /api/v1/user/settings  ->  403 Forbidden
```

