
Preuve de concept et laboratoire pour un DoS de l'artefact Gitea Actions v4 : un io.ReadAll non borné dans parseProtobufBody permet à un jeton de job de POSTer des corps CreateArtifact surdimensionnés.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-artifact-v4-readall
Gitea 1.27.3 - Gitea
job_summary.go plafonne déjà à MaxJobSummarySize (1 MiB) avec io.LimitReader. parseProtobufBody dans artifact v4 fait io.ReadAll(ctx.Req.Body) sans plafond. CreateArtifact, Finalize, List, Get, Delete passent tous par là. Un PAT utilisateur aléatoire ne suffit pas. Il faut un token de job Actions en cours d'exécution. Les jobs fork-PR restent en NeedApproval sur 1.27.3.
Le job-summary a appris LimitReader. Artifact v4 ne l'a pas copié. Un job actif peut POSTer un CreateArtifact de 12 MiB et Gitea bufferise le tout.
| ID | pas encore de CVE |
| CWE | CWE-400 |
| CVSS | Moyen : 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
| Produit | Gitea |
| Affecté | jusqu'à v1.27.3 (146cc3e) avec Actions activé |
| Auth | token de job Actions en cours d'exécution |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
Depuis un job Actions en cours d'exécution (son propre dépôt, ou un fork-PR ayant déjà passé NeedApproval), POSTer un corps CreateArtifact v4 arbitrairement volumineux et forcer Gitea à le bufferiser entièrement. Le job-summary sur la même instance rejette déjà tout ce qui dépasse 1 MiB.
C'est une pression mémoire sur le processus Gitea. Le lab a prouvé l'absence de plafond (12 MiB -> 200). Il n'a pas tué le processus. Ce n'est pas du RCE. Ce n'est pas non authentifié. Un PAT utilisateur volé sans job actif n'atteint pas ce chemin.
La carte résiduelle après la vague GHSA incluait la gestion des corps Actions. Les jobs fork-PR restent en NeedApproval sur 1.27.3, donc ce n'est pas un contournement fork-PR non authentifié. J'ai examiné comment l'API Actions lit les corps de requête. Le job-summary a une limite. Artifact v4 n'en a pas.
J'ai monté gitea/act_runner:0.2.13, amorcé un workflow de maintien pour qu'un token de job existe, et POSTé un CreateArtifact de 12 MiB. HTTP 200 ok plus signedUploadUrl. Puis un job-summary de 8 MiB : 400 invalid summary. GET /api/v1/version toujours 200. Gitea est resté debout. Le mem_limit du lab est 1g. Corps plafonné à 12 MiB pour que l'hôte ne soit pas l'expérience. Exécuteur hôte uniquement. Pas de docker.sock.
Fausses pistes déjà consignées : CreateArtifact sans token de job (401 n'est pas le bug) ; kill du processus / OOM (SUCCESS est 12 MiB -> 200 tandis que 8 MiB summary -> 400) ; traiter signedUploadUrl comme du RCE ; fork-PR non authentifié ; un reverse shell. Du théâtre.
cd lab
./run.sh
Cible uniquement http://127.0.0.1:18135. Le ROOT_URL in-compose est http://gitea:3000/ pour que le runner puisse s'enregistrer.
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
Envelopper parseProtobufBody dans io.LimitReader avec un plafond de taille proto, même schéma que MaxJobSummarySize. Un CreateArtifact de 12 MiB doit échouer de la même classe de manière qu'un job-summary de 8 MiB le fait déjà.
GNU Affero GPL v3.0. Voir LICENSE. Lab en boucle locale uniquement. Aucune garantie.