← Back to Feed CACHED · 2026-08-04 21:24:57 · CACHE_KEY CVE-2026-60004
CVE-2026-60004 · CWE-94 · Disclosed 2026-07-28

Gitea Remote Code Execution via diffpatch Git Hook Injection

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

Someone left the keys to your entire codebase hanging on the front door handle

CVE-2026-60004 is a code-injection flaw in Gitea's POST /api/v1/repos/{owner}/{repo}/diffpatch endpoint, present in every release from 1.17 through 1.27.0 — roughly four years of releases. By sending a crafted patch that triggers an add/add collision in Git's three-way merge fallback, an attacker writes a malicious file into the Git hooks directory of a temporary bare clone. The next Git operation executes that hook as the Gitea service account, giving the attacker full OS-level command execution. Because Gitea's default configuration ships with open self-registration, no email confirmation, and no admin approval, an anonymous internet user can create an account, create a repository, and fire the exploit — no prior credentials required.

The CNA-assigned CVSS 9.8 is accurate and, if anything, undersells the operational impact. Gitea is not a random web app — it is the source-code management layer for every project it hosts. RCE on the Gitea server means access to every repository, every deploy key, every CI/CD webhook secret, and every stored credential. On default installations the attack is unauthenticated and trivially scriptable with a public PoC. The only friction is that the target must be reachable and running a vulnerable version. There is no reason to downgrade this.

"Gitea diffpatch RCE lets anyone with signup access own the server and every repo on it"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Register or authenticate to Gitea

The attacker visits the Gitea instance's registration page. Because default configuration enables open registration with no email verification or admin approval, the attacker creates a new account in seconds. If registration is disabled, any existing user with write access to at least one repository qualifies.
Conditions required:
  • Gitea instance reachable over the network
  • Open registration enabled (default) OR existing write-access credentials
Where this breaks in practice:
  • Instances behind VPN or with DISABLE_REGISTRATION = true block anonymous signup
Detection/coverage: Web-server access logs will show POST to /user/sign_up followed immediately by API calls — anomalous for a brand-new account.
STEP 02

Create or access a writable repository

The attacker creates a new repository via the API or web UI. This grants owner-level write access automatically. Alternatively, any repository the attacker already has write permission on works.
Conditions required:
  • Authenticated session from step 1
  • Repository creation allowed (default)
Where this breaks in practice:
  • Environments that restrict repo creation to admins limit this path
STEP 03

Send malicious diffpatch payload

The attacker sends a POST /api/v1/repos/{owner}/{repo}/diffpatch request containing a specially crafted patch. The patch exploits an add/add collision in Git's three-way merge, causing the patched content to land in the .git/hooks/ directory of the temporary bare clone. The hook file is made executable and contains attacker-controlled shell commands. No outbound callback is needed — the PoC stores command output in Git objects retrievable over authenticated smart HTTP.
Conditions required:
  • Write access to target repository
  • Gitea version 1.17–1.27.0
  • Git >= 2.32 on server
  • Writable and executable temp filesystem
Where this breaks in practice:
  • Read-only or noexec-mounted temp filesystems would prevent hook execution
  • Git versions below 2.32 do not support the required merge behavior
Detection/coverage: WAF rules inspecting API request bodies for .git/hooks path traversal patterns. API audit logs will show the diffpatch call.
STEP 04

Hook executes as Gitea service account

Any subsequent Git operation on the repository — push, fetch, or internal housekeeping — triggers the planted hook. The hook runs with the full privileges of the Gitea process user, typically git or gitea. This yields OS-level command execution: read all repos, exfiltrate secrets, pivot to CI/CD runners, or install persistence.
Conditions required:
  • Successful hook write from step 3
  • A Git operation triggers the hook (often immediate)
Where this breaks in practice:
  • Containerized Gitea with minimal capabilities and read-only root FS limits lateral movement but does not prevent initial RCE
