
재현 가능한 CVE-2026-10053 (GitLab npm 패키지 레지스트리 경로 탐색 -> git 권한으로 임의 파일 쓰기) 실습 환경. 취약한 19.2.1 vs 패치된 19.2.2, 결정론적 오라클.
자체 포함된 실습 환경으로, 결정적 온디스크 오라클(HTTP 상태가 아님)을 사용하여 GitLab npm 패키지 레지스트리 경로 탐색 (CVE-2026-10053)을 입증하고 수정되었음을 보여줍니다.
무엇이 입증되고 입증되지 않는가. 이 실습은
gitOS 사용자로 인증된 임의 파일 쓰기를 취약한 GitLab에서 입증하며, 패치된 릴리스가 이를 차단함을 보여줍니다. 이것은 독립형 원격 코드 실행 PoC가 아닙니다 — 범위 참조. RCE로 인용하지 마십시오.
| 취약점 | npm 패키지 레지스트리의 CWE-22 경로 탐색 (TOCTOU: 검사가 before :cache에서는 실행되었으나 before :store에서는 실행되지 않음) |
| CVSS | 8.5 높음 — AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| 영향받는 버전 | GitLab CE/EE 18.8 → <19.0.6, 19.1 → <19.1.4, 19.2 → <19.2.2 |
| 수정 버전 | 19.0.6 / 19.1.4 / 19.2.2 — 커밋 435cf863 "저장 전 업로드 경로 탐색 재검증" |
python3, git, curl 필요../run.sh # up + provision + exploit + verify BOTH; exits 0 iff vuln writes AND patched blocks
예상 결과:
================ CVE-2026-10053 verification ================
INSTANCE EXPECT RESULT STATUS
gitlab-vuln (19.2.1) written written PASS
gitlab-patched (19.2.2) blocked blocked PASS
------------------------------------------------------------
PROVEN: authenticated arbitrary file write as git on 19.2.1.
NOT a standalone RCE — see README.md 'Scope' and ./run.sh rce-gate.
============================================================
기타 하위 명령:
./run.sh up # just start + wait until healthy
./run.sh rce-gate # honest exec-bit-gate demo (below)
./run.sh down # docker compose down -v
취약 서버와 패치된 서버 모두 악의적인 게시에 대해 다음으로 응답합니다.
HTTP 200 {"status":"processing"} — 파일은 나중에 finalize 작업자에서 기록됩니다. 따라서 HTTP
상태는 오라클이 아닙니다. exploit/poc.py --verify-docker <container>는 대신 컨테이너
내부에서 탐색된 경로의 제어된 파일을 폴링하고 SHA-1을 비교합니다:
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz 파일이 나타남 (소유자 git:git, 우리가 보낸 바이트) → 종료 코드 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path를 기록 → 종료 코드 2.익스플로잇 자체는 인증된 PUT …/packages/npm/:pkg 요청에 불과합니다; 경로 탐색은
전적으로 JSON 본문의 name에 있습니다 (file_name = "#{name}-#{version}.tgz", 공백만 검사됨).
docker exec 호출은 검증 및 설정일 뿐이며, 공격자의 능력의 일부가 아닙니다.
git이 쓸 수 있는 경로에 기록합니다. 파일은 0644로 저장됩니다.-<semver>.tgz로 끝나야 합니다 (NUL은 커널이 거부하고,
개행 문자는 잘리지 않습니다). 따라서 정확한 이름의 대상(secrets.yml, authorized_keys,
.rb 파일, gitaly 바이너리)을 덮어쓸 수 없습니다. 유일한 파일 이름 무관 실행기인
custom_hooks/<hook>.d/는 모든 파일 이름을 실행하지만 실행 가능한 경우에만 실행됩니다 — 그리고 이 쓰기는 0644입니다.
기존 0755 파일을 덮어써도 도움이 되지 않습니다: 저장소가 inode를 교체하여
0644로 재설정합니다. 따라서 RCE에는 이 PoC가 제공하지 않는 별도의 실행 비트 / 실행 프리미티브가 필요합니다../run.sh rce-gate는 바로 이것을 정직하게 보여줍니다: 경로 탐색을 통해 pre-receive.d 훅을 설치하고,
푸시한 다음 0644 훅이 실행되지 않음을 보여줍니다. ./rce_gate_demo.sh --illustrate-gate는
추가적으로 docker exec chmod를 통해 +x를 설정합니다 (대역 외 docker-root 작업이며, 공격자
능력이 아님) 순전히 gitaly가 실행했을 것임을 보여주기 위한 것입니다 — 누락된 지렛대가 실행 비트임을 강조합니다.
전체 분석은 ANALYSIS.md를 참조하십시오. 요약하면: app/uploaders/gitlab_uploader.rb는
저장 경로를 before :cache에서만 검증했습니다. before :store에서는 모델에서 파생된 file_name이
(공격자가 제어하며 검증되지 않음) 그대로 기록되었습니다. 수정은 before :store, :protect_from_path_traversal!를 추가합니다.
docker-compose.yml vuln (19.2.1) + patched (19.2.2) GitLab CE
run.sh one-command harness with deterministic file oracle + PASS/FAIL
provision.rb lab setup: mint root PAT + create project (NOT part of the exploit)
exploit/poc.py the PoC sender + --verify-docker oracle
rce_gate_demo.sh honest exec-bit-gate demonstration