
Gitea Actions artifact v4 DoS에 대한 Proof-of-concept 및 실습 환경: parseProtobufBody의 무제한 io.ReadAll로 인해 job token이 과대한 CreateArtifact 본문을 POST할 수 있습니다.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-artifact-v4-readall
Gitea 1.27.3 - Gitea
job_summary.go는 이미 io.LimitReader를 사용해 MaxJobSummarySize(1 MiB)로 제한하고 있습니다. artifact v4의 parseProtobufBody는 제한 없이 io.ReadAll(ctx.Req.Body)를 수행합니다. CreateArtifact, Finalize, List, Get, Delete 모두 이 경로를 거칩니다. 임의의 사용자 PAT만으로는 충분하지 않습니다. 실행 중인 Actions job token이 필요합니다. Fork-PR job은 1.27.3에서 여전히 NeedApproval 상태입니다.
Job-summary는 LimitReader를 배웠습니다. Artifact v4는 그것을 복사하지 않았습니다. 실행 중인 job이 12 MiB CreateArtifact를 POST하면 Gitea가 전부 버퍼링합니다.
| ID | 아직 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 |
| 제품 | Gitea |
| 영향받는 버전 | Actions가 활성화된 v1.27.3(146cc3e)까지 |
| 인증 | 실행 중인 Actions job token |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1 전용 |
실행 중인 Actions job(자신의 저장소 또는 이미 NeedApproval을 통과한 fork-PR)에서 임의로 큰 v4 CreateArtifact 본문을 POST하여 Gitea가 전부 버퍼링하도록 강제할 수 있습니다. 같은 인스턴스의 job-summary는 이미 1 MiB를 초과하는 모든 것을 거부합니다.
이는 Gitea 프로세스에 대한 메모리 압박입니다. 랩에서는 누락된 제한(12 MiB -> 200)을 입증했습니다. 프로세스를 죽이지는 못했습니다. RCE가 아닙니다. 인증되지 않은 공격도 아닙니다. 실행 중인 job 없이 탈취한 사용자 PAT만으로는 이 경로에 도달하지 못합니다.
GHSA 물결 이후 남은 맵에는 Actions 본문 처리도 포함되어 있었습니다. Fork-PR job은 1.27.3에서 여전히 NeedApproval 상태이므로, 이는 인증 없는 fork-PR 우회가 아닙니다. 저는 Actions API가 요청 본문을 읽는 방식을 살펴보았습니다. Job-summary에는 제한이 있습니다. Artifact v4에는 없습니다.
gitea/act_runner:0.2.13을 띄우고, job token이 존재하도록 hold 워크플로를 심은 뒤, 12 MiB CreateArtifact를 POST했습니다. HTTP 200 ok와 signedUploadUrl이 반환되었습니다. 그다음 8 MiB job-summary: 400 invalid summary. GET /api/v1/version은 여전히 200. Gitea는 계속 살아 있었습니다. 랩의 mem_limit은 1g입니다. 본문은 12 MiB로 제한했으므로 호스트가 실험이 되지 않도록 했습니다. 호스트 executor 전용. docker.sock 없음.
이미 기록된 잘못된 방향들: job token 없는 CreateArtifact(401은 버그가 아님); 프로세스 킬 / OOM(SUCCESS는 12 MiB -> 200인 반면 8 MiB summary -> 400); signedUploadUrl을 RCE로 취급; fork-PR 인증 없음; 리버스 셸. 전부 쇼일 뿐입니다.
cd lab
./run.sh
오직 http://127.0.0.1:18135만 대상으로 합니다. compose 내부의 ROOT_URL은 http://gitea:3000/이므로 runner가 등록할 수 있습니다.
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를 proto 크기 제한으로 io.LimitReader로 감싸면 됩니다. MaxJobSummarySize와 같은 패턴입니다. 12 MiB CreateArtifact는 8 MiB job-summary가 이미 실패하는 것과 같은 종류의 방식으로 실패해야 합니다.
GNU Affero GPL v3.0. LICENSE를 참조하세요. 루프백 랩 전용. 보증 없음.