Detection/coverage: Host-based EDR detecting unexpected child processes spawned by the Gitea binary. File-integrity monitoring on .git/hooks/ directories.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed exploitation as of 2026-08-05. Gitea's July 28 advisory does not report active attacks. Not KEV-listed.
Proof of conceptPublic. A working PoC is included in the official Gitea security advisory (July 28, 2026). Researcher Shai Rod (NightRang3r) published the discovery. The PoC requires no outbound callback — output is retrieved via Git objects over authenticated smart HTTP.
EPSS scoreNot yet scored by FIRST (CVE too recent as of 2026-08-05). Given public PoC + default-unauthenticated attack surface, expect high-percentile EPSS once scored.
KEV statusNot listed on CISA KEV as of 2026-08-05.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — 9.8 Critical. Network-reachable, low complexity, no privileges on default installs, no user interaction, full CIA impact.
Affected versionsGitea 1.17 through 1.27.0 (~4 years of releases). Most self-hosted deployments run the 1.23–1.26 lines.
Fixed versionGitea 1.27.1 (released July 27, 2026). Gitea Cloud instances auto-upgraded. Fix converts temp clone from bare to non-bare, preventing hook-directory writes.
Exposure data~6,200 Gitea instances indexed on Shodan (per Sysdig data). Default open-registration means many are exploitable without prior credentials.
Disclosure timelineFix merged July 26, 2026 → v1.27.1 released July 27 → advisory with PoC published July 28. The release notes did not flag the fix as security-critical, potentially delaying patching.
ReporterShai Rod (NightRang3r) — independent security researcher. GitHub profile.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to CRITICAL (9.8/10)

Supply-chain pivot via source-code management server — Gitea *is* the SCM layer; RCE here gives the attacker every repository, deploy key, webhook secret, and CI/CD integration credential on the instance, making fleet-wide supply-chain compromise the expected outcome, not a theoretical edge case. The public PoC, default-unauthenticated attack surface, and four-year affected version window leave no room for a lower bucket.

HIGH Vulnerability severity and exploitability
HIGH Affected version range and patch availability
MEDIUM Exposure population (Shodan count may undercount internal instances)
LOW In-the-wild exploitation (no reports yet, but PoC is public and trivial)

Why this verdict

  • Unauthenticated on default installs: Open registration is the default — the attacker self-provisions the write access the chain needs. This is functionally pre-auth RCE on unmodified deployments.
  • Public weaponized PoC with zero user interaction: The advisory itself ships a working exploit. No callback infrastructure needed; output is exfiltrated via Git objects. Script-kiddie-grade barrier to entry.
  • Role multiplier: Gitea is canonically an SCM/CI-adjacent component. 100% of installs serve this role by definition. RCE on Gitea = access to all hosted source code, secrets embedded in repos, deploy keys, CI/CD webhook tokens, and runner configurations. Blast radius is supply-chain-scale. This sets a CRITICAL floor that friction cannot override.
  • Four-year affected window: Versions 1.17–1.27.0 span the vast majority of the installed base, including the most popular 1.23–1.26 release lines. The narrow unaffected population is pre-1.17 legacy only.
  • Stealth-friendly release notes: The v1.27.1 release notes did not flag this as a security fix, meaning many operators may not have prioritized the upgrade — widening the real-world exposure window.

Why not higher?

This is already assessed at the maximum actionable severity (CRITICAL / 9.8). There is no higher bucket.

Why not lower?

Every downgrade factor was evaluated and rejected. The only friction is network reachability and registration policy — but default config is open, and ~6,200 instances are internet-facing per Shodan. Gitea is a canonical high-value SCM component where RCE yields supply-chain compromise by definition. The public PoC makes exploitation trivial. There is no credible basis for reducing below CRITICAL.

05 · Compensating Control

