Someone left the keys in the ignition of the factory that builds all your software
CVE-2026-60004 is a code injection flaw in Gitea's diffpatch API endpoint (POST /api/v1/repos/{owner}/{repo}/diffpatch) affecting versions 1.17.0 through 1.27.0. A crafted patch payload triggers an add/add collision in Git's three-way merge logic, causing attacker-controlled content to materialize as an executable Git hook (e.g., post-receive) inside the server-side repository directory. When Git performs any hook-triggering operation, the injected script runs as the Gitea OS service account — typically git or gitea. Because Gitea ships with self-registration enabled by default, an unauthenticated attacker can register an account, create a repository, and achieve full RCE in approximately 11 seconds. Gitea patched this in v1.27.1 on July 27, 2026.
The vendor's CRITICAL / 9.8 rating is fully justified and may even understate operational risk. The CVSS vector marks PR:N (no privileges required), which is accurate for default configurations where open sign-up converts anonymous internet access into authenticated write access in one API call. Real-world exploitation has already graduated from opportunistic crypto-mining to state-aligned APT operations: Acronis documented the Red Heron campaign weaponizing this flaw within 48 hours of PoC publication, compromising 11 organizations across defense, government, and energy sectors in five countries. Shadowserver counted 8,393 vulnerable internet-facing instances as of August 27, 2026. This is not a theoretical risk — it is an active emergency.
5 steps from start to impact.
Reach the Gitea HTTP endpoint
X-Gitea-Version response header. Shodan, FOFA, and Censys dorks return thousands of results. Internal instances are reachable from any host on the same network segment.- Network access to Gitea HTTP port (default 3000)
- Internal-only instances require pre-existing network access — but any developer workstation or CI runner on the same VLAN qualifies
CVE-2026-60004.yaml from projectdiscovery detects vulnerable instances. runZero query vendor:=Gitea AND product:=Gitea enumerates assets.Obtain write access via self-registration
/user/sign_up or the API, then creates a new repository. This grants the write access prerequisite. The PoC by imbas007 automates this end-to-end in full-auto mode. If registration is disabled, any compromised developer credential or leaked CI token with repo:write scope suffices.- Open self-registration (Gitea default) OR any valid credential with repo write access
- Instances with
DISABLE_REGISTRATION = truerequire credential theft — but this is not the default
Submit malicious diffpatch payload
POST /api/v1/repos/{owner}/{repo}/diffpatch request containing a patch that induces an add/add collision. Gitea's backend creates a temporary bare clone and applies the patch. Due to the bare-clone behavior, the merge fallback writes attacker-controlled content into a path that resolves as a Git hook (e.g., hooks/post-receive). The file is created with execute permissions. The weaponized PoC (cve-2026-60004-poc.py) handles all encoding and collision crafting automatically.- Write access to at least one repository
- Vulnerable Gitea version (1.17.0–1.27.0)
- None — the exploit is reliable and deterministic against vulnerable versions
/api/v1/repos/*/diffpatch with anomalous patch content. Log analysis: look for diffpatch API calls from newly registered accounts.Git hook executes attacker payload
.ssh/authorized_keys file.- A Git operation triggers the hook (attacker can force this immediately)
- None — the attacker controls the trigger
/etc/ld.so.preload, outbound connections from gitea process, new files in /tmp/.X11-unix*.Post-exploitation: credential harvest and supply-chain pivot
app.ini to extract database credentials, JWT signing secrets, and OAuth client secrets. They can inject backdoors into any hosted repository's source code — a supply-chain attack affecting every downstream build that pulls from this forge. The Red Heron campaign exfiltrated repositories and pivoted laterally to Proxmox hypervisors in at least one victim environment. The SIXZUT LD_PRELOAD rootkit was deployed for persistence.- Shell access as Gitea service account (achieved in step 4)
- Containerized deployments limit filesystem access — but secrets and repos are still inside the container
app.ini and repository hook directories. YARA rules for JITTERLY (SHA256: B441F793...) and SIXZUT (28B132AD...). Network detection: connections to s2.981666.xyz:8082, 72.11.138.109:8888.DISABLE_REGISTRATION = true in app.ini under [service]. This eliminates the unauthenticated-to-authenticated escalation path, forcing attackers to obtain valid credentials before exploitation. Deploy within the noisgate mitigation SLA of ≤3 days (CRITICAL verdict). Note: this does NOT fix the vulnerability — any authenticated user with write access to any repo can still exploit it.POST /api/v1/repos/*/diffpatch. This disables the vulnerable endpoint entirely. Some CI/CD workflows that use the diffpatch API will break — validate before deploying. Apply within ≤3 days.--read-only and explicit writable volume mounts for /data only. This prevents the hook injection from writing to unexpected paths. Also run as a non-root UID to limit post-exploitation pivot. Deploy within ≤3 days if patching is not immediately possible.- Rate-limiting the API — the exploit requires only a single POST request to the diffpatch endpoint; rate-limiting will not prevent exploitation.
- Gitea's built-in webhook restrictions — these control outbound webhooks, not server-side Git hooks. The vulnerability installs a server-side hook, which bypasses webhook allowlists entirely.
- Repository permission audits — the attacker creates their own repository; reviewing existing repo permissions does not prevent the attack path.
- Two-factor authentication on Gitea accounts — MFA protects against credential theft but does not help when the attacker registers their own account via open self-registration.
The supporting signals.
| In-the-Wild Exploitation | Confirmed and active. CISA added to KEV on 2026-08-25. Red Heron APT exploited within 48 hours of PoC release, compromising 11 organizations across defense, government, energy, and aerospace in US, Canada, Taiwan, Argentina, Sri Lanka (Acronis TRU). Commodity crypto-miner campaigns documented on Habr. |
|---|---|
| Proof-of-Concept | Multiple public PoCs. imbas007/CVE-2026-60004-POC — Python, full-auto mode (registers account → creates repo → exploits → exfils output). 11 total GitHub repos indexed. Nuclei template available (CVE-2026-60004.yaml). ExploitDB entry present. |
| EPSS | 0.23988 — top 5% of all scored CVEs. Indicates high predicted exploitation probability, consistent with confirmed in-the-wild activity. |
| KEV Status | Listed 2026-08-25. CISA BOD 22-01 federal remediation deadline: 2026-08-28 (3-day window). This is among the shortest remediation windows CISA has ever mandated. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — Network-attackable, low complexity, no privileges required, no user interaction, full CIA impact. The PR:N is accurate because default open registration converts anonymous access to write access. |
| Affected Versions | Gitea 1.17.0 through 1.27.0 inclusive. Fixed in 1.27.1 (2026-07-27). A further hardened release 1.27.2 followed. |
| Exposure Data | Shadowserver identified 8,393 vulnerable internet-facing instances as of 2026-08-27 (BleepingComputer). Red Heron scanned 1,386 instances across 7 countries and maintained a separate Taiwan-focused list of 477 targets. |
| Reporting Researcher | Shai Rod (aka NightRang3r), Salesforce security team. Responsible disclosure to Gitea project; patch shipped 2026-07-27, advisory published 2026-07-28. |
| Threat Actor Activity | Red Heron (suspected state-aligned): deployed JITTERLY C++ implant (30+ post-exploitation commands) and SIXZUT LD_PRELOAD rootkit. C2: s2.981666.xyz:8082. Pivoted from Gitea to Proxmox hypervisors. Targeted defense, elections, energy sectors. |
| Detection Coverage | Nuclei template available. No specific Sigma rule published yet but behavioral indicators are well-documented: rapid registration→repo→diffpatch sequence, child processes spawned by Gitea service, modifications to hook directories. |
Sources.
- CISA KEV Alert — CVE-2026-60004 Added
- BleepingComputer — Over 8,300 Gitea Servers Vulnerable
- Acronis TRU — Red Heron Campaign and SIXZUT Rootkit
- Help Net Security — CVE-2026-60004 Exploited in the Wild
- imbas007/CVE-2026-60004-POC — Full-Auto Python Exploit
- SOC Prime — Detection Guidance and Miner Payload Analysis
- runZero — Finding Vulnerable Gitea Assets
- SecurityWeek — CISA Warns of Exploited Gitea Vulnerability
Why this verdict
- Default config = pre-auth RCE: Gitea ships with
ENABLE_OPENID_SIGNUPandALLOW_ONLY_EXTERNAL_REGISTRATIONoff, meaning self-registration is enabled by default. This converts PR:N in the CVSS vector from theoretical to literal — any anonymous internet user can register, create a repo, and achieve RCE in ~11 seconds. - Active APT and commodity exploitation: Red Heron weaponized this within 48 hours of PoC publication, compromising defense and government targets across five countries. Commodity actors are independently deploying crypto miners. This is not a future risk — it is happening now.
- Weaponization is turnkey: The imbas007 PoC is a single Python script with a
--mode full-autoflag that handles registration, repo creation, exploit delivery, and output exfiltration. No manual steps, no binary dependencies, no exploit development skill required. - Role multiplier: Gitea is canonically a CI/CD and source-code forge component — it occupies the supply-chain-scale high-value role by definition. Compromise of a Gitea instance gives the attacker write access to every hosted repository (supply-chain pivot), database credentials, OAuth tokens, SSH keys, and JWT signing secrets. In the Red Heron campaign, attackers pivoted from Gitea to Proxmox hypervisors — demonstrating domain/fleet-scale blast radius. Because Gitea IS the forge, ≥90% of Gitea installations occupy this high-value role, placing the verdict floor at CRITICAL.
- 8,393 exposed instances, 11-second exploit chain: Shadowserver data shows thousands of internet-facing targets. The attack is fully automated and completes faster than most detection pipelines can alert. No friction point in the chain meaningfully reduces the population of exploitable targets on default configurations.
Why not higher?
The CVSS scale caps at 10.0 and this scores 9.8. The only missing tenth is Scope:Changed — the exploit runs as the Gitea service account, not as root (though privilege escalation from git to root is trivial on many Linux deployments, especially containerized ones where the service runs as UID 0). There is no basis to exceed the vendor score.
Why not lower?
Every friction-based downgrade argument fails here. *'Requires authentication'* — no, default config allows self-registration. *'Requires internal network'* — no, 8,393+ instances are internet-facing and attackers are actively scanning for them. *'Requires specific configuration'* — no, the default configuration is exploitable. *'Limited blast radius'* — no, Gitea is a code forge; compromise yields supply-chain access to all hosted repositories. The deployment-role floor for a CI/CD/forge component with confirmed fleet-scale outcomes is CRITICAL, and no friction point in this chain breaks below that floor.
Crowdsourced verification payload.
Run this script on the Gitea host or from any machine with network access to the Gitea instance. Invoke as: bash check_cve_2026_60004.sh https://gitea.example.com:3000 — no special privileges required; it queries the unauthenticated /api/v1/version endpoint.
#!/usr/bin/env bash
# check_cve_2026_60004.sh — Detect CVE-2026-60004 (Gitea diffpatch RCE)
# Usage: bash check_cve_2026_60004.sh <GITEA_BASE_URL>
# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN
set -euo pipefail
if [ $# -lt 1 ]; then
echo "Usage: $0 <GITEA_BASE_URL>"
echo "Example: $0 https://gitea.example.com:3000"
exit 2
fi
BASE_URL="${1%/}"
API_URL="${BASE_URL}/api/v1/version"
# Fetch version from Gitea API
RESPONSE=$(curl -sf --max-time 10 "${API_URL}" 2>/dev/null) || {
echo "UNKNOWN — could not reach ${API_URL}"
exit 2
}
VERSION=$(echo "${RESPONSE}" | grep -oP '"version"\s*:\s*"\K[^"]+' 2>/dev/null) || {
echo "UNKNOWN — could not parse version from response"
exit 2
}
echo "Detected Gitea version: ${VERSION}"
# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$(echo "${VERSION}" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')"
if [ -z "${MAJOR:-}" ] || [ -z "${MINOR:-}" ] || [ -z "${PATCH:-}" ]; then
echo "UNKNOWN — could not parse semantic version from '${VERSION}'"
exit 2
fi
# Vulnerable: 1.17.0 <= version <= 1.27.0
# Fixed: >= 1.27.1
if [ "${MAJOR}" -eq 1 ]; then
if [ "${MINOR}" -ge 17 ] && [ "${MINOR}" -le 26 ]; then
echo "VULNERABLE — Gitea ${VERSION} is in the affected range (1.17.0–1.27.0). CVE-2026-60004 applies."
exit 1
elif [ "${MINOR}" -eq 27 ] && [ "${PATCH}" -eq 0 ]; then
echo "VULNERABLE — Gitea ${VERSION} is in the affected range (1.17.0–1.27.0). CVE-2026-60004 applies."
exit 1
elif [ "${MINOR}" -eq 27 ] && [ "${PATCH}" -ge 1 ]; then
echo "PATCHED — Gitea ${VERSION} contains the fix for CVE-2026-60004."
exit 0
elif [ "${MINOR}" -ge 28 ]; then
echo "PATCHED — Gitea ${VERSION} is beyond the affected range."
exit 0
elif [ "${MINOR}" -lt 17 ]; then
echo "PATCHED — Gitea ${VERSION} predates the affected range (1.17.0+). Not vulnerable to CVE-2026-60004."
exit 0
fi
elif [ "${MAJOR}" -gt 1 ]; then
echo "PATCHED — Gitea ${VERSION} is beyond the affected major version."
exit 0
fi
echo "UNKNOWN — could not determine vulnerability status for version ${VERSION}"
exit 2