
Proof-of-concept e lab per un DoS su Gitea Actions artifact v4: io.ReadAll senza limiti in parseProtobufBody consente a un job token di inviare corpi CreateArtifact di dimensioni eccessive.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-artifact-v4-readall
Gitea 1.27.3 - Gitea
job_summary.go limita già a MaxJobSummarySize (1 MiB) con io.LimitReader. parseProtobufBody in artifact v4 esegue io.ReadAll(ctx.Req.Body) senza alcun limite. CreateArtifact, Finalize, List, Get, Delete passano tutti da lì. Un PAT utente casuale non è sufficiente. Serve un token di un job Actions in esecuzione. I job da fork-PR su 1.27.3 richiedono comunque NeedApproval.
Job-summary ha imparato LimitReader. Artifact v4 non l'ha copiato. Un job attivo può inviare un CreateArtifact da 12 MiB e Gitea bufferizza tutto.
| ID | nessuna CVE al momento |
| CWE | CWE-400 |
| CVSS | Medium: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
| Prodotto | Gitea |
| Affetto | fino alla v1.27.3 (146cc3e) con Actions abilitato |
| Auth | token di un job Actions in esecuzione |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Da un job Actions in esecuzione (nel proprio repository, o da un fork-PR che ha già superato NeedApproval), inviare un body CreateArtifact v4 di dimensione arbitraria e forzare Gitea a bufferizzarlo interamente. Job-summary sulla stessa istanza rifiuta già qualsiasi cosa superi 1 MiB.
Si tratta di pressione sulla memoria del processo Gitea. Il lab ha dimostrato l'assenza del limite (12 MiB -> 200). Non ha ucciso il processo. Non è RCE. Non è non autenticato. Un PAT utente rubato senza un job attivo non raggiunge questo percorso.
La mappa rimanente dopo l'ondata di GHSA includeva la gestione dei body di Actions. I job da fork-PR su 1.27.3 richiedono comunque NeedApproval, quindi non si tratta di un bypass non autenticato dei fork-PR. Ho esaminato come l'API Actions legge i body delle richieste. Job-summary ha un numero. Artifact v4 no.
Ho avviato gitea/act_runner:0.2.13, preparato un workflow di attesa in modo che esistesse un token di job, e inviato un CreateArtifact da 12 MiB. HTTP 200 ok più signedUploadUrl. Poi job-summary da 8 MiB: 400 invalid summary. GET /api/v1/version ancora 200. Gitea è rimasto attivo. Il mem_limit del lab è 1g. Body limitato a 12 MiB così che l'host non sia l'esperimento. Solo host executor. Nessun docker.sock.
Vicoli ciechi già registrati: CreateArtifact senza token di job (401 non è il bug); kill del processo / OOM (SUCCESS è 12 MiB -> 200 mentre summary da 8 MiB -> 400); trattare signedUploadUrl come RCE; fork-PR non autenticato; una reverse shell. Teatro.
cd lab
./run.sh
Target solo http://127.0.0.1:18135. Il ROOT_URL in-compose è http://gitea:3000/ così il runner può registrarsi.
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
Avvolgere parseProtobufBody in io.LimitReader con un limite di dimensione proto, stesso schema di MaxJobSummarySize. Un CreateArtifact da 12 MiB deve fallire nello stesso modo in cui fallisce già un job-summary da 8 MiB.
GNU Affero GPL v3.0. Vedi LICENSE. Solo lab su loopback. Nessuna garanzia.