
Prueba de concepto y laboratorio para un DoS de artefacto v4 de Gitea Actions: io.ReadAll sin límites en parseProtobufBody permite que un token de trabajo POSTee cuerpos CreateArtifact sobredimensionados.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-artifact-v4-readall
Gitea 1.27.3 - Gitea
job_summary.go ya limita a MaxJobSummarySize (1 MiB) con io.LimitReader. parseProtobufBody en artifact v4 hace io.ReadAll(ctx.Req.Body) sin límite. CreateArtifact, Finalize, List, Get, Delete pasan todos por ahí. Un PAT de usuario aleatorio no es suficiente. Necesitas un token de job de Actions en ejecución. Los jobs de fork-PR siguen en NeedApproval en 1.27.3.
Job-summary aprendió LimitReader. Artifact v4 no lo copió. Un job activo puede hacer POST de un CreateArtifact de 12 MiB y Gitea lo almacena en búfer entero.
| ID | sin CVE todavía |
| CWE | CWE-400 |
| CVSS | Medio: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
| Producto | Gitea |
| Afectado | hasta v1.27.3 (146cc3e) con Actions habilitado |
| Auth | token de job de Actions en ejecución |
| Licencia | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Desde un job de Actions en ejecución (su propio repo, o un fork-PR que ya pasó NeedApproval), hacer POST de un cuerpo CreateArtifact v4 de tamaño arbitrario y forzar a Gitea a almacenarlo entero en búfer. Job-summary en la misma instancia ya rechaza cualquier cosa superior a 1 MiB.
Eso es presión de memoria sobre el proceso de Gitea. El lab demostró la falta de límite (12 MiB -> 200). No mató el proceso. No es RCE. No es no autenticado. Un PAT de usuario robado sin un job activo no llega a esta ruta.
El mapa de restos tras la oleada de GHSA incluía el manejo del cuerpo de Actions. Los jobs de fork-PR siguen en NeedApproval en 1.27.3, así que esto no es un salto de fork-PR sin autenticación. Miré cómo la API de Actions lee los cuerpos de las peticiones. Job-summary tiene un número. Artifact v4 no.
Levanté gitea/act_runner:0.2.13, sembré un workflow de retención para que exista un token de job, y hice POST de un CreateArtifact de 12 MiB. HTTP 200 ok más signedUploadUrl. Luego 8 MiB de job-summary: 400 invalid summary. GET /api/v1/version sigue en 200. Gitea siguió en pie. El mem_limit del lab es 1g. Cuerpo limitado a 12 MiB para que el host no sea el experimento. Solo ejecutor de host. Sin docker.sock.
Caminos equivocados ya registrados: CreateArtifact sin token de job (401 no es el bug); matar el proceso / OOM (SUCCESS es 12 MiB -> 200 mientras que 8 MiB de summary -> 400); tratar signedUploadUrl como RCE; fork-PR sin autenticación; una reverse shell. Teatro.
cd lab
./run.sh
Objetivo solo http://127.0.0.1:18135. El ROOT_URL dentro del compose es http://gitea:3000/ para que el runner pueda registrarse.
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
Envolver parseProtobufBody en io.LimitReader con un límite de tamaño proto, el mismo patrón que MaxJobSummarySize. Un CreateArtifact de 12 MiB debe fallar de la misma clase de forma que ya lo hace un job-summary de 8 MiB.
GNU Affero GPL v3.0. Ver LICENSE. Solo lab en loopback. Sin garantía.