A AegiFlow
HIGHCVSS 8.8EPSS 0.5%

CVE-2026-27775

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

Published
2026-07-21
Modified
2026-07-21
EPSS percentile
41%
Aliases
GHSA-649p-mmhf-85c7
Sources
github-advisory

Summary

## Vulnerability Header | Field | Value | | ------------------- | ----------------------------------------------------------------------------------- | | Vulnerability Title | Cached Per-Branch Permission Check in Pre-Receive Hook Allows Full Repository Write | | Severity Rating | High | | Bug Category | Authorization Bypass | | Location | `routers/private/hook_pre_receive.go:55-64`, `CanWriteCode()` | | Affected Versions | 1.25.5 | ## Executive Summary The pre-receive hook in Gitea evaluates the `CanMaintainerWriteToBranch` permission only once per `git push` session and caches the result for all subsequent refs in the same batch. An attacker who has a legitimate per-branch write grant (e.g., via an open pull request with "Allow edits from maintainers" enabled) can batch-push that branch together with any other ref. The cached `true` from the first ref is reused for all following refs, allowing the attacker to overwrite protected branches (including `main`), create arbitrary new branches, and push tags. This effectively escalates a single-branch maintainer-edit grant into full repository write access. ## Root Cause Analysis ### Technical Description When processing a multi-ref `git push`, the `HookPreReceive` handler at `hook_pre_receive.go:107` iterates over all incoming refs. For each branch ref, `preReceiveBranch` (`:140`) updates `ctx.branchName` to the current branch (`:142`) and then calls `AssertCanWriteCode()` (`:144`). `CanWriteCode()` (`:55-64`) checks whether the user can write to the repository. On the first call, it evaluates `issues_model.CanMaintainerWriteToBranch(ctx, userPerm, ctx.branchName, user)` and stores the result in a boolean flag (`canWriteCode`) with a guard (`checkedCanWriteCode`). On all subsequent calls within the same batch, it returns the cached boolean without re-evaluating against the now-different `ctx.branchName`. This means the permission check is branch-specific in its inputs but session-scoped in its caching — a classic check-vs-use divergence. A second contributing factor is the AGit-flow relaxation at `routers/web/repo/githttp.go:190-192` (and `routers/private/serv.go:337-338`), which downgrades the outer `receive-pack` access gate from `Write` to `Read` when `git.DefaultFeatures().SupportProcReceive` is true (git ≥ 2.29). This allows a user with only Read access on a repository to initiate a `receive-pack` session, deferring all authorization to the pre-receive hook — which contains the caching bug described above. ### First Faulty Condition | File | `routers/private/hook_pre_receive.go` | | --------- | ------------------------------------- | | Line | 55-64 | | Condition | `CanWriteCode()` evaluates the branch-specific `CanMaintainerWriteToBranch` check only on the first invocation and caches the result, reusing it for all subsequent refs in the batch regardless of which branch they target. | ### Trace Analysis The following is the path from the attacker's `git push` to the authorization fault: 1. `POST /{owner}/{repo}.git/git-receive-pack` → `routers/web/repo/githttp.go:437` (`ServiceReceivePack`) → `httpBase()` (`:60`) - Access gate is downgraded from Write to Read at `:190-192` due to AGit-flow support. 2. `git receive-pack` invokes the pre-receive hook → `cmd/hook.go:184` (`runHookPreReceive`) → `modules/private/hook.go:96` (`HookPreReceive`) → internal API → `routers/private/hook_pre_receive.go:107` (`HookPreReceive`) 3. Loop at `:117` iterates over all refs in the batch. For each branch ref, `preReceiveBranch` (`:140`) sets `ctx.branchName` at `:142`. 4. **Fault**: `A

Affected packages

EcosystemPackageAffected versionsFixed versions
Gocode.gitea.io/gitea1.26.3

Remediation: Upgrade to 1.26.3 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.

CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.

EPSS scores provided by the FIRST.org Exploit Prediction Scoring System.