
Proof-of-Concept und Lab für einen Gitea Actions Artifact v4 DoS: Unbegrenztes io.ReadAll in parseProtobufBody ermöglicht es einem Job-Token, übergroße CreateArtifact-Bodies zu POSTen.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-artifact-v4-readall
Gitea 1.27.3 - Gitea
job_summary.go begrenzt bereits auf MaxJobSummarySize (1 MiB) mit io.LimitReader. parseProtobufBody in artifact v4 führt io.ReadAll(ctx.Req.Body) ohne Begrenzung aus. CreateArtifact, Finalize, List, Get, Delete laufen alle darüber. Ein zufälliges Benutzer-PAT reicht nicht aus. Man benötigt ein laufendes Actions-Job-Token. Fork-PR-Jobs haben auf 1.27.3 weiterhin NeedApproval.
Job-summary hat LimitReader gelernt. Artifact v4 hat es nicht übernommen. Ein laufender Job kann ein 12 MiB großes CreateArtifact per POST senden, und Gitea puffert alles.
| ID | noch keine CVE |
| 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 |
| Produkt | Gitea |
| Betroffen | bis v1.27.3 (146cc3e) mit aktivierten Actions |
| Auth | laufendes Actions-Job-Token |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Aus einem laufenden Actions-Job (eigenes Repository oder ein Fork-PR, der NeedApproval bereits passiert hat) einen beliebig großen v4-CreateArtifact-Body per POST senden und Gitea dazu zwingen, ihn vollständig zu puffern. Job-summary auf derselben Instanz lehnt bereits alles über 1 MiB ab.
Das ist Speicherdruck auf den Gitea-Prozess. Das Lab hat die fehlende Begrenzung nachgewiesen (12 MiB -> 200). Es hat den Prozess nicht beendet. Es ist kein RCE. Es ist nicht unauthentifiziert. Ein gestohlenes Benutzer-PAT ohne laufenden Job erreicht diesen Pfad nicht.
Die übrig gebliebene Map nach der GHSA-Welle enthielt die Actions-Body-Verarbeitung. Fork-PR-Jobs haben auf 1.27.3 weiterhin NeedApproval, also ist dies kein unauth-Fork-PR-Skip. Ich habe mir angesehen, wie die Actions-API Request-Bodies liest. Job-summary hat eine Zahl. Artifact v4 nicht.
Ich habe gitea/act_runner:0.2.13 hochgefahren, einen Hold-Workflow eingespielt, damit ein Job-Token existiert, und ein 12 MiB großes CreateArtifact per POST gesendet. HTTP 200 ok plus signedUploadUrl. Dann 8 MiB job-summary: 400 invalid summary. GET /api/v1/version weiterhin 200. Gitea blieb oben. Lab mem_limit ist 1g. Body auf 12 MiB begrenzt, damit der Host nicht das Experiment ist. Nur Host-Executor. Kein docker.sock.
Fehlversuche bereits festgehalten: CreateArtifact ohne Job-Token (401 ist nicht der Bug); Prozess-Kill / OOM (SUCCESS ist 12 MiB -> 200, während 8 MiB summary -> 400); signedUploadUrl als RCE behandeln; Fork-PR unauth; eine Reverse Shell. Theater.
cd lab
./run.sh
Ziel nur http://127.0.0.1:18135. In-Compose ROOT_URL ist http://gitea:3000/, damit sich der Runner registrieren kann.
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
parseProtobufBody in io.LimitReader mit einer proto-großen Obergrenze einwickeln, dasselbe Muster wie MaxJobSummarySize. Ein 12 MiB großes CreateArtifact muss auf dieselbe Art fehlschlagen, wie es ein 8 MiB job-summary bereits tut.
GNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.