
Prova de conceito e laboratório para um DoS de artefato v4 do Gitea Actions: io.ReadAll sem limite em parseProtobufBody permite que um token de job faça POST de corpos CreateArtifact superdimensionados.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-artifact-v4-readall
Gitea 1.27.3 - Gitea
job_summary.go já limita em MaxJobSummarySize (1 MiB) com io.LimitReader. parseProtobufBody no artifact v4 faz io.ReadAll(ctx.Req.Body) sem limite. CreateArtifact, Finalize, List, Get, Delete passam todos por aí. Um PAT de usuário aleatório não é suficiente. Você precisa de um token de job do Actions em execução. Jobs de fork-PR ainda exigem NeedApproval no 1.27.3.
O job-summary aprendeu o LimitReader. O artifact v4 não o copiou. Um job ativo pode enviar um CreateArtifact de 12 MiB e o Gitea armazena tudo em buffer.
| ID | sem CVE ainda |
| CWE | CWE-400 |
| CVSS | Médio: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
| Produto | Gitea |
| Afetado | até v1.27.3 (146cc3e) com Actions habilitado |
| Autenticação | token de job do Actions em execução |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
De um job do Actions em execução (seu próprio repositório, ou um fork-PR que já passou pelo NeedApproval), envie um corpo CreateArtifact v4 de tamanho arbitrário e force o Gitea a armazená-lo inteiramente em buffer. O job-summary na mesma instância já rejeita qualquer coisa acima de 1 MiB.
Isso é pressão de memória sobre o processo do Gitea. O lab provou a ausência do limite (12 MiB -> 200). Não matou o processo. Não é RCE. Não é não autenticado. Um PAT de usuário roubado sem um job ativo não chega a esse caminho.
O mapa restante após a onda de GHSAs incluía o tratamento de corpos do Actions. Jobs de fork-PR ainda exigem NeedApproval no 1.27.3, então isso não é um bypass de fork-PR não autenticado. Eu olhei como a API do Actions lê os corpos das requisições. O job-summary tem um número. O artifact v4 não.
Eu subi o gitea/act_runner:0.2.13, preparei um workflow de espera para que um token de job existisse, e enviei um CreateArtifact de 12 MiB. HTTP 200 ok mais signedUploadUrl. Depois 8 MiB de job-summary: 400 invalid summary. GET /api/v1/version ainda 200. O Gitea permaneceu no ar. O mem_limit do lab é 1g. Corpo limitado a 12 MiB para que o host não seja o experimento. Apenas executor de host. Sem docker.sock.
Caminhos errados já registrados: CreateArtifact sem um token de job (401 não é o bug); kill de processo / OOM (SUCCESS é 12 MiB -> 200 enquanto 8 MiB de summary -> 400); tratar signedUploadUrl como RCE; fork-PR não autenticado; um reverse shell. Teatro.
cd lab
./run.sh
Alvo apenas http://127.0.0.1:18135. O ROOT_URL dentro do compose é http://gitea:3000/ para que o runner possa se registrar.
v4-create status=200 sent=12582912
v4-unbounded-readall=1
job-summary status=400 sent=8388608 invalid summary cap=MaxJobSummarySize=1048576
gitea-still-up status=200
SUCCESS GITEA-ARTIFACT-V4-READALL
Envolva parseProtobufBody em io.LimitReader com um limite de tamanho de proto, o mesmo padrão do MaxJobSummarySize. Um CreateArtifact de 12 MiB deve falhar da mesma classe de forma que um job-summary de 8 MiB já falha.
GNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantia.