# Gitea: Authorization Bypass via "Allow edits from maintainers" allows unauthorized commits to any readable repo

Reverse-fork PR with allow_maintainer_edit lets any read-only Gitea user push commits to the upstream repo, bypassing write checks.

- Payout: Research
- Severity: high
- Program: Gitea
- Found by (vendor credit): ddd
- Found by: ddd
- Disclosed: 2026-06-16
- CVE: CVE-2026-26231
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-mm7c-rhg6-qr4r
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-mm7c-rhg6-qr4r
- Tags: area:web domain:web impact:integrity kind:research platform:ghsa sev:high tech:go technique:chained technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Gitea git HTTP/SSH push endpoint and PR-create web endpoint
**Root cause:** PR-create endpoint sets AllowMaintainerEdit=true without verifying write access to the HEAD repo, and CanMaintainerWriteToBranch only checks write access to the BASE (attacker's fork) repo, so the pre-receive hook authorizes pushes to upstream.
**Steps:**
1. Fork the target repository as attacker.
2. Create a PR via web compare endpoint with `BASE = attacker_fork`, `HEAD = upstream`, and `allow_maintainer_edit=true`.
3. Clone the fork, make a commit, run `git push <upstream_url> <branch>`.
4. Pre-receive hook calls CanMaintainerWriteToBranch, sees AllowMaintainerEdit=true and BASE write access, authorizes push to upstream.
**How found:** Code review of the githttp/serv push path showing access relaxed to Read when SupportProcReceive is enabled, then tracing the pre-receive hook into CanMaintainerWriteToBranch and noticing it checks BASE rather than HEAD repo ownership.
**Exploitation:** Attacker with only read access forks the repo, opens a reverse-fork PR (BASE=own fork, HEAD=upstream) with maintainer-edit enabled, then pushes arbitrary commits directly to the upstream repo over HTTP/SSH; the pre-receive hook validates against the attacker-owned BASE and accepts the push, landing a commit (e.g. PWNED.txt) on the target's default branch.
**Escalation:** Any authenticated user can backdoor any readable repo, including all public repos on the instance.
**Insight:** When a push-authorization hook trusts a PR flag, check which repo the flag's privilege is evaluated against versus which repo is being written to; reverse-fork PRs (BASE=attacker, HEAD=victim) invert the trust direction and are a recurring bypass pattern.

## Proof of concept (copied exactly from the original report)

**PoC script invocation against target repo**

```
python3 poc.py --repo http://gitea:3000/victim/repo --user attacker --password attacker_pass
```

**Unauthorized git push to upstream output**

```
remote: . Processing 1 references
remote: Processed 1 references in total
To http://192.168.101.20:3000/victim/my_repo.git
   e5c07b3..9a0b884  main -> main

[+] latest commit on victim/my_repo@main: 'PoC: unauthorized commit via maintainer-edit bypass'
[+] CONFIRMED: unauthorized push to upstream succeeded.
```