What to do — in priority order.

  1. Disable public registration immediately — Set DISABLE_REGISTRATION = true in [service] of app.ini and restart Gitea. This removes the self-provisioning path for unauthenticated attackers. Does not protect against existing authenticated users with write access. Deploy within the noisgate mitigation SLA of ≤3 days.
  2. Restrict or disable the diffpatch API endpoint — Use a reverse-proxy rule (nginx, Caddy, Traefik) to block POST requests matching /api/v1/repos/*/diffpatch. This surgically kills the vulnerable code path without disrupting normal Git operations. Deploy within 3 days per noisgate mitigation SLA.
  3. Mount Gitea temp directories with noexec — If the Gitea process's temp filesystem is mounted noexec, planted hooks cannot execute even if written. Add noexec to the mount options for /tmp or the configured TEMP_PATH. Verify with mount | grep tmp. Deploy within 3 days.
  4. Run Gitea in a hardened container with read-only root filesystem — Use --read-only and drop all unnecessary Linux capabilities. This limits post-exploitation lateral movement even if the hook fires. Not a substitute for patching but reduces blast radius.
  5. Audit existing repository write permissions — Review all users with write or admin access to repositories. Remove stale accounts and enforce MFA. This reduces the insider-threat variant of the attack path.
What doesn't work
  • Network-level IP allowlisting alone — does not help if the attacker is an authenticated internal user or if the Gitea instance is intentionally public-facing for open-source collaboration.
  • Git commit signing enforcement — the attack uses the diffpatch API, not a normal Git push. Commit signature verification does not inspect or block API-applied patches.
  • Rate limiting on the API — the exploit requires a single HTTP request. Rate limiting has no meaningful effect on a one-shot RCE.
06 · Verification

Crowdsourced verification payload.

Run this script on the Gitea server (or any host with SSH/API access to it) as any user who can read the Gitea binary or query its API. Example: bash check_cve_2026_60004.sh https://gitea.example.com — no special privileges required beyond reading the /api/v1/version endpoint.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_60004.sh — Detect Gitea versions vulnerable to CVE-2026-60004
# 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"
  exit 2
fi

BASE_URL="${1%/}"

# Fetch version from public API endpoint
VERSION_JSON=$(curl -sf --max-time 10 "${BASE_URL}/api/v1/version" 2>/dev/null) || {
  echo "UNKNOWN — could not reach ${BASE_URL}/api/v1/version"
  exit 2
}

VERSION=$(echo "$VERSION_JSON" | grep -oP '"version"\s*:\s*"\K[^"]+' 2>/dev/null) || {
  echo "UNKNOWN — could not parse version from API response"
  exit 2
}

echo "Detected Gitea version: ${VERSION}"

# Extract 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.1
if (( MAJOR == 1 )); then
  if (( MINOR < 17 )); then
    echo "PATCHED — version ${VERSION} predates the vulnerable code (introduced in 1.17)"
    exit 0
  elif (( MINOR > 27 )); then
    echo "PATCHED — version ${VERSION} is newer than the fixed release"
    exit 0
  elif (( MINOR == 27 && PATCH >= 1 )); then
    echo "PATCHED — version ${VERSION} includes the fix (1.27.1+)"
    exit 0
  else
    echo "VULNERABLE — version ${VERSION} is in the affected range (1.17.0–1.27.0)"
    exit 1
  fi
elif (( MAJOR > 1 )); then
  echo "PATCHED — major version ${MAJOR} is beyond affected range"
  exit 0
else
  echo "PATCHED — version ${VERSION} predates affected range"
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
Patch every Gitea instance to 1.27.1 immediately. This is a CRITICAL-severity RCE with a public PoC that works against default installations without credentials. Under the noisgate mitigation SLA (≤3 days for CRITICAL), disable public registration (DISABLE_REGISTRATION = true) and block the /api/v1/repos/*/diffpatch endpoint at your reverse proxy by end of day Wednesday. Under the noisgate remediation SLA (≤90 days for CRITICAL), complete the upgrade to 1.27.1 across all instances — but given the trivial exploitability and public PoC, treat the remediation as a same-week emergency, not a 90-day project. Audit your Gitea instances for signs of compromise: check .git/hooks/ directories for unexpected files, review access logs for diffpatch API calls, and inspect for new user registrations since July 28. If you find any Gitea instance exposed to the internet with open registration, assume it may already be compromised and begin incident response.

Sources

  1. CyCognito Emerging Threat Analysis
  2. The Hacker News — Gitea RCE Coverage
  3. Sanjay Seth — CVE-2026-60004 Deep Dive
  4. runZero — Gitea Vulnerability Asset Discovery
  5. CyberSecurityNews — Gitea RCE Vulnerability
  6. CyberArtsPro — Gitea Critical RCE Flaw
  7. NightRang3r (Shai Rod) — GitHub Profile
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.