【漏洞复现】CVE-2026-60004 Gitea diffpatch 接口 Git 钩子注入致远程命令执行
前言
Gitea 在 1.17 ~ 1.27.0 版本中存在一个严重的代码注入漏洞(CVE-2026-60004,GHSA-rcr6-4jqh-j84m),官方已在 1.27.1 修复。漏洞位于 diffpatch(差异补丁)API:攻击者只需拥有一个账号并对任意仓库具备写权限,即可通过该接口提交恶意补丁,在服务器以 裸仓库(bare repo) 形式存储的临时仓库中 植入并执行 Git 钩子,从而以 Gitea 服务账户权限执行任意命令。
由于 Gitea 默认 开放注册,外部人员可以自行注册账号并创建仓库,从「无任何权限」直接获得仓库写权限,使该漏洞极易被真实利用(CVSS 9.8)。
漏洞信息
- 漏洞编号:CVE-2026-60004(GHSA-rcr6-4jqh-j84m)
- 影响版本:Gitea 1.17 ~ 1.27.0
- 修复版本:1.27.1
- 漏洞位置:
POST /api/v1/repos/{owner}/{repo}/diffpatch - 利用前提:拥有账号 + 对目标仓库具备写权限(默认开放注册时,攻击者可自行注册并创建仓库,天然满足)
- 危害:任意命令执行(以 Gitea 运行账户身份,CWE-94)
环境信息
- 目标:一台存在该漏洞的 Gitea 实例(受影响的 1.27.0)
- 服务账户:
git(Run As Username: git) - Git 版本:2.54.0(
git apply -3需要 Git ≥ 2.32)
漏洞原理
diffpatch 接口
Gitea 提供 diffpatch 接口,用于对一个仓库应用一段 unified diff 补丁:
1 | POST /api/v1/repos/{owner}/{repo}/diffpatch |
服务端逻辑位于 services/repository/files/patch.go,流程为:创建临时仓库 → 克隆目标仓库 → git apply 应用补丁 → 生成提交 → 推送回目标仓库。
漏洞点:裸仓库 + git apply --index --cached + three-way 回退
受影响版本用 裸仓库 克隆临时仓库,并以如下命令应用补丁:
1 | // v1.27.0 —— 存在漏洞 |
关键点:
- 临时仓库是 裸仓库,其「工作目录」就是 Git 目录本身(
$GIT_DIR); - 正常情况下
--cached只写索引,不会写工作区。但 把同一个补丁提交两次 会产生 add/add 冲突,触发 Git 的 three-way 回退(-3)——即使带--cached,Git 仍会把索引中的路径 checkout 到工作区; - 于是补丁里的文件被真实地写进裸仓库的 Git 目录:
hooks/post-index-change成为一个「活的」Git 钩子; post-index-change是 Git 在 写索引时 调用的钩子(git apply、read-tree、update-index等都会触发),因此 无需等待 push,diffpatch 操作本身就会执行该钩子;钩子以 Gitea 服务账户(git)权限运行,实现任意命令执行。
结果回显(无外连)
临时仓库是 --shared 克隆,其 objects/info/alternates 指向 源仓库(真实仓库) 的对象目录。钩子可以据此定位真实仓库的 Git 目录,把命令输出写成对象并 update-ref 创建一个分支;攻击者再用 smart HTTP 拉取该分支即可读取命令输出,全程不需要目标主动出网。
官方修复
1.27.1 通过 PR #38637 修复:把临时仓库改为 非裸仓库(non-bare) 克隆,使工作区与 Git 目录分离,补丁再也无法写入 hooks/ 等 Git 内部文件。
1 | // v1.27.1 —— 已修复 |
复现步骤
1. 指纹识别
访问首页,底部显示 版本: 1.27.0,处于受影响版本(见上图)。
2. 注册账号并创建仓库
默认开放注册,注册一个普通账号后创建仓库,即可获得写权限。
3. 构造恶意补丁
补丁在 hooks/post-index-change 新增一个可执行脚本。脚本会执行命令、把输出写入源仓库对象,并创建一个 refs/heads/<leak_ref> 分支用于回显:
1 |
|
对应的补丁:
1 | diff --git a/hooks/post-index-change b/hooks/post-index-change |
4. 同一补丁提交两次
关键:把这段完全相同的补丁连续提交两次。
1 | curl -X POST "$TARGET/api/v1/repos/$USER/$REPO/diffpatch" \ |
第一次 201 Created;第二次仍是 201 Created,但这次触发了 add/add 冲突与 three-way 回退,hooks/post-index-change 被真实写入裸仓库的 Git 目录并被执行。由于 post-index-change 的返回值不会体现在 diffpatch 的响应里,两次请求都表现为正常成功。
仓库中可见被植入的可执行钩子:
5. 读取命令执行结果
拉取钩子创建的 poc-output 分支,即可看到命令输出:
1 | git init --bare leak.git |
输出确认命令以 Gitea 服务账户 git 身份执行:
6. 获取 flag
拿到命令执行能力后,读取 flag 文件即可(本例中 flag 位于服务器根目录,属主为服务账户 git):
1 | ls -la / |
(flag 值略。)
修复建议
- 升级:立即升级到 Gitea 1.27.1 及以上;
- 关闭开放注册:
DISABLE_REGISTRATION = true,或改为管理员审批 / 邮箱域名白名单; - 收紧接口权限:限制
diffpatch等会写仓库内容 / Git 目录的接口,仅授予可信用户并做审计; - 资产排查:检查仓库中是否存在异常的
hooks/、custom_hooks/文件(可执行脚本),以及来源不明的分支; - 最小权限:以低权限、隔离的账户运行 Gitea,降低 RCE 后的影响面。
参考
- GHSA-rcr6-4jqh-j84m —— Remote Code Execution via diffpatch Git Hook Installation
https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m - Gitea PR #38637(git patch apply 修复)
https://github.com/go-gitea/gitea/pull/38637






![【BUUCTF】WEB-刷题记录-[极客大挑战 2019]BuyFlag](/../attachments/image-20261014052001111.png)
