Skip to main content

Gitea CVE-2026-28744

HIGH
Incorrect Authorization (CWE-863)
2026-06-16 https://github.com/go-gitea/gitea GHSA-cc8w-r4qh-3v65
8.1
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
8.1 HIGH
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
vuln.today AI
8.1 HIGH

Exploitation requires any valid user token (PR:L), is network-reachable with a single header swap (AV:N/AC:L), no UI, and grants private repo read and write (C:H/I:H, A:N).

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N

Primary rating from GitHub Advisory.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
Jun 17, 2026 - 00:15 vuln.today
Analysis Generated
Jun 17, 2026 - 00:15 vuln.today
CVE Published
Jun 16, 2026 - 23:38 github-advisory
HIGH 8.1

DescriptionGitHub Advisory

Summary

Gitea v1.26.1 enforces repository-scoped access-token permissions on repository operations. In the Git Smart HTTP path, however, this check runs only when the token is presented via HTTP Basic authentication - CheckRepoScopedToken() returns early unless ctx.IsBasicAuth is true - so the same token sent as Authorization: Bearer <token> bypasses the scope check entirely.

As a result, a PAT or OAuth2 token presented as a Bearer credential can clone or fetch private repositories without the read:repository scope, and likewise reach the Git push without write:repository.

Details

Git Smart HTTP routes allow both Basic auth and OAuth2/Bearer auth:

go
// routers/web/web.go
addOwnerRepoGitHTTPRouters(
	m,
	repo.HTTPGitEnabledHandler,
	webAuth.AllowBasic,
	webAuth.AllowOAuth2,
	repo.CorsHandler(),
	optSignInFromAnyOrigin,
	context.UserAssignmentWeb(),
)

The Git HTTP authorization path calls CheckRepoScopedToken() before falling through to normal repository RBAC:

go
// routers/web/repo/githttp.go
if askAuth {
	if !ctx.IsSigned {
		ctx.HTTPError(http.StatusUnauthorized)
		return nil
	}

	context.CheckRepoScopedToken(ctx, repo, auth_model.GetScopeLevelFromAccessMode(accessMode))
	if ctx.Written() {
		return nil
	}

	// normal repository RBAC follows
}

However, CheckRepoScopedToken() only enforces token scopes for Basic-authenticated requests:

go
// services/context/permission.go
func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_model.AccessTokenScopeLevel) {
	if !ctx.IsBasicAuth || ctx.Data["IsApiToken"] != true {
		return
	}

	scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
	if ok {
		requiredScopes := auth_model.GetRequiredScopes(level, auth_model.AccessTokenScopeCategoryRepository)
		// public-only and required repository scope checks follow
	}
}

The Bearer/OAuth2 auth path still records the token scope:

go
// services/auth/oauth2.go
accessTokenScope, uid := GetOAuthAccessTokenScopeAndUserID(ctx, tokenSHA)
if uid != 0 {
	store.GetData()["IsApiToken"] = true
	store.GetData()["ApiTokenScope"] = accessTokenScope
}

Bearer PATs also set IsApiToken=true and ApiTokenScope, but ctx.IsBasicAuth remains false because the selected auth method is OAuth2/Bearer rather than Basic. The scope is therefore available but ignored.

PoC

This test creates a token for user2 with only read:notification, then requests Git Smart HTTP refs for user2/repo2, which is private. The same token is rejected over Basic auth, but succeeds over Bearer auth.

go
func TestPOCGitSmartHTTPBearerTokenBypassesRepositoryScope(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerName: "user2", Name: "repo2"})
	assert.True(t, repo.IsPrivate)

	session := loginUser(t, "user2")
	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadNotification)
	url := "/user2/repo2/info/refs?service=git-upload-pack"

	basicReq := NewRequest(t, "GET", url)
	basicReq.SetBasicAuth(token, "x-oauth-basic")
	MakeRequest(t, basicReq, http.StatusForbidden)

	bearerReq := NewRequest(t, "GET", url).AddTokenAuth(token)
	resp := MakeRequest(t, bearerReq, http.StatusOK)
	assert.Contains(t, resp.Body.String(), "refs/heads/master")
}

Impact

Any Gitea instance exposing Git Smart HTTP is affected when users use PATs or OAuth2 tokens as Bearer tokens. The attacker still needs a token for a user who has normal repository RBAC, so this does not grant access to repositories the token owner could not otherwise access.

The vulnerability breaks the access-token scope boundary. A token intended only for unrelated scopes, such as read:notification, can clone or fetch private repository contents over Git Smart HTTP. The same root cause can affect write flows because git-receive-pack also calls the same repository scope check before normal write RBAC.

AnalysisAI

Authorization scope bypass in Gitea v1.26.1 and earlier allows authenticated users to use OAuth2/PAT Bearer tokens to perform Git Smart HTTP clone, fetch, and push operations on private repositories without holding the required read:repository or write:repository token scopes. The flaw stems from CheckRepoScopedToken() short-circuiting unless ctx.IsBasicAuth is true, while the same route accepts Bearer authentication. …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Access
Obtain low-scope PAT or OAuth2 token
Delivery
Identify target private repo where token owner has RBAC
Exploit
Send Git Smart HTTP request with Authorization: Bearer header
Execution
Bypass CheckRepoScopedToken scope enforcement
Persist
Clone/fetch private repo or push via git-receive-pack
Impact
Exfiltrate source or tamper with repository contents

Vulnerability AssessmentAI

Exploitation Attacker must possess a valid Gitea PAT or OAuth2 access token for an account that already has normal RBAC (read or write) on the target repository - the bug bypasses token scope, not user permissions. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment CVSS 3.1 base score is 8.1 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N), reflecting network reach, low complexity, low privileges (a valid token of any scope) and high confidentiality and integrity impact. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario An attacker who has obtained a low-privilege PAT - for example a token issued for read:notification to a chatops integration, a leaked CI token, or a token phished from a developer - sends `git clone https://gitea.example.com/victim/private-repo.git` with `Authorization: Bearer <token>` (or equivalent `git -c http.extraHeader='Authorization: Bearer …'`) and successfully clones the private repository despite the token lacking read:repository. The same technique against /info/refs?service=git-receive-pack enables push if the user owns write RBAC on the repo. …
Remediation Vendor-released patch: upgrade to Gitea 1.26.2 or later, per GHSA-cc8w-r4qh-3v65 (https://github.com/go-gitea/gitea/security/advisories/GHSA-cc8w-r4qh-3v65). … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours: Identify all Gitea instances running v1.26.1 or earlier; audit OAuth2/PAT token scope assignments and Git Smart HTTP access logs for unauthorized private repository access. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

Share

CVE-2026-28744 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy