Skip to content

Gitea diffpatch Git-hook installation KEV (CVE-2026-60004)

Source: hourly offensive-security scan, 2026-08-25. CISA KEV entry added 2026-08-25 (catalog 2026.08.25), primary record: GHSA-rcr6-4jqh-j84m / CVE-2026-60004.

Why this is worth an operator page

Gitea's diffpatch endpoint can be abused to install and execute a Git hook from repository-controlled content. An attacker with ordinary write access to a repository executes arbitrary shell commands as the Gitea service account (CWE-94). With default open registration, an unauthenticated visitor can obtain the required write access by registering an account and creating a repository. Because the public record documents the full mechanics — and the PoC returns evidence through the Git protocol itself rather than an outbound connection — this is one of the rare KEVs whose exploit chain can be validated end-to-end in an authorized lab without touching any external infrastructure.

The durable operator lesson is a two-class Git-forge pattern:

  1. git apply on a bare temporary clone with --index --cached --binary plus -3 (Git ≥ 2.32): a deliberately induced add/add collision forces the three-way fallback to check an indexed path out even though the operation was supposedly cache-only. In a bare clone, the repository root is $GIT_DIR, so an executable entry named hooks/post-index-change becomes a live hook, and Git invokes it while writing the index.
  2. The hook's return value is not propagated to the diffpatch response — the failure is silent, so the flow cannot be detected from the API response alone.

Product boundary and affected versions

  • Component: Gitea services/repository/files/patch.go — the diffpatch route that applies patches to repositories.
  • Affected: >=1.17, <1.27.1. Patched: 1.27.1.
  • Preconditions:
  • repository write access (or open registration on the deployment);
  • Git ≥ 2.32 on the Gitea host (the -3 three-way flag path);
  • enabled diffpatch route;
  • writable and executable temporary filesystem for Gitea's bare clones.
  • Impact (per the advisory): RCE as the Gitea OS user; depending on deployment isolation this can expose app.ini and application secrets, process environment secrets, mounted repositories, database credentials and contents, and OAuth/integration credentials.

Replayable validation workflow (authorized lab only)

Work on a disposable Gitea instance (container or VM) with a test account that can create repositories. Never point this at a shared or production Gitea: the chain writes an executable hook into Gitea's temp clone root and executes it as the Gitea service user.

1. Confirm the boundary

  • Version check against the Gitea instance (/api/v1/version or banner).
  • Confirm git version ≥ 2.32 on the server (error paths, or version disclosure in your lab).
  • Confirm the diffpatch route is enabled and the temp filesystem is executable (touch/execute probe in a lab container is enough; on the target, just note the assumption).

2. Build the collision patch

The exploit requires submitting the same patch twice so that the second apply hits an add/add collision:

  • Create a private repo with an initialized main branch.
  • Craft a patch that adds a file whose path, in the bare clone, resolves under $GIT_DIR/hooks/ — specifically hooks/post-index-change with a shebang and a marker command.
  • Submit the patch once via the diffpatch endpoint; submit the identical patch a second time.

In the lab, replace the marker command with something inert (id, uname -srm, pwd, echo SKILLZ_CANARY).

3. Evidence that stays in-protocol

The public PoC design is worth reusing: the hook stores command output in Git objects and creates a branch containing the result, so no outbound connection is required.

  • The marker branch is then fetched through authenticated smart HTTP with the test account.
  • Positive proof: the branch exists and contains only the marker output (uid=...(git), hostname/OS marker, /data/gitea/tmp/... style path, exit status 0).

4. Negative controls

  • Gitea ≥ 1.27.1: the same two-patch flow does not install or invoke the hook.
  • A patch without the duplicate collision does not trigger the fallback.
  • A non-executable temp filesystem or Git < 2.32 blocks the chain.

Stop at the marker branch. Do not read app.ini, environment variables, database credentials, OAuth tokens, or repository contents on anything that is not your disposable lab. Do not run the public PoC script against a target — run the two-patch flow yourself against your lab so every byte of evidence is under your control.

Reporting heuristic

Frame the finding around the boundary, not the CVE: "diffpatch route applies repository-controlled patches in a shared bare temp clone; a duplicated add/add patch forces the three-way fallback to check out hooks/post-index-change from the index; Git invokes the hook while writing the index, executing repository-controlled content as the Gitea service account; hook exit status is not propagated to the API response." Include:

  • Gitea version and server git version;
  • the exact diffpatch requests (both submits) and the collision evidence;
  • the marker branch name and its inert contents;
  • patched-build denial.

Do not attach environment, credentials, or app.ini excerpts as proof; the marker branch is the proof.

Recon heuristics for Gitea deployments

  • Any internet-reachable Gitea with open registration should be treated as unauthenticated-RCE-adjacent when running < 1.27.1: registration + repo creation satisfies the entire write-access precondition.
  • In internal recon, Gitea/Forgejo instances behind SSO still need this check if repo creation is granted to low-privilege groups.
  • The same class of bug — apply/merge tooling that operates in a bare or shared $GIT_DIR where hooks/ is an executable namespace — is worth auditing in other Git-forge code paths (import, migration, and patch-apply routes). See the June 17 Gitea boundary batch for the broader Git-forge authorization-drift pattern and the August 5 Gitea migration/OAuth fetch follow-up.

Safety

  • Authorized, lab-only validation. This chain executes code as the Gitea service account; on any non-lab system it is a full compromise, not a probe.
  • Keep the marker inert (id, uname, pwd); never encode exfiltration, curl, or credential access in the hook.
  • Do not run the public gitea_diffpatch_rce_poc.py against anything you do not own; its output shape is what to replicate, not its code.
  • No outbound channel is needed for proof — use the Git-branch evidence pattern instead.

Sources: CISA KEV catalog · GHSA-rcr6-4jqh-j84m / CVE-2026-60004 · NVD CVE-2026-60